pdf zooming
This commit is contained in:
parent
97759378de
commit
b261c95bd1
@ -5,8 +5,8 @@ class Env {
|
|||||||
);
|
);
|
||||||
static const String apiUrl = String.fromEnvironment(
|
static const String apiUrl = String.fromEnvironment(
|
||||||
'API_URL',
|
'API_URL',
|
||||||
defaultValue: 'https://partner.nhanceindia.in/partner_api/api/', /* Live build (enable index.html line 18) */
|
// 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://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) */
|
// defaultValue: 'http://localhost/nhance_partner_be/', /* localhost build (enable index.html line 19) */
|
||||||
);
|
);
|
||||||
// static const String baseUrl = String.fromEnvironment(
|
// static const String baseUrl = String.fromEnvironment(
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:dropdown_search/dropdown_search.dart';
|
import 'package:dropdown_search/dropdown_search.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.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 'dart:typed_data';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
// ============= KEY FIX: Separate PDF Viewer Widget =============
|
|
||||||
class PolicyPdfViewer extends StatefulWidget {
|
class PolicyPdfViewer extends StatefulWidget {
|
||||||
final String? pdfUrl;
|
final String? pdfUrl;
|
||||||
|
|
||||||
@ -19,74 +19,321 @@ class PolicyPdfViewer extends StatefulWidget {
|
|||||||
State<PolicyPdfViewer> createState() => _PolicyPdfViewerState();
|
State<PolicyPdfViewer> createState() => _PolicyPdfViewerState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PolicyPdfViewerState extends State<PolicyPdfViewer>
|
class _PolicyPdfViewerState extends State<PolicyPdfViewer> {
|
||||||
with AutomaticKeepAliveClientMixin {
|
Uint8List? _pdfBytes;
|
||||||
@override
|
String? _loadedUrl;
|
||||||
bool get wantKeepAlive => true; // Prevents rebuilding
|
|
||||||
|
|
||||||
// Cache the loaded PDF bytes
|
final TransformationController _transformController =
|
||||||
Uint8List? _cachedPdfBytes;
|
TransformationController();
|
||||||
String? _lastLoadedUrl;
|
|
||||||
|
|
||||||
Future<Uint8List> _loadPdf() async {
|
double _scale = 1.0;
|
||||||
// Return cached bytes if URL hasn't changed
|
final double _minScale = 0.7;
|
||||||
if (_cachedPdfBytes != null && _lastLoadedUrl == widget.pdfUrl) {
|
final double _maxScale = 3.0;
|
||||||
return _cachedPdfBytes!;
|
|
||||||
}
|
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!));
|
final response = await http.get(Uri.parse(widget.pdfUrl!));
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
_cachedPdfBytes = response.bodyBytes;
|
// setState(() {
|
||||||
_lastLoadedUrl = widget.pdfUrl;
|
_pdfBytes = response.bodyBytes;
|
||||||
return _cachedPdfBytes!;
|
_loadedUrl = widget.pdfUrl;
|
||||||
} else {
|
// });
|
||||||
throw Exception('Failed to load PDF: ${response.statusCode}');
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context); // Required for AutomaticKeepAliveClientMixin
|
if (_pdfBytes == null) {
|
||||||
|
|
||||||
if (widget.pdfUrl == null) {
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
|
||||||
return FutureBuilder<Uint8List>(
|
final bool isZoomed = _scale > 1.0;
|
||||||
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 Stack(
|
||||||
return Center(child: Text('Error: ${snapshot.error}'));
|
children: [
|
||||||
}
|
InteractiveViewer(
|
||||||
|
transformationController: _transformController,
|
||||||
if (!snapshot.hasData) {
|
panEnabled: isZoomed, // 👈 ONLY pan when zoomed
|
||||||
return const Center(child: CircularProgressIndicator());
|
scaleEnabled: false, // zoom via buttons only
|
||||||
}
|
boundaryMargin: const EdgeInsets.all(200),
|
||||||
|
minScale: _minScale,
|
||||||
return PdfDocumentLoader.openData(
|
maxScale: _maxScale,
|
||||||
snapshot.data!,
|
child: SingleChildScrollView(
|
||||||
documentBuilder: (context, pdfDocument, pageCount) {
|
controller: _scrollController,
|
||||||
return ListView.builder(
|
physics: isZoomed
|
||||||
itemCount: pageCount,
|
? const NeverScrollableScrollPhysics() // ❌ no scroll when zoomed
|
||||||
itemBuilder: (context, index) {
|
: const BouncingScrollPhysics(), // ✅ normal scroll
|
||||||
return Container(
|
child: PdfDocumentLoader.openData(
|
||||||
color: Colors.black12,
|
_pdfBytes!,
|
||||||
child: PdfPageView(
|
documentBuilder: (context, pdfDocument, pageCount) {
|
||||||
pdfDocument: pdfDocument,
|
return Column(
|
||||||
pageNumber: index + 1,
|
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`.
|
the `--base-href` argument provided to `flutter build`.
|
||||||
-->
|
-->
|
||||||
<!-- <base href="$FLUTTER_BASE_HREF">-->
|
<!-- <base href="$FLUTTER_BASE_HREF">-->
|
||||||
<base href="/partner/"> <!-- Live build: also check env.dart (line 8) -->
|
<!-- <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="/nhance/partner/app/"> Testing build: also check env.dart (line 9)
|
||||||
|
|
||||||
|
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user