mirror of
https://github.com/flutter/flutter
synced 2024-10-13 11:42:54 +00:00
Ignore DismissIntent when barrier is not dismissible (#70156)
This commit is contained in:
parent
86f9ab511e
commit
f8d2f58b52
|
@ -650,9 +650,19 @@ mixin LocalHistoryRoute<T> on Route<T> {
|
|||
}
|
||||
|
||||
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
|
||||
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>> {
|
|||
setState(fn);
|
||||
}
|
||||
|
||||
static final Map<Type, Action<Intent>> _actionMap = <Type, Action<Intent>>{
|
||||
DismissIntent: _DismissModalAction(),
|
||||
};
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
|
@ -790,51 +796,57 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
|
|||
offstage: widget.route.offstage, // _routeSetState is called if this updates
|
||||
child: PageStorage(
|
||||
bucket: widget.route._storageBucket, // immutable
|
||||
child: Actions(
|
||||
actions: _actionMap,
|
||||
child: FocusScope(
|
||||
node: focusScopeNode, // immutable
|
||||
child: RepaintBoundary(
|
||||
child: AnimatedBuilder(
|
||||
animation: _listenable, // immutable
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return widget.route.buildTransitions(
|
||||
context,
|
||||
widget.route.animation!,
|
||||
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(
|
||||
child: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return Actions(
|
||||
actions: <Type, Action<Intent>>{
|
||||
DismissIntent: _DismissModalAction(context),
|
||||
},
|
||||
child: FocusScope(
|
||||
node: focusScopeNode, // immutable
|
||||
child: RepaintBoundary(
|
||||
child: AnimatedBuilder(
|
||||
animation: _listenable, // immutable
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return widget.route.buildTransitions(
|
||||
context,
|
||||
widget.route.animation!,
|
||||
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() {
|
|||
' FocusScope\n'
|
||||
' _ActionsMarker\n'
|
||||
' Actions\n'
|
||||
' Builder\n'
|
||||
' PageStorage\n'
|
||||
' Offstage\n'
|
||||
' _ModalScopeStatus\n'
|
||||
|
|
|
@ -1641,6 +1641,29 @@ void main() {
|
|||
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 {
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
|
|
Loading…
Reference in a new issue