@@ -27,6 +27,9 @@ import 'restoration_properties.dart';
2727import 'routes.dart' ;
2828import 'ticker_provider.dart' ;
2929
30+ // Duration for delay before refocusing in android so that the focus won't be interrupted.
31+ const Duration _kAndroidRefocusingDelayDuration = Duration (milliseconds: 300 );
32+
3033// Examples can assume:
3134// typedef MyAppHome = Placeholder;
3235// typedef MyHomePage = Placeholder;
@@ -372,6 +375,8 @@ abstract class Route<T> {
372375 Future <T ?> get popped => _popCompleter.future;
373376 final Completer <T ?> _popCompleter = Completer <T ?>();
374377
378+ final Completer <T ?> _disposeCompleter = Completer <T ?>();
379+
375380 /// A request was made to pop this route. If the route can handle it
376381 /// internally (e.g. because it has its own stack of internal state) then
377382 /// return false, otherwise return true (by returning the value of calling
@@ -511,6 +516,7 @@ abstract class Route<T> {
511516 void dispose () {
512517 _navigator = null ;
513518 _restorationScopeId.dispose ();
519+ _disposeCompleter.complete ();
514520 if (kFlutterMemoryAllocationsEnabled) {
515521 MemoryAllocations .instance.dispatchObjectDisposed (object: this );
516522 }
@@ -2940,6 +2946,7 @@ class _RouteEntry extends RouteTransitionRecord {
29402946 Route <dynamic >? lastAnnouncedPreviousRoute = notAnnounced; // last argument to Route.didChangePrevious
29412947 WeakReference <Route <dynamic >> lastAnnouncedPoppedNextRoute = WeakReference <Route <dynamic >>(notAnnounced); // last argument to Route.didPopNext
29422948 Route <dynamic >? lastAnnouncedNextRoute = notAnnounced; // last argument to Route.didChangeNext
2949+ int ? lastFocusNode; // The last focused semantic node for the route entry.
29432950
29442951 /// Restoration ID to be used for the encapsulating route when restoration is
29452952 /// enabled for it or null if restoration cannot be enabled for it.
@@ -3028,6 +3035,24 @@ class _RouteEntry extends RouteTransitionRecord {
30283035 void handleDidPopNext (Route <dynamic > poppedRoute) {
30293036 route.didPopNext (poppedRoute);
30303037 lastAnnouncedPoppedNextRoute = WeakReference <Route <dynamic >>(poppedRoute);
3038+ if (lastFocusNode != null ) {
3039+ // Move focus back to the last focused node.
3040+ poppedRoute._disposeCompleter.future.then ((dynamic result) async {
3041+ switch (defaultTargetPlatform) {
3042+ case TargetPlatform .android:
3043+ // In the Android platform, we have to wait for the system refocus to complete before
3044+ // sending the refocus message. Otherwise, the refocus message will be ignored.
3045+ // TODO(hangyujin): update this logic if Android provide a better way to do so.
3046+ final int ? reFocusNode = lastFocusNode;
3047+ await Future <void >.delayed (_kAndroidRefocusingDelayDuration);
3048+ SystemChannels .accessibility.send (const FocusSemanticEvent ().toMap (nodeId: reFocusNode));
3049+ case TargetPlatform .iOS:
3050+ SystemChannels .accessibility.send (const FocusSemanticEvent ().toMap (nodeId: lastFocusNode));
3051+ case _:
3052+ break ;
3053+ }
3054+ });
3055+ }
30313056 }
30323057
30333058 /// Process the to-be-popped route.
@@ -3576,9 +3601,16 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
35763601 SystemNavigator .selectSingleEntryHistory ();
35773602 }
35783603
3604+ ServicesBinding .instance.accessibilityFocus.addListener (_recordLastFocus);
35793605 _history.addListener (_handleHistoryChanged);
35803606 }
35813607
3608+ // Record the last focused node in route entry.
3609+ void _recordLastFocus (){
3610+ final _RouteEntry ? entry = _history.where (_RouteEntry .isPresentPredicate).lastOrNull;
3611+ entry? .lastFocusNode = ServicesBinding .instance.accessibilityFocus.value;
3612+ }
3613+
35823614 // Use [_nextPagelessRestorationScopeId] to get the next id.
35833615 final RestorableNum <int > _rawNextPagelessRestorationScopeId = RestorableNum <int >(0 );
35843616
@@ -3871,6 +3903,7 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
38713903 _rawNextPagelessRestorationScopeId.dispose ();
38723904 _serializableHistory.dispose ();
38733905 userGestureInProgressNotifier.dispose ();
3906+ ServicesBinding .instance.accessibilityFocus.removeListener (_recordLastFocus);
38743907 _history.removeListener (_handleHistoryChanged);
38753908 _history.dispose ();
38763909 super .dispose ();
0 commit comments