nhance_partner/lib/presentation/screens/staff/policy/policyPdf.dart

338 lines
9.9 KiB
Dart

import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'package:pdf_render/pdf_render_widgets.dart';
import 'dart:typed_data';
import 'package:flutter/services.dart';
class PolicyPdfViewer extends StatefulWidget {
final String? pdfUrl;
const PolicyPdfViewer({Key? key, this.pdfUrl}) : super(key: key);
@override
State<PolicyPdfViewer> createState() => _PolicyPdfViewerState();
}
class _PolicyPdfViewerState extends State<PolicyPdfViewer> {
Uint8List? _pdfBytes;
final TransformationController _transformController =
TransformationController();
double _scale = 1.0;
final double _minScale = 0.7;
final double _maxScale = 3.0;
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
_loadPdf();
}
Future<void> _loadPdf() async {
if (widget.pdfUrl == null) return;
try {
final response = await http.get(Uri.parse(widget.pdfUrl!));
if (!mounted) return;
if (response.statusCode == 200) {
setState(() {
_pdfBytes = response.bodyBytes; // ✅ MUST use setState
});
}
} catch (e) {
debugPrint('PDF load error: $e');
}
}
void _zoomIn() {
setState(() {
_scale = (_scale + 0.2).clamp(_minScale, _maxScale);
_transformController.value = Matrix4.identity()..scale(_scale);
});
}
void _zoomOut() {
setState(() {
_scale = (_scale - 0.2).clamp(_minScale, _maxScale);
_transformController.value = Matrix4.identity()..scale(_scale);
});
}
void _resetZoom() {
setState(() {
_scale = 1.0;
_transformController.value = Matrix4.identity();
});
}
@override
Widget build(BuildContext context) {
if (_pdfBytes == null) {
return const Center(child: CircularProgressIndicator());
}
final bool isZoomed = _scale > 1.0;
return Stack(
children: [
InteractiveViewer(
transformationController: _transformController,
panEnabled: isZoomed,
scaleEnabled: false,
boundaryMargin: const EdgeInsets.all(200),
minScale: _minScale,
maxScale: _maxScale,
child: PdfDocumentLoader.openData(
_pdfBytes!,
documentBuilder: (context, pdfDocument, pageCount) {
return ListView.builder(
controller: _scrollController,
physics: isZoomed
? const NeverScrollableScrollPhysics()
: const BouncingScrollPhysics(),
itemCount: pageCount,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: PdfPageView(
pdfDocument: pdfDocument,
pageNumber: index + 1,
// renderQuality: PdfPageRenderQuality.medium, // ⚡ performance
),
);
},
);
},
),
),
// ───────── Zoom Controls ─────────
Positioned(
right: 16,
top: 16,
child: Column(
children: [
_zoomButton(Icons.add, _zoomIn),
const SizedBox(height: 8),
_zoomButton(Icons.remove, _zoomOut),
const SizedBox(height: 8),
_zoomButton(Icons.refresh, _resetZoom),
],
),
),
],
);
}
Widget _zoomButton(IconData icon, VoidCallback onTap) {
return Material(
elevation: 4,
shape: const CircleBorder(),
color: Colors.white,
child: InkWell(
onTap: onTap,
customBorder: const CircleBorder(),
child: Padding(
padding: const EdgeInsets.all(10),
child: Icon(icon, size: 22),
),
),
);
}
}
//scroll zoom code
// import 'package:dropdown_search/dropdown_search.dart';
// import 'package:flutter/gestures.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter_riverpod/flutter_riverpod.dart';
// import 'package:go_router/go_router.dart';
// import 'package:google_fonts/google_fonts.dart';
// import 'package:http/http.dart' as http;
// import 'package:intl/intl.dart';
// import 'package:pdf_render/pdf_render_widgets.dart';
// import 'dart:typed_data';
// import 'package:flutter/services.dart';
//
// // ============= KEY FIX: Separate PDF Viewer Widget =============
// class PolicyPdfViewer extends StatefulWidget {
// final String? pdfUrl;
//
// const PolicyPdfViewer({Key? key, this.pdfUrl}) : super(key: key);
//
// @override
// State<PolicyPdfViewer> createState() => _PolicyPdfViewerState();
// }
//
// class _PolicyPdfViewerState extends State<PolicyPdfViewer>
// with AutomaticKeepAliveClientMixin {
// @override
// bool get wantKeepAlive => true;
//
// Uint8List? _cachedPdfBytes;
// String? _lastLoadedUrl;
//
// double _scale = 1.0;
// final double _minScale = 0.5;
// final double _maxScale = 3.0;
//
// Future<Uint8List> _loadPdf() async {
// if (_cachedPdfBytes != null && _lastLoadedUrl == widget.pdfUrl) {
// return _cachedPdfBytes!;
// }
//
// final response = await http.get(Uri.parse(widget.pdfUrl!));
// if (response.statusCode == 200) {
// _cachedPdfBytes = response.bodyBytes;
// _lastLoadedUrl = widget.pdfUrl;
// return _cachedPdfBytes!;
// } else {
// throw Exception('Failed to load PDF');
// }
// }
//
// void _onPointerSignal(PointerSignalEvent event) {
// if (event is PointerScrollEvent) {
// if (HardwareKeyboard.instance.isControlPressed) {
// setState(() {
// _scale += event.scrollDelta.dy > 0 ? -0.1 : 0.1;
// _scale = _scale.clamp(_minScale, _maxScale);
// });
// }
// }
// }
//
// @override
// Widget build(BuildContext context) {
// super.build(context);
//
// if (widget.pdfUrl == null) {
// return const Center(child: CircularProgressIndicator());
// }
//
// return Listener(
// onPointerSignal: _onPointerSignal,
// child: FutureBuilder<Uint8List>(
// key: ValueKey(widget.pdfUrl),
// future: _loadPdf(),
// builder: (context, snapshot) {
// if (!snapshot.hasData) {
// return const Center(child: CircularProgressIndicator());
// }
//
// return InteractiveViewer(
// minScale: _minScale,
// maxScale: _maxScale,
// scaleEnabled: true,
// panEnabled: true,
// child: Transform.scale(
// scale: _scale,
// alignment: Alignment.topCenter,
// child: PdfDocumentLoader.openData(
// snapshot.data!,
// documentBuilder: (context, pdfDocument, pageCount) {
// return ListView.builder(
// itemCount: pageCount,
// itemBuilder: (context, index) {
// return PdfPageView(
// pdfDocument: pdfDocument,
// pageNumber: index + 1,
// );
// },
// );
// },
// ),
// ),
// );
// },
// ),
// );
// }
// }
//Old Code for PDF Viewer
// class _PolicyPdfViewerState extends State<PolicyPdfViewer>
// with AutomaticKeepAliveClientMixin {
// @override
// bool get wantKeepAlive => true; // Prevents rebuilding
//
// // Cache the loaded PDF bytes
// Uint8List? _cachedPdfBytes;
// String? _lastLoadedUrl;
//
// Future<Uint8List> _loadPdf() async {
// // Return cached bytes if URL hasn't changed
// if (_cachedPdfBytes != null && _lastLoadedUrl == widget.pdfUrl) {
// return _cachedPdfBytes!;
// }
//
// // Load new PDF
// final response = await http.get(Uri.parse(widget.pdfUrl!));
// if (response.statusCode == 200) {
// _cachedPdfBytes = response.bodyBytes;
// _lastLoadedUrl = widget.pdfUrl;
// return _cachedPdfBytes!;
// } else {
// throw Exception('Failed to load PDF: ${response.statusCode}');
// }
// }
//
// @override
// Widget build(BuildContext context) {
// super.build(context); // Required for AutomaticKeepAliveClientMixin
//
// if (widget.pdfUrl == null) {
// return const Center(child: CircularProgressIndicator());
// }
//
// return FutureBuilder<Uint8List>(
// key: ValueKey(widget.pdfUrl), // Only rebuild if URL changes
// future: _loadPdf(),
// builder: (context, snapshot) {
// if (snapshot.connectionState == ConnectionState.waiting) {
// return const Center(child: CircularProgressIndicator());
// }
//
// if (snapshot.hasError) {
// return Center(child: Text('Error: ${snapshot.error}'));
// }
//
// if (!snapshot.hasData) {
// return const Center(child: CircularProgressIndicator());
// }
//
// return PdfDocumentLoader.openData(
// snapshot.data!,
// documentBuilder: (context, pdfDocument, pageCount) {
// return ListView.builder(
// itemCount: pageCount,
// itemBuilder: (context, index) {
// return Container(
// color: Colors.black12,
// child: PdfPageView(
// pdfDocument: pdfDocument,
// pageNumber: index + 1,
// ),
// );
// },
// );
// },
// );
// },
// );
// }
// }