Skip to content

Commit

Permalink
feat: Allow clicking on widgets behind the barrier (#101)
Browse files Browse the repository at this point in the history
* feat: allow clicking on widgets behind the barrier

* chore: remove and untrack unwanted files

* test: cover `allowClicksOnBackground` cases
  • Loading branch information
mcquenji authored Jan 5, 2025
1 parent c313899 commit 1283ecd
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 10 deletions.
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,5 @@ build/
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3


#
coverage/
.fvm/flutter_sdk
3 changes: 0 additions & 3 deletions .vscode/settings.json

This file was deleted.

4 changes: 2 additions & 2 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
1 change: 1 addition & 0 deletions lib/popover.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export 'src/popover.dart';
export 'src/popover_direction.dart';
export 'src/popover_route.dart';
export 'src/popover_transition.dart';
8 changes: 7 additions & 1 deletion lib/src/popover.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -110,14 +114,16 @@ Future<T?> showPopover<T extends Object?>({
String? barrierLabel,
PopoverTransitionBuilder? popoverTransitionBuilder,
Key? key,
bool allowClicksOnBackground = false,
}) {
constraints = (width != null || height != null)
? constraints?.tighten(width: width, height: height) ??
BoxConstraints.tightFor(width: width, height: height)
: constraints;

return Navigator.of(context, rootNavigator: true).push<T>(
RawDialogRoute<T>(
PopoverRoute<T>(
allowClicksOnBackground: allowClicksOnBackground,
pageBuilder: (_, animation, __) {
return PopScope(
onPopInvokedWithResult: (didPop, _) => onPop?.call(),
Expand Down
27 changes: 27 additions & 0 deletions lib/src/popover_route.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';

class PopoverRoute<T> extends RawDialogRoute<T> {
/// 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(),
);
}
}
4 changes: 2 additions & 2 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
86 changes: 86 additions & 0 deletions test/popover_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down

0 comments on commit 1283ecd

Please sign in to comment.