pdf zooming
This commit is contained in:
parent
97759378de
commit
b261c95bd1
@ -5,8 +5,8 @@ class Env {
|
||||
);
|
||||
static const String apiUrl = String.fromEnvironment(
|
||||
'API_URL',
|
||||
defaultValue: 'https://partner.nhanceindia.in/partner_api/api/', /* Live build (enable index.html line 18) */
|
||||
// defaultValue: 'https://venbait.in/nhance/partner/dev/api/', /* Test build (enable index.html line 19) */
|
||||
// defaultValue: 'https://partner.nhanceindia.in/partner_api/api/', /* Live build (enable index.html line 18) */
|
||||
defaultValue: 'https://venbait.in/nhance/partner/dev/api/', /* Test build (enable index.html line 19) */
|
||||
// defaultValue: 'http://localhost/nhance_partner_be/', /* localhost build (enable index.html line 19) */
|
||||
);
|
||||
// static const String baseUrl = String.fromEnvironment(
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
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';
|
||||
@ -9,7 +10,6 @@ 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;
|
||||
|
||||
@ -19,74 +19,321 @@ class PolicyPdfViewer extends StatefulWidget {
|
||||
State<PolicyPdfViewer> createState() => _PolicyPdfViewerState();
|
||||
}
|
||||
|
||||
class _PolicyPdfViewerState extends State<PolicyPdfViewer>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
bool get wantKeepAlive => true; // Prevents rebuilding
|
||||
class _PolicyPdfViewerState extends State<PolicyPdfViewer> {
|
||||
Uint8List? _pdfBytes;
|
||||
String? _loadedUrl;
|
||||
|
||||
// Cache the loaded PDF bytes
|
||||
Uint8List? _cachedPdfBytes;
|
||||
String? _lastLoadedUrl;
|
||||
final TransformationController _transformController =
|
||||
TransformationController();
|
||||
|
||||
Future<Uint8List> _loadPdf() async {
|
||||
// Return cached bytes if URL hasn't changed
|
||||
if (_cachedPdfBytes != null && _lastLoadedUrl == widget.pdfUrl) {
|
||||
return _cachedPdfBytes!;
|
||||
}
|
||||
double _scale = 1.0;
|
||||
final double _minScale = 0.7;
|
||||
final double _maxScale = 3.0;
|
||||
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
Future<void> _loadPdf() async {
|
||||
if (_pdfBytes != null && _loadedUrl == widget.pdfUrl) return;
|
||||
|
||||
// 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}');
|
||||
// setState(() {
|
||||
_pdfBytes = response.bodyBytes;
|
||||
_loadedUrl = widget.pdfUrl;
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
void _applyTransform() {
|
||||
_transformController.value = Matrix4.identity()..scale(_scale);
|
||||
}
|
||||
|
||||
void _zoomIn() {
|
||||
setState(() {
|
||||
_scale = (_scale + 0.2).clamp(_minScale, _maxScale);
|
||||
_applyTransform();
|
||||
});
|
||||
}
|
||||
|
||||
void _zoomOut() {
|
||||
setState(() {
|
||||
_scale = (_scale - 0.2).clamp(_minScale, _maxScale);
|
||||
_applyTransform();
|
||||
});
|
||||
}
|
||||
|
||||
void _resetZoom() {
|
||||
setState(() {
|
||||
_scale = 1.0;
|
||||
_transformController.value = Matrix4.identity();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.pdfUrl != null) {
|
||||
_loadPdf();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context); // Required for AutomaticKeepAliveClientMixin
|
||||
|
||||
if (widget.pdfUrl == null) {
|
||||
if (_pdfBytes == 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());
|
||||
}
|
||||
final bool isZoomed = _scale > 1.0;
|
||||
|
||||
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,
|
||||
return Stack(
|
||||
children: [
|
||||
InteractiveViewer(
|
||||
transformationController: _transformController,
|
||||
panEnabled: isZoomed, // 👈 ONLY pan when zoomed
|
||||
scaleEnabled: false, // zoom via buttons only
|
||||
boundaryMargin: const EdgeInsets.all(200),
|
||||
minScale: _minScale,
|
||||
maxScale: _maxScale,
|
||||
child: SingleChildScrollView(
|
||||
controller: _scrollController,
|
||||
physics: isZoomed
|
||||
? const NeverScrollableScrollPhysics() // ❌ no scroll when zoomed
|
||||
: const BouncingScrollPhysics(), // ✅ normal scroll
|
||||
child: PdfDocumentLoader.openData(
|
||||
_pdfBytes!,
|
||||
documentBuilder: (context, pdfDocument, pageCount) {
|
||||
return Column(
|
||||
children: List.generate(
|
||||
pageCount,
|
||||
(index) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: PdfPageView(
|
||||
pdfDocument: pdfDocument,
|
||||
pageNumber: index + 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// ─────────── Zoom buttons ───────────
|
||||
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,
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
@ -15,8 +15,8 @@
|
||||
the `--base-href` argument provided to `flutter build`.
|
||||
-->
|
||||
<!-- <base href="$FLUTTER_BASE_HREF">-->
|
||||
<base href="/partner/"> <!-- Live build: also check env.dart (line 8) -->
|
||||
<!--<base href="/nhance/partner/app/"> Testing build: also check env.dart (line 9) -->
|
||||
<!-- <base href="/partner/"> <!– Live build: also check env.dart (line 8) –>-->
|
||||
<base href="/nhance/partner/app/"> Testing build: also check env.dart (line 9)
|
||||
|
||||
|
||||
<meta charset="UTF-8">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user