Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow clicking on widgets behind the barrier #101

Merged
merged 3 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
mcquenji marked this conversation as resolved.
Show resolved Hide resolved
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
Loading