Unverified Commit 0f2254a5 authored by Tong Mu's avatar Tong Mu Committed by GitHub

ModalRoute resumes previous focus on didPopNext (#33152)

This PR changes ModalRoute so that, when the route on top of it is popped, it requests to focus on the last focus.
parent 3ebebebb
......@@ -1086,6 +1086,17 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
return await super.willPop();
}
@override
void didPopNext(Route<dynamic> nextRoute) {
super.didPopNext(nextRoute);
// When the route on top of this route is popped, resume focus onto the
// widget that received the last focus.
final _ModalScopeState<T> modalScopeState = _scopeKey.currentState;
if (modalScopeState != null) {
modalScopeState.focusScopeNode.requestFocus();
}
}
/// Enables this route to veto attempts by the user to dismiss it.
///
/// This callback is typically added using a [WillPopScope] widget. That
......
......@@ -40,11 +40,6 @@ void main() {
Navigator.of(tester.element(find.text('Dialog'))).pop();
await tester.pump();
expect(tester.testTextInput.isVisible, isFalse);
await tester.tap(find.byType(TextField));
await tester.idle();
expect(tester.testTextInput.isVisible, isTrue);
await tester.pumpWidget(Container());
......
......@@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart';
import 'package:mockito/mockito.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
final List<String> results = <String>[];
......@@ -503,6 +504,52 @@ void main() {
verifyNoMoreInteractions(pageRouteAware);
});
});
group('ModalRoute', () {
testWidgets('should resume focus when the next route is popped', (WidgetTester tester) async {
// TODO(tongmu): It's currently using PageRoute (from MaterialApp)
// instead of ModalRoute, which might leave a risk but avoids building
// everything from scratch by using the existing setup by MaterialApp and
// TextField as a focusable widget. We might want to write a better test.
final FocusNode node = FocusNode();
BuildContext pageContext;
final Widget widget = MaterialApp(
theme: ThemeData(),
home: Material(
child: Builder(
builder: (BuildContext context) {
pageContext = context;
return Column(
children: <Widget>[
const TextField(),
TextField(focusNode: node),
],
);
}
),
),
);
await tester.pumpWidget(widget);
expect(node.hasFocus, false);
node.requestFocus();
await tester.pumpAndSettle();
expect(node.hasFocus, true);
showDialog<void>(
context: pageContext,
builder: (BuildContext _) => const Center(),
);
await tester.pumpAndSettle();
expect(node.hasFocus, false);
Navigator.of(pageContext).pop();
await tester.pumpAndSettle();
expect(node.hasFocus, true);
});
});
}
class MockPageRoute extends Mock implements PageRoute<dynamic> { }
......
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