Unverified Commit f8d2f58b authored by Todd Volkert's avatar Todd Volkert Committed by GitHub

Ignore DismissIntent when barrier is not dismissible (#70156)

parent 86f9ab51
...@@ -650,9 +650,19 @@ mixin LocalHistoryRoute<T> on Route<T> { ...@@ -650,9 +650,19 @@ mixin LocalHistoryRoute<T> on Route<T> {
} }
class _DismissModalAction extends DismissAction { class _DismissModalAction extends DismissAction {
_DismissModalAction(this.context);
final BuildContext context;
@override
bool isEnabled(DismissIntent intent) {
final ModalRoute<dynamic> route = ModalRoute.of<dynamic>(context)!;
return route.barrierDismissible;
}
@override @override
Object invoke(DismissIntent intent) { Object invoke(DismissIntent intent) {
return Navigator.of(primaryFocus!.context!)!.maybePop(); return Navigator.of(context)!.maybePop();
} }
} }
...@@ -767,10 +777,6 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> { ...@@ -767,10 +777,6 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
setState(fn); setState(fn);
} }
static final Map<Type, Action<Intent>> _actionMap = <Type, Action<Intent>>{
DismissIntent: _DismissModalAction(),
};
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AnimatedBuilder( return AnimatedBuilder(
...@@ -790,51 +796,57 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> { ...@@ -790,51 +796,57 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
offstage: widget.route.offstage, // _routeSetState is called if this updates offstage: widget.route.offstage, // _routeSetState is called if this updates
child: PageStorage( child: PageStorage(
bucket: widget.route._storageBucket, // immutable bucket: widget.route._storageBucket, // immutable
child: Actions( child: Builder(
actions: _actionMap, builder: (BuildContext context) {
child: FocusScope( return Actions(
node: focusScopeNode, // immutable actions: <Type, Action<Intent>>{
child: RepaintBoundary( DismissIntent: _DismissModalAction(context),
child: AnimatedBuilder( },
animation: _listenable, // immutable child: FocusScope(
builder: (BuildContext context, Widget? child) { node: focusScopeNode, // immutable
return widget.route.buildTransitions( child: RepaintBoundary(
context, child: AnimatedBuilder(
widget.route.animation!, animation: _listenable, // immutable
widget.route.secondaryAnimation!, builder: (BuildContext context, Widget? child) {
// This additional AnimatedBuilder is include because if the return widget.route.buildTransitions(
// value of the userGestureInProgressNotifier changes, it's
// only necessary to rebuild the IgnorePointer widget and set
// the focus node's ability to focus.
AnimatedBuilder(
animation: widget.route.navigator?.userGestureInProgressNotifier ?? ValueNotifier<bool>(false),
builder: (BuildContext context, Widget? child) {
final bool ignoreEvents = _shouldIgnoreFocusRequest;
focusScopeNode.canRequestFocus = !ignoreEvents;
return IgnorePointer(
ignoring: ignoreEvents,
child: child,
);
},
child: child,
),
);
},
child: _page ??= RepaintBoundary(
key: widget.route._subtreeKey, // immutable
child: Builder(
builder: (BuildContext context) {
return widget.route.buildPage(
context, context,
widget.route.animation!, widget.route.animation!,
widget.route.secondaryAnimation!, widget.route.secondaryAnimation!,
// This additional AnimatedBuilder is include because if the
// value of the userGestureInProgressNotifier changes, it's
// only necessary to rebuild the IgnorePointer widget and set
// the focus node's ability to focus.
AnimatedBuilder(
animation: widget.route.navigator?.userGestureInProgressNotifier ?? ValueNotifier<bool>(false),
builder: (BuildContext context, Widget? child) {
final bool ignoreEvents = _shouldIgnoreFocusRequest;
focusScopeNode.canRequestFocus = !ignoreEvents;
return IgnorePointer(
ignoring: ignoreEvents,
child: child,
);
},
child: child,
),
); );
}, },
child: _page ??= RepaintBoundary(
key: widget.route._subtreeKey, // immutable
child: Builder(
builder: (BuildContext context) {
return widget.route.buildPage(
context,
widget.route.animation!,
widget.route.secondaryAnimation!,
);
},
),
),
), ),
), ),
), ),
), );
), },
), ),
), ),
), ),
......
...@@ -137,6 +137,7 @@ void main() { ...@@ -137,6 +137,7 @@ void main() {
' FocusScope\n' ' FocusScope\n'
' _ActionsMarker\n' ' _ActionsMarker\n'
' Actions\n' ' Actions\n'
' Builder\n'
' PageStorage\n' ' PageStorage\n'
' Offstage\n' ' Offstage\n'
' _ModalScopeStatus\n' ' _ModalScopeStatus\n'
......
...@@ -1641,6 +1641,29 @@ void main() { ...@@ -1641,6 +1641,29 @@ void main() {
expect(find.text('dialog1'), findsNothing); expect(find.text('dialog1'), findsNothing);
}); });
testWidgets('can not be dismissed with escape keyboard shortcut if barrier not dismissible', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
await tester.pumpWidget(MaterialApp(
navigatorKey: navigatorKey,
home: const Text('dummy1'),
));
final Element textOnPageOne = tester.element(find.text('dummy1'));
// Show a simple dialog
showDialog<void>(
context: textOnPageOne,
barrierDismissible: false,
builder: (BuildContext context) => const Text('dialog1'),
);
await tester.pumpAndSettle();
expect(find.text('dialog1'), findsOneWidget);
// Try to dismiss the dialog with the shortcut key
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
await tester.pumpAndSettle();
expect(find.text('dialog1'), findsOneWidget);
});
testWidgets('ModalRoute.of works for void routes', (WidgetTester tester) async { testWidgets('ModalRoute.of works for void routes', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment