David PHAM-VAN

Implement pan and zoom on PdfPreview widget

@@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
4 4
5 - Remove deprecated methods 5 - Remove deprecated methods
6 - Document.save() now returns a Future 6 - Document.save() now returns a Future
  7 +- Implement pan and zoom on PdfPreview widget
7 8
8 ## 3.7.2 9 ## 3.7.2
9 10
  1 +import 'dart:async';
1 import 'dart:math'; 2 import 'dart:math';
2 import 'dart:typed_data'; 3 import 'dart:typed_data';
3 4
@@ -100,6 +101,16 @@ class _PdfPreviewState extends State<PdfPreview> { @@ -100,6 +101,16 @@ class _PdfPreviewState extends State<PdfPreview> {
100 101
101 Object error; 102 Object error;
102 103
  104 + int preview;
  105 +
  106 + double updatePosition;
  107 +
  108 + final scrollController = ScrollController();
  109 +
  110 + final transformationController = TransformationController();
  111 +
  112 + Timer previewUpdate;
  113 +
103 static const Map<String, PdfPageFormat> defaultPageFormats = 114 static const Map<String, PdfPageFormat> defaultPageFormats =
104 <String, PdfPageFormat>{ 115 <String, PdfPageFormat>{
105 'A4': PdfPageFormat.a4, 116 'A4': PdfPageFormat.a4,
@@ -178,6 +189,8 @@ class _PdfPreviewState extends State<PdfPreview> { @@ -178,6 +189,8 @@ class _PdfPreviewState extends State<PdfPreview> {
178 @override 189 @override
179 void didUpdateWidget(covariant PdfPreview oldWidget) { 190 void didUpdateWidget(covariant PdfPreview oldWidget) {
180 if (oldWidget.build != widget.build) { 191 if (oldWidget.build != widget.build) {
  192 + preview = null;
  193 + updatePosition = null;
181 pages.clear(); 194 pages.clear();
182 _raster(); 195 _raster();
183 } 196 }
@@ -196,6 +209,8 @@ class _PdfPreviewState extends State<PdfPreview> { @@ -196,6 +209,8 @@ class _PdfPreviewState extends State<PdfPreview> {
196 }); 209 });
197 } 210 }
198 211
  212 + previewUpdate?.cancel();
  213 + previewUpdate = Timer(const Duration(seconds: 1), () {
199 final mq = MediaQuery.of(context); 214 final mq = MediaQuery.of(context);
200 dpi = (min(mq.size.width - 16, widget.maxPageWidth ?? double.infinity)) * 215 dpi = (min(mq.size.width - 16, widget.maxPageWidth ?? double.infinity)) *
201 mq.devicePixelRatio / 216 mq.devicePixelRatio /
@@ -203,6 +218,7 @@ class _PdfPreviewState extends State<PdfPreview> { @@ -203,6 +218,7 @@ class _PdfPreviewState extends State<PdfPreview> {
203 72; 218 72;
204 219
205 _raster(); 220 _raster();
  221 + });
206 super.didChangeDependencies(); 222 super.didChangeDependencies();
207 } 223 }
208 224
@@ -242,8 +258,33 @@ class _PdfPreviewState extends State<PdfPreview> { @@ -242,8 +258,33 @@ class _PdfPreviewState extends State<PdfPreview> {
242 258
243 return Scrollbar( 259 return Scrollbar(
244 child: ListView.builder( 260 child: ListView.builder(
  261 + controller: scrollController,
245 itemCount: pages.length, 262 itemCount: pages.length,
246 - itemBuilder: (BuildContext context, int index) => pages[index], 263 + itemBuilder: (BuildContext context, int index) => GestureDetector(
  264 + onDoubleTap: () {
  265 + setState(() {
  266 + updatePosition = scrollController.position.pixels;
  267 + preview = index;
  268 + transformationController.value.setIdentity();
  269 + });
  270 + },
  271 + child: pages[index],
  272 + ),
  273 + ),
  274 + );
  275 + }
  276 +
  277 + Widget _zoomPreview() {
  278 + return GestureDetector(
  279 + onDoubleTap: () {
  280 + setState(() {
  281 + preview = null;
  282 + });
  283 + },
  284 + child: InteractiveViewer(
  285 + transformationController: transformationController,
  286 + maxScale: 5,
  287 + child: Center(child: pages[preview]),
247 ), 288 ),
248 ); 289 );
249 } 290 }
@@ -252,6 +293,26 @@ class _PdfPreviewState extends State<PdfPreview> { @@ -252,6 +293,26 @@ class _PdfPreviewState extends State<PdfPreview> {
252 Widget build(BuildContext context) { 293 Widget build(BuildContext context) {
253 final theme = Theme.of(context); 294 final theme = Theme.of(context);
254 295
  296 + Widget page;
  297 +
  298 + if (preview != null) {
  299 + page = _zoomPreview();
  300 + } else {
  301 + page = Container(
  302 + constraints: widget.maxPageWidth != null
  303 + ? BoxConstraints(maxWidth: widget.maxPageWidth)
  304 + : null,
  305 + child: _createPreview(),
  306 + );
  307 +
  308 + if (updatePosition != null) {
  309 + Timer.run(() {
  310 + scrollController.jumpTo(updatePosition);
  311 + updatePosition = null;
  312 + });
  313 + }
  314 + }
  315 +
255 final Widget scrollView = Container( 316 final Widget scrollView = Container(
256 decoration: widget.scrollViewDecoration ?? 317 decoration: widget.scrollViewDecoration ??
257 BoxDecoration( 318 BoxDecoration(
@@ -263,12 +324,7 @@ class _PdfPreviewState extends State<PdfPreview> { @@ -263,12 +324,7 @@ class _PdfPreviewState extends State<PdfPreview> {
263 ), 324 ),
264 width: double.infinity, 325 width: double.infinity,
265 alignment: Alignment.center, 326 alignment: Alignment.center,
266 - child: Container(  
267 - constraints: widget.maxPageWidth != null  
268 - ? BoxConstraints(maxWidth: widget.maxPageWidth)  
269 - : null,  
270 - child: _createPreview(),  
271 - ), 327 + child: page,
272 ); 328 );
273 329
274 final actions = <Widget>[]; 330 final actions = <Widget>[];