1
1
library photo_view;
2
2
3
- import 'dart:async' ;
4
-
5
3
import 'package:flutter/material.dart' ;
6
4
7
5
import 'package:photo_view/src/controller/photo_view_controller.dart' ;
8
6
import 'package:photo_view/src/controller/photo_view_scalestate_controller.dart' ;
9
7
import 'package:photo_view/src/core/photo_view_core.dart' ;
10
8
import 'package:photo_view/src/photo_view_computed_scale.dart' ;
11
- import 'package:photo_view/src/photo_view_default_widgets.dart' ;
12
9
import 'package:photo_view/src/photo_view_scale_state.dart' ;
10
+ import 'package:photo_view/src/photo_view_wrappers.dart' ;
13
11
import 'package:photo_view/src/utils/photo_view_hero_attributes.dart' ;
14
- import 'package:photo_view/src/utils/photo_view_utils.dart' ;
15
12
16
13
export 'src/controller/photo_view_controller.dart' ;
17
14
export 'src/controller/photo_view_scalestate_controller.dart' ;
@@ -259,6 +256,7 @@ class PhotoView extends StatefulWidget {
259
256
this .tightMode,
260
257
this .filterQuality,
261
258
this .disableGestures,
259
+ this .errorBuilder,
262
260
}) : child = null ,
263
261
childSize = null ,
264
262
super (key: key);
@@ -292,6 +290,7 @@ class PhotoView extends StatefulWidget {
292
290
this .filterQuality,
293
291
this .disableGestures,
294
292
}) : loadFailedChild = null ,
293
+ errorBuilder = null ,
295
294
imageProvider = null ,
296
295
gaplessPlayback = false ,
297
296
loadingBuilder = null ,
@@ -306,6 +305,10 @@ class PhotoView extends StatefulWidget {
306
305
final LoadingBuilder loadingBuilder;
307
306
308
307
/// Show loadFailedChild when the image failed to load
308
+ final ImageErrorWidgetBuilder errorBuilder;
309
+
310
+ /// Show loadFailedChild when the image failed to load
311
+ @Deprecated ("Use errorBuilder instead" )
309
312
final Widget loadFailedChild;
310
313
311
314
/// Changes the background behind image, defaults to `Colors.black` .
@@ -385,74 +388,29 @@ class PhotoView extends StatefulWidget {
385
388
// Useful when custom gesture detector is used in child widget.
386
389
final bool disableGestures;
387
390
391
+ bool get _isCustomChild {
392
+ return child != null ;
393
+ }
394
+
388
395
@override
389
396
State <StatefulWidget > createState () {
390
397
return _PhotoViewState ();
391
398
}
392
399
}
393
400
394
401
class _PhotoViewState extends State <PhotoView > {
395
- Size _childSize;
396
- bool _loading;
397
- ImageChunkEvent _imageChunkEvent;
402
+ // image retrieval
398
403
404
+ // controller
399
405
bool _controlledController;
400
406
PhotoViewControllerBase _controller;
401
-
402
407
bool _controlledScaleStateController;
403
408
PhotoViewScaleStateController _scaleStateController;
404
409
405
- Future <ImageInfo > _getImage () {
406
- final Completer completer = Completer <ImageInfo >();
407
- final ImageStream stream = widget.imageProvider.resolve (
408
- const ImageConfiguration (),
409
- );
410
- final listener = ImageStreamListener ((
411
- ImageInfo info,
412
- bool synchronousCall,
413
- ) {
414
- if (completer.isCompleted) {
415
- return ;
416
- }
417
- completer.complete (info);
418
- if (mounted) {
419
- final setupCallback = () {
420
- _childSize = Size (
421
- info.image.width.toDouble (),
422
- info.image.height.toDouble (),
423
- );
424
- _loading = false ;
425
- _imageChunkEvent = null ;
426
- };
427
- synchronousCall ? setupCallback () : setState (setupCallback);
428
- }
429
- }, onChunk: (event) {
430
- if (mounted) {
431
- setState (() => _imageChunkEvent = event);
432
- }
433
- }, onError: (exception, stackTrace) {
434
- if (completer.isCompleted) {
435
- return ;
436
- }
437
- completer.completeError (exception, stackTrace);
438
- });
439
- stream.addListener (listener);
440
- completer.future.then ((_) {
441
- stream.removeListener (listener);
442
- });
443
- return completer.future;
444
- }
445
-
446
410
@override
447
411
void initState () {
448
412
super .initState ();
449
- if (widget.child == null ) {
450
- _getImage ();
451
- } else {
452
- _childSize = widget.childSize;
453
- _loading = false ;
454
- _imageChunkEvent = null ;
455
- }
413
+
456
414
if (widget.controller == null ) {
457
415
_controlledController = true ;
458
416
_controller = PhotoViewController ();
@@ -474,11 +432,6 @@ class _PhotoViewState extends State<PhotoView> {
474
432
475
433
@override
476
434
void didUpdateWidget (PhotoView oldWidget) {
477
- if (oldWidget.childSize != widget.childSize && widget.childSize != null ) {
478
- setState (() {
479
- _childSize = widget.childSize;
480
- });
481
- }
482
435
if (widget.controller == null ) {
483
436
if (! _controlledController) {
484
437
_controlledController = true ;
@@ -525,114 +478,58 @@ class _PhotoViewState extends State<PhotoView> {
525
478
BuildContext context,
526
479
BoxConstraints constraints,
527
480
) {
528
- return widget.child == null
529
- ? _buildImage (context, constraints)
530
- : _buildCustomChild (context, constraints);
481
+ final computedOuterSize = widget.customSize ?? constraints.biggest;
482
+
483
+ return widget._isCustomChild
484
+ ? CustomChildWrapper (
485
+ child: widget.child,
486
+ childSize: widget.childSize,
487
+ backgroundDecoration: widget.backgroundDecoration,
488
+ heroAttributes: widget.heroAttributes,
489
+ scaleStateChangedCallback: widget.scaleStateChangedCallback,
490
+ enableRotation: widget.enableRotation,
491
+ controller: _controller,
492
+ scaleStateController: _scaleStateController,
493
+ maxScale: widget.maxScale,
494
+ minScale: widget.minScale,
495
+ initialScale: widget.initialScale,
496
+ basePosition: widget.basePosition,
497
+ scaleStateCycle: widget.scaleStateCycle,
498
+ onTapUp: widget.onTapUp,
499
+ onTapDown: widget.onTapDown,
500
+ outerSize: computedOuterSize,
501
+ gestureDetectorBehavior: widget.gestureDetectorBehavior,
502
+ tightMode: widget.tightMode,
503
+ filterQuality: widget.filterQuality,
504
+ disableGestures: widget.disableGestures,
505
+ )
506
+ : ImageWrapper (
507
+ imageProvider: widget.imageProvider,
508
+ loadingBuilder: widget.loadingBuilder,
509
+ loadFailedChild: widget.loadFailedChild,
510
+ backgroundDecoration: widget.backgroundDecoration,
511
+ gaplessPlayback: widget.gaplessPlayback,
512
+ heroAttributes: widget.heroAttributes,
513
+ scaleStateChangedCallback: widget.scaleStateChangedCallback,
514
+ enableRotation: widget.enableRotation,
515
+ controller: _controller,
516
+ scaleStateController: _scaleStateController,
517
+ maxScale: widget.maxScale,
518
+ minScale: widget.minScale,
519
+ initialScale: widget.initialScale,
520
+ basePosition: widget.basePosition,
521
+ scaleStateCycle: widget.scaleStateCycle,
522
+ onTapUp: widget.onTapUp,
523
+ onTapDown: widget.onTapDown,
524
+ outerSize: computedOuterSize,
525
+ gestureDetectorBehavior: widget.gestureDetectorBehavior,
526
+ tightMode: widget.tightMode,
527
+ filterQuality: widget.filterQuality,
528
+ disableGestures: widget.disableGestures,
529
+ );
531
530
},
532
531
);
533
532
}
534
-
535
- Widget _buildCustomChild (BuildContext context, BoxConstraints constraints) {
536
- final _computedOuterSize = widget.customSize ?? constraints.biggest;
537
-
538
- final scaleBoundaries = ScaleBoundaries (
539
- widget.minScale ?? 0.0 ,
540
- widget.maxScale ?? double .infinity,
541
- widget.initialScale ?? PhotoViewComputedScale .contained,
542
- _computedOuterSize,
543
- _childSize ?? constraints.biggest,
544
- );
545
-
546
- return PhotoViewCore .customChild (
547
- customChild: widget.child,
548
- backgroundDecoration: widget.backgroundDecoration,
549
- enableRotation: widget.enableRotation,
550
- heroAttributes: widget.heroAttributes,
551
- controller: _controller,
552
- scaleStateController: _scaleStateController,
553
- scaleStateCycle: widget.scaleStateCycle ?? defaultScaleStateCycle,
554
- basePosition: widget.basePosition ?? Alignment .center,
555
- scaleBoundaries: scaleBoundaries,
556
- onTapUp: widget.onTapUp,
557
- onTapDown: widget.onTapDown,
558
- gestureDetectorBehavior: widget.gestureDetectorBehavior,
559
- tightMode: widget.tightMode ?? false ,
560
- filterQuality: widget.filterQuality ?? FilterQuality .none,
561
- disableGestures: widget.disableGestures ?? false ,
562
- );
563
- }
564
-
565
- Widget _buildImage (BuildContext context, BoxConstraints constraints) {
566
- return widget.heroAttributes == null
567
- ? _buildAsync (context, constraints)
568
- : _buildSync (context, constraints);
569
- }
570
-
571
- Widget _buildAsync (BuildContext context, BoxConstraints constraints) {
572
- return FutureBuilder (
573
- future: _getImage (),
574
- builder: (BuildContext context, AsyncSnapshot <ImageInfo > info) {
575
- if (info.hasError) {
576
- return _buildLoadFailed ();
577
- }
578
- if (info.hasData) {
579
- return _buildWrapperImage (context, constraints);
580
- }
581
- return _buildLoading ();
582
- });
583
- }
584
-
585
- Widget _buildSync (BuildContext context, BoxConstraints constraints) {
586
- if (_loading == null ) {
587
- return _buildLoading ();
588
- }
589
- return _buildWrapperImage (context, constraints);
590
- }
591
-
592
- Widget _buildWrapperImage (BuildContext context, BoxConstraints constraints) {
593
- final _computedOuterSize = widget.customSize ?? constraints.biggest;
594
-
595
- final scaleBoundaries = ScaleBoundaries (
596
- widget.minScale ?? 0.0 ,
597
- widget.maxScale ?? double .infinity,
598
- widget.initialScale ?? PhotoViewComputedScale .contained,
599
- _computedOuterSize,
600
- _childSize,
601
- );
602
-
603
- return PhotoViewCore (
604
- imageProvider: widget.imageProvider,
605
- backgroundDecoration: widget.backgroundDecoration,
606
- gaplessPlayback: widget.gaplessPlayback,
607
- enableRotation: widget.enableRotation,
608
- heroAttributes: widget.heroAttributes,
609
- basePosition: widget.basePosition ?? Alignment .center,
610
- controller: _controller,
611
- scaleStateController: _scaleStateController,
612
- scaleStateCycle: widget.scaleStateCycle ?? defaultScaleStateCycle,
613
- scaleBoundaries: scaleBoundaries,
614
- onTapUp: widget.onTapUp,
615
- onTapDown: widget.onTapDown,
616
- gestureDetectorBehavior: widget.gestureDetectorBehavior,
617
- tightMode: widget.tightMode ?? false ,
618
- filterQuality: widget.filterQuality ?? FilterQuality .none,
619
- disableGestures: widget.disableGestures ?? false ,
620
- );
621
- }
622
-
623
- Widget _buildLoading () {
624
- if (widget.loadingBuilder != null ) {
625
- return widget.loadingBuilder (context, _imageChunkEvent);
626
- }
627
-
628
- return PhotoViewDefaultLoading (
629
- event: _imageChunkEvent,
630
- );
631
- }
632
-
633
- Widget _buildLoadFailed () {
634
- return widget.loadFailedChild ?? PhotoViewDefaultError ();
635
- }
636
533
}
637
534
638
535
/// The default [ScaleStateCycle]
@@ -673,7 +570,7 @@ typedef PhotoViewImageTapDownCallback = Function(
673
570
PhotoViewControllerValue controllerValue,
674
571
);
675
572
676
- /// A type definition for a callback to show a widget while a image is loading, a [ImageChunkEvent] is passed to inform progress
573
+ /// A type definition for a callback to show a widget while the image is loading, a [ImageChunkEvent] is passed to inform progress
677
574
typedef LoadingBuilder = Widget Function (
678
575
BuildContext context,
679
576
ImageChunkEvent event,
0 commit comments