diff --git a/.gitignore b/.gitignore index ccd8bab..ea8e1b5 100644 --- a/.gitignore +++ b/.gitignore @@ -73,7 +73,5 @@ build/ !**/ios/**/default.pbxuser !**/ios/**/default.perspectivev3 - # coverage/ -.fvm/flutter_sdk \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 8f2b711..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "java.compile.nullAnalysis.mode": "disabled" -} \ No newline at end of file diff --git a/example/pubspec.lock b/example/pubspec.lock index 01c0e58..1664814 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -187,10 +187,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "14.2.5" sdks: dart: ">=3.5.0 <4.0.0" flutter: ">=3.24.0" diff --git a/lib/popover.dart b/lib/popover.dart index d18a70b..27222b8 100644 --- a/lib/popover.dart +++ b/lib/popover.dart @@ -1,3 +1,4 @@ export 'src/popover.dart'; export 'src/popover_direction.dart'; +export 'src/popover_route.dart'; export 'src/popover_transition.dart'; diff --git a/lib/src/popover.dart b/lib/src/popover.dart index dbb857b..08e810d 100644 --- a/lib/src/popover.dart +++ b/lib/src/popover.dart @@ -2,12 +2,16 @@ import 'package:flutter/material.dart'; import 'popover_direction.dart'; import 'popover_item.dart'; +import 'popover_route.dart'; import 'popover_transition.dart'; import 'utils/popover_utils.dart'; /// A popover is a transient view that appears above other content onscreen /// when you tap a control or in an area. /// +/// Note that when [allowClicksOnBackground] is set to true, +/// [barrierDismissible] is ignored (will behave as if it was set to false). +/// /// This function allows for customization of aspects of the dialog popup. /// /// `bodyBuilder` argument is builder which builds body/content of popover. @@ -110,6 +114,7 @@ Future showPopover({ String? barrierLabel, PopoverTransitionBuilder? popoverTransitionBuilder, Key? key, + bool allowClicksOnBackground = false, }) { constraints = (width != null || height != null) ? constraints?.tighten(width: width, height: height) ?? @@ -117,7 +122,8 @@ Future showPopover({ : constraints; return Navigator.of(context, rootNavigator: true).push( - RawDialogRoute( + PopoverRoute( + allowClicksOnBackground: allowClicksOnBackground, pageBuilder: (_, animation, __) { return PopScope( onPopInvokedWithResult: (didPop, _) => onPop?.call(), diff --git a/lib/src/popover_route.dart b/lib/src/popover_route.dart new file mode 100644 index 0000000..610b051 --- /dev/null +++ b/lib/src/popover_route.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +class PopoverRoute extends RawDialogRoute { + /// If true, widgets behind the barrier can receive pointer events. + final bool allowClicksOnBackground; + + PopoverRoute({ + required super.pageBuilder, + super.anchorPoint, + super.barrierColor, + super.barrierDismissible, + super.barrierLabel, + super.settings, + super.transitionBuilder, + super.transitionDuration, + super.traversalEdgeBehavior, + this.allowClicksOnBackground = false, + }); + + @override + Widget buildModalBarrier() { + return IgnorePointer( + ignoring: allowClicksOnBackground, + child: super.buildModalBarrier(), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 074c341..6d11304 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -180,10 +180,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "14.2.5" sdks: dart: ">=3.5.0 <4.0.0" flutter: ">=3.24.0" diff --git a/test/popover_test.dart b/test/popover_test.dart index 6058ca8..fa2558f 100644 --- a/test/popover_test.dart +++ b/test/popover_test.dart @@ -90,6 +90,92 @@ void main() { expect(find.text('Dialog'), findsOneWidget); }); + testWidgets('Clicks on background are disabled on default', (tester) async { + var didOpenDialog = false; + + await tester.pumpWidget( + createAppWithButtonThatLaunchesDialog( + dialogBuilder: (context) { + didOpenDialog = true; + return InkWell( + onTap: () { + Navigator.pop(context); + }, + child: const Text('This should not happen'), + ); + }, + ), + ); + + final BuildContext context = tester.element(find.text('Go')); + + showPopover( + context: context, + bodyBuilder: (context) { + return Container( + width: 100.0, + height: 100.0, + alignment: Alignment.center, + child: const Text('Popover'), + ); + }, + ); + + await tester.pumpAndSettle(const Duration(seconds: 1)); + expect(find.text('Popover'), findsOneWidget); + + // Tap on the 'Go' button, which should not open the dialog. + await tester.tap(find.text('Go')); + + await tester.pumpAndSettle(const Duration(seconds: 1)); + + expect(didOpenDialog, isFalse); + }); + + testWidgets('Popover configurable to allow clicks on background', + (tester) async { + var didOpenDialog = false; + + await tester.pumpWidget( + createAppWithButtonThatLaunchesDialog( + dialogBuilder: (context) { + didOpenDialog = true; + return InkWell( + onTap: () { + Navigator.pop(context); + }, + child: const Text('This should happen'), + ); + }, + ), + ); + + final BuildContext context = tester.element(find.text('Go')); + + showPopover( + context: context, + bodyBuilder: (context) { + return Container( + width: 100.0, + height: 100.0, + alignment: Alignment.center, + child: const Text('Popover'), + ); + }, + allowClicksOnBackground: true, + ); + + await tester.pumpAndSettle(const Duration(seconds: 1)); + expect(find.text('Popover'), findsOneWidget); + + // Tap on the 'Go' button, which should open the dialog. + await tester.tap(find.text('Go')); + + await tester.pumpAndSettle(const Duration(seconds: 1)); + + expect(didOpenDialog, isTrue); + }); + testWidgets('onPop is called after tap on barrier', (tester) async { var didPop = false;