From e10b3dfe79896a2718a9fba321b781bacea62a85 Mon Sep 17 00:00:00 2001 From: sant009 <530705296@qq.com> Date: Tue, 14 Aug 2018 20:52:48 +0800 Subject: [PATCH] Added HeroPhotoView class which is more suitable for using with Hero widget. --- API.md | 37 +++++++++++++++ lib/hero_photo_view.dart | 97 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 lib/hero_photo_view.dart diff --git a/API.md b/API.md index a645a614..d97197e2 100644 --- a/API.md +++ b/API.md @@ -73,4 +73,41 @@ dynamic ([double](https://docs.flutter.io/flutter/dart-core/double-class.html) o bool gaplessPlayback }) +## HeroPhotoView class +A specific [PhotoView](/lib/photo_view.dart) used in playing [Hero](https://docs.flutter.io/flutter/widgets/Hero-class.html) animation. + +#### Sample code + +```dart +new HeroPhotoView( + imageProvider: imageProvider, + backgroundColor: Colors.white, + minScale: PhotoViewScaleBoundary.contained, + maxScale: 2.0, + gaplessPlayback: false, + size: MediaQuery.of(context).size +); +``` + +### Constructor + +[HeroPhotoView](/lib/hero_photo_view.dart)({ +@required [ImageProvider](https://docs.flutter.io/flutter/painting/ImageProvider-class.html) imageProvider, +[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) backgroundColor, +dynamic ([double](https://docs.flutter.io/flutter/dart-core/double-class.html) or [PhotoViewScaleBoundary](/lib/photo_view_scale_boundary.dart)) minScale, +dynamic ([double](https://docs.flutter.io/flutter/dart-core/double-class.html) or [PhotoViewScaleBoundary](/lib/photo_view_scale_boundary.dart)) maxScale, +bool gaplessPlayback, +[Size](https://docs.flutter.io/flutter/dart-ui/Size-class.html) size}) + +Instead of using [FutureBuilder](https://docs.flutter.io/flutter/widgets/FutureBuilder-class.html) in the normal [PhotoView](/lib/photo_view.dart), [HeroPhotoView](/lib/hero_photo_view.dart) uses [setState()](https://docs.flutter.io/flutter/widgets/State/setState.html) method to update UI after [ImageProvider](https://docs.flutter.io/flutter/painting/ImageProvider-class.html) being resolved. + +The zoom scale can be clamped by `minScale` and `maxScale` params. + +`backgroundColor`, changes the background behind image, defaults to `Colors.black`. + +The parameter `gaplessPlayback` is used to continue showing the old image (`true`), or briefly show nothing (`false`), when the `imageProvider` changes. By default it's set to `false`. + +`size` defines the size of the scaling base of the image inside `PhotoView`, by default it is `MediaQuery.of(context).size`. This param is used by `PhotoViewInline` class. + +**All but `imageProvider` are optional** diff --git a/lib/hero_photo_view.dart b/lib/hero_photo_view.dart new file mode 100644 index 00000000..315a68f8 --- /dev/null +++ b/lib/hero_photo_view.dart @@ -0,0 +1,97 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:photo_view/photo_view.dart'; +import 'package:photo_view/photo_view_image_wrapper.dart'; +import 'package:photo_view/photo_view_scale_boundaries.dart'; +import 'package:photo_view/photo_view_scale_state.dart'; +import 'package:photo_view/photo_view_utils.dart'; + +export 'package:photo_view/photo_view_scale_boundary.dart'; + +class HeroPhotoView extends PhotoView { + HeroPhotoView({ + Key key, + @required ImageProvider imageProvider, + Color backgroundColor = const Color.fromRGBO(0, 0, 0, 1.0), + minScale, + maxScale, + bool gaplessPlayback = false, + Size size, + }) : super( + key: key, + imageProvider: imageProvider, + backgroundColor: backgroundColor, + minScale: minScale, + maxScale: maxScale, + gaplessPlayback: gaplessPlayback, + size: size); + + @override + State createState() { + return new _HeroPhotoViewState(); + } +} + +class _HeroPhotoViewState extends State { + PhotoViewScaleState _scaleState; + GlobalKey containerKey = new GlobalKey(); + ImageInfo _imageInfo; + + Future _getImage() { + Completer completer = new Completer(); + ImageStream stream = widget.imageProvider.resolve(new ImageConfiguration()); + var listener = (ImageInfo info, bool completed) { + completer.complete(info); + setState(() { + _imageInfo = info; + }); + }; + stream.addListener(listener); + completer.future.then((_) { + stream.removeListener(listener); + }); + return completer.future; + } + + void onDoubleTap() { + setState(() { + _scaleState = nextScaleState(_scaleState); + }); + } + + void onStartPanning() { + setState(() { + _scaleState = PhotoViewScaleState.zooming; + }); + } + + @override + void initState() { + _getImage(); + super.initState(); + _scaleState = PhotoViewScaleState.contained; + } + + @override + Widget build(BuildContext context) { + if (_imageInfo == null) { + return new SizedBox(); + } + return new PhotoViewImageWrapper( + onDoubleTap: onDoubleTap, + onStartPanning: onStartPanning, + imageProvider: widget.imageProvider, + imageInfo: _imageInfo, + scaleState: _scaleState, + backgroundColor: widget.backgroundColor, + gaplessPlayback: widget.gaplessPlayback, + size: widget.size ?? MediaQuery.of(context).size, + scaleBoundaries: new ScaleBoundaries( + widget.minScale ?? 0.0, + widget.maxScale ?? 100000000000.0, + imageInfo: _imageInfo, + size: widget.size ?? MediaQuery.of(context).size, + ), + ); + } +}