Unverified Commit d8e72c6d authored by flutteractionsbot's avatar flutteractionsbot Committed by GitHub

[CP-stable]Refactor route focus node creation (#149378)

### Issue Link:
https://github.com/flutter/flutter/issues/148867

Cherry-picking https://github.com/flutter/flutter/pull/147390

### Changelog Description:
Fixes a focus issue that causes TextFields to not function after cupertino back swipes

### Impact Description:
iOS, macos, iOS web, macos web app that uses TextField

### Workaround:
disable cupertino back swipe

### Risk:
What is the risk level of this cherry-pick?

### Test Coverage:
Are you confident that your fix is well-tested by automated tests?

### Validation Steps:
What are the steps to validate that this fix works?

TextField should not be broken after a cupertino back swipe
parent a14f74ff
...@@ -10,7 +10,7 @@ void main() { ...@@ -10,7 +10,7 @@ void main() {
testWidgets('Tapping FAB increments counter', (WidgetTester tester) async { testWidgets('Tapping FAB increments counter', (WidgetTester tester) async {
await tester.pumpWidget(const example.ListenableBuilderExample()); await tester.pumpWidget(const example.ListenableBuilderExample());
String getCount() => (tester.widget(find.descendant(of: find.byType(ListenableBuilder), matching: find.byType(Text))) as Text).data!; String getCount() => (tester.widget(find.descendant(of: find.byType(ListenableBuilder).last, matching: find.byType(Text))) as Text).data!;
expect(find.text('Current counter value:'), findsOneWidget); expect(find.text('Current counter value:'), findsOneWidget);
expect(find.text('0'), findsOneWidget); expect(find.text('0'), findsOneWidget);
......
...@@ -1022,6 +1022,8 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> { ...@@ -1022,6 +1022,8 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Only top most route can participate in focus traversal.
focusScopeNode.skipTraversal = !widget.route.isCurrent;
return AnimatedBuilder( return AnimatedBuilder(
animation: widget.route.restorationScopeId, animation: widget.route.restorationScopeId,
builder: (BuildContext context, Widget? child) { builder: (BuildContext context, Widget? child) {
...@@ -1048,24 +1050,22 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> { ...@@ -1048,24 +1050,22 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
}, },
child: PrimaryScrollController( child: PrimaryScrollController(
controller: primaryScrollController, controller: primaryScrollController,
child: FocusScope( child: FocusScope.withExternalFocusNode(
node: focusScopeNode, // immutable focusScopeNode: focusScopeNode, // immutable
// Only top most route can participate in focus traversal.
skipTraversal: !widget.route.isCurrent,
child: RepaintBoundary( child: RepaintBoundary(
child: AnimatedBuilder( child: ListenableBuilder(
animation: _listenable, // immutable listenable: _listenable, // immutable
builder: (BuildContext context, Widget? child) { builder: (BuildContext context, Widget? child) {
return widget.route.buildTransitions( return widget.route.buildTransitions(
context, context,
widget.route.animation!, widget.route.animation!,
widget.route.secondaryAnimation!, widget.route.secondaryAnimation!,
// This additional AnimatedBuilder is include because if the // This additional ListenableBuilder is include because if the
// value of the userGestureInProgressNotifier changes, it's // value of the userGestureInProgressNotifier changes, it's
// only necessary to rebuild the IgnorePointer widget and set // only necessary to rebuild the IgnorePointer widget and set
// the focus node's ability to focus. // the focus node's ability to focus.
AnimatedBuilder( ListenableBuilder(
animation: widget.route.navigator?.userGestureInProgressNotifier ?? ValueNotifier<bool>(false), listenable: widget.route.navigator?.userGestureInProgressNotifier ?? ValueNotifier<bool>(false),
builder: (BuildContext context, Widget? child) { builder: (BuildContext context, Widget? child) {
final bool ignoreEvents = _shouldIgnoreFocusRequest; final bool ignoreEvents = _shouldIgnoreFocusRequest;
focusScopeNode.canRequestFocus = !ignoreEvents; focusScopeNode.canRequestFocus = !ignoreEvents;
......
...@@ -1736,7 +1736,7 @@ void main() { ...@@ -1736,7 +1736,7 @@ void main() {
semantics.dispose(); semantics.dispose();
}, variant: const TargetPlatformVariant(<TargetPlatform>{TargetPlatform.iOS})); }, variant: const TargetPlatformVariant(<TargetPlatform>{TargetPlatform.iOS}));
testWidgets('focus traverse correct when pop multiple page simultaneously', (WidgetTester tester) async { testWidgets('focus traversal is correct when popping multiple pages simultaneously', (WidgetTester tester) async {
// Regression test: https://github.com/flutter/flutter/issues/48903 // Regression test: https://github.com/flutter/flutter/issues/48903
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