From 65dbce7432b8745e4077c9bab22d6a0b2fea9057 Mon Sep 17 00:00:00 2001 From: Johannes Fahrenkrug Date: Mon, 16 May 2022 22:53:07 +0200 Subject: [PATCH] feat: Add option to allow swipe-down to dismiss An ticket asked for swipe-to-dismiss (https://github.com/thesmythgroup/easy_image_viewer/issues/14) It's easy to implement and now supported with the `swipeDismissable` argument. This allows the user to drag the dialog down to dismiss it. However, this is still WIP because when the user has zoomed in, you can't pan the image anymore because the Dismissable widget begins to dismiss. Will have to address this in a future commit. --- .vscode/extensions.json | 7 +++ .vscode/settings.json | 7 +++ example/lib/main.dart | 9 ++-- lib/easy_image_viewer.dart | 88 ++++++++++++++++++++------------------ pubspec.yaml | 2 +- 5 files changed, 65 insertions(+), 48 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..3c6f040 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "dart-code.dart-code", + "dart-code.flutter" + ] +} + \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..adc1784 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "[dart]": { + "editor.defaultFormatter": "Dart-Code.dart-code", + "editor.formatOnSave": false + }, + "dart.lineLength": 120 + } \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index b6523b3..32fecb5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -58,16 +58,13 @@ class _MyHomePageState extends State { ElevatedButton( child: const Text("Show Single Image"), onPressed: () { - showImageViewer( - context, - Image.network("https://picsum.photos/id/1001/5616/3744") - .image); + showImageViewer(context, Image.network("https://picsum.photos/id/1001/5616/3744").image, + swipeDismissable: true); }), ElevatedButton( child: const Text("Show Multiple Images (Simple)"), onPressed: () { - MultiImageProvider multiImageProvider = - MultiImageProvider(_imageProviders); + MultiImageProvider multiImageProvider = MultiImageProvider(_imageProviders); showImageViewerPager(context, multiImageProvider); }), ElevatedButton( diff --git a/lib/easy_image_viewer.dart b/lib/easy_image_viewer.dart index e4ccb32..3b0c743 100644 --- a/lib/easy_image_viewer.dart +++ b/lib/easy_image_viewer.dart @@ -25,23 +25,24 @@ const _defaultCloseButtonTooltip = 'Close'; /// Setting [immersive] to false will prevent the top and bottom bars from being hidden. /// The optional [onViewerDismissed] callback function is called when the dialog is closed. /// The optional [useSafeArea] boolean defaults to false and is passed to [showDialog]. +/// The optional [swipeDismissable] boolean defaults to false allows swipe-down-to-dismiss. /// The [backgroundColor] defaults to black, but can be set to any other color. /// The [closeButtonTooltip] text is displayed when the user long-presses on the /// close button and is used for accessibility. /// The [closeButtonColor] defaults to white, but can be set to any other color. -Future showImageViewer( - BuildContext context, ImageProvider imageProvider, +Future showImageViewer(BuildContext context, ImageProvider imageProvider, {bool immersive = true, void Function()? onViewerDismissed, bool useSafeArea = false, + bool swipeDismissable = false, Color backgroundColor = _defaultBackgroundColor, String closeButtonTooltip = _defaultCloseButtonTooltip, Color closeButtonColor = _defaultCloseButtonColor}) { return showImageViewerPager(context, SingleImageProvider(imageProvider), immersive: immersive, - onViewerDismissed: - onViewerDismissed != null ? (_) => onViewerDismissed() : null, + onViewerDismissed: onViewerDismissed != null ? (_) => onViewerDismissed() : null, useSafeArea: useSafeArea, + swipeDismissable: swipeDismissable, backgroundColor: backgroundColor, closeButtonTooltip: closeButtonTooltip, closeButtonColor: closeButtonColor); @@ -54,16 +55,17 @@ Future showImageViewer( /// The optional [onViewerDismissed] callback function is called with the index of /// the image that is displayed when the dialog is closed. /// The optional [useSafeArea] boolean defaults to false and is passed to [showDialog]. +/// The optional [swipeDismissable] boolean defaults to false allows swipe-down-to-dismiss. /// The [backgroundColor] defaults to black, but can be set to any other color. /// The [closeButtonTooltip] text is displayed when the user long-presses on the /// close button and is used for accessibility. /// The [closeButtonColor] defaults to white, but can be set to any other color. -Future showImageViewerPager( - BuildContext context, EasyImageProvider imageProvider, +Future showImageViewerPager(BuildContext context, EasyImageProvider imageProvider, {bool immersive = true, void Function(int)? onPageChanged, void Function(int)? onViewerDismissed, bool useSafeArea = false, + bool swipeDismissable = false, Color backgroundColor = _defaultBackgroundColor, String closeButtonTooltip = _defaultCloseButtonTooltip, Color closeButtonColor = _defaultCloseButtonColor}) { @@ -73,8 +75,7 @@ Future showImageViewerPager( } void Function()? internalPageChangeListener; - final pageController = - PageController(initialPage: imageProvider.initialIndex); + final pageController = PageController(initialPage: imageProvider.initialIndex); if (onPageChanged != null) { internalPageChangeListener = () { @@ -87,42 +88,47 @@ Future showImageViewerPager( context: context, useSafeArea: useSafeArea, builder: (context) { - return Dialog( + final dialog = Dialog( backgroundColor: backgroundColor, insetPadding: const EdgeInsets.all(0), - child: Stack( - clipBehavior: Clip.none, - alignment: Alignment.center, - children: [ - EasyImageViewPager( - easyImageProvider: imageProvider, - pageController: pageController), - Positioned( - top: 5, - right: 5, - child: IconButton( - icon: const Icon(Icons.close), - color: closeButtonColor, - tooltip: closeButtonTooltip, - onPressed: () { - Navigator.of(context).pop(); + child: Stack(clipBehavior: Clip.none, alignment: Alignment.center, children: [ + EasyImageViewPager(easyImageProvider: imageProvider, pageController: pageController), + Positioned( + top: 5, + right: 5, + child: IconButton( + icon: const Icon(Icons.close), + color: closeButtonColor, + tooltip: closeButtonTooltip, + onPressed: () { + Navigator.of(context).pop(); - if (onViewerDismissed != null) { - onViewerDismissed( - pageController.page?.round() ?? 0); - } + if (onViewerDismissed != null) { + onViewerDismissed(pageController.page?.round() ?? 0); + } - if (immersive) { - SystemChrome.setEnabledSystemUIMode( - SystemUiMode.edgeToEdge); - } - if (internalPageChangeListener != null) { - pageController - .removeListener(internalPageChangeListener); - } - pageController.dispose(); - }, - )) - ])); + if (immersive) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + } + if (internalPageChangeListener != null) { + pageController.removeListener(internalPageChangeListener); + } + pageController.dispose(); + }, + )) + ])); + + if (swipeDismissable) { + return Dismissible( + direction: DismissDirection.down, + resizeDuration: null, + onDismissed: (_) { + Navigator.of(context).pop(); + }, + key: const Key('dismissable_easy_image_viewer_dialog'), + child: dialog); + } else { + return dialog; + } }); } diff --git a/pubspec.yaml b/pubspec.yaml index 5ae485c..f7abfca 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: easy_image_viewer description: An easy image viewer with pinch & zoom, multi image, and built-in full-screen dialog support. -version: 1.0.4 +version: 1.0.5 homepage: https://github.com/thesmythgroup/easy_image_viewer environment: