engine
engine copied to clipboard
Support accessibility back gesture to pop route from view controller
Adapted suggestion from https://github.com/flutter/flutter/issues/74246#issuecomment-971326926 to pop the route if the view controller route if accessibilityPerformEscape is called at that level in the responder chain.
Ideally this could know if the route was successfully popped, and only then return YES (halting propagation through the responder chain).
I confirmed by editing https://docs.flutter.dev/cookbook/navigation/navigation-basics#interactive-example as a demo that that the two-finger scrub (move two fingers back and forth three times quickly, making a "z") navigates back to the last route, and that the SementicObject scrub, which already worked, continued to work.
I'm not super familiar with this code, hopefully there aren't further gotchas I'm not thinking of.
Fixes https://github.com/flutter/flutter/issues/74246
Pre-launch Checklist
- [x] I read the Contributor Guide and followed the process outlined there for submitting PRs.
- [x] I read the Tree Hygiene wiki page, which explains my responsibilities.
- [x] I read and followed the Flutter Style Guide and the C++, Objective-C, Java style guides.
- [x] I listed at least one issue that this PR fixes in the description above.
- [x] I added new tests to check the change I am making or feature I am adding, or the PR is test-exempt. See testing the engine for instructions on writing and running engine tests.
- [x] I updated/added relevant documentation (doc comments with
///). - [x] I signed the CLA.
- [x] All existing and new tests are passing.
If you need help, consider asking for advice on the #hackers-new channel on Discord.
@hangyujin are you the best person to review this since @chunhtai is out? Or should I add someone else?
Did you also try dismissing a dialog (example: https://api.flutter.dev/flutter/material/showDialog.html)? Seems to me it should probably work too
Did you also try dismissing a dialog (example: https://api.flutter.dev/flutter/material/showDialog.html)? Seems to me it should probably work too
Confirmed that worked on main and also on this branch.
Routes and modal combined, showing the modal is dismissed before the route is popped
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(
title: 'Navigation Basics',
home: FirstRoute(),
));
}
class FirstRoute extends StatelessWidget {
const FirstRoute({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('First Route'),
),
body: Center(
child: ElevatedButton(
child: const Text('Open route'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SecondRoute()),
);
},
),
),
);
}
}
class SecondRoute extends StatelessWidget {
const SecondRoute({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Second Route'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).restorablePush(_modalBuilder);
},
child: const Text('Open Modal'),
),
),
);
}
@pragma('vm:entry-point')
static Route<void> _modalBuilder(BuildContext context, Object? arguments) {
return CupertinoModalPopupRoute<void>(
builder: (BuildContext context) {
return CupertinoActionSheet(
title: const Text('Title'),
message: const Text('Message'),
actions: <CupertinoActionSheetAction>[
CupertinoActionSheetAction(
child: const Text('Action One'),
onPressed: () {
Navigator.pop(context);
},
),
CupertinoActionSheetAction(
child: const Text('Action Two'),
onPressed: () {
Navigator.pop(context);
},
),
],
);
},
);
}
}
(gesture done at :09 and :13) https://github.com/flutter/engine/assets/682784/8886cd6e-ab0d-4258-8015-f0dd0ed7329c