Refactor route focus node creation (#147390)
## Description This fixes an issue in the creation of the `FocusScope` in a route: the route should be creating the `FocusScope` widget it has with `withExternalFocusNode`, since it is modifying the node attributes in a builder. Also modified some `AnimatedBuilder`s to be `ListenableBuilder`s, since they're not using animations (no functionality change there, since the implementation of the two is identical). ## Related Issues - #147256 - Fixes #146844 ## Tests - Updated example test.
This commit is contained in:
parent
973d3a009b
commit
d274a2126f
@ -10,7 +10,7 @@ void main() {
|
||||
testWidgets('Tapping FAB increments counter', (WidgetTester tester) async {
|
||||
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('0'), findsOneWidget);
|
||||
|
@ -1022,6 +1022,8 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Only top most route can participate in focus traversal.
|
||||
focusScopeNode.skipTraversal = !widget.route.isCurrent;
|
||||
return AnimatedBuilder(
|
||||
animation: widget.route.restorationScopeId,
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
@ -1048,24 +1050,22 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
|
||||
},
|
||||
child: PrimaryScrollController(
|
||||
controller: primaryScrollController,
|
||||
child: FocusScope(
|
||||
node: focusScopeNode, // immutable
|
||||
// Only top most route can participate in focus traversal.
|
||||
skipTraversal: !widget.route.isCurrent,
|
||||
child: FocusScope.withExternalFocusNode(
|
||||
focusScopeNode: focusScopeNode, // immutable
|
||||
child: RepaintBoundary(
|
||||
child: AnimatedBuilder(
|
||||
animation: _listenable, // immutable
|
||||
child: ListenableBuilder(
|
||||
listenable: _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
|
||||
// This additional ListenableBuilder 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),
|
||||
ListenableBuilder(
|
||||
listenable: widget.route.navigator?.userGestureInProgressNotifier ?? ValueNotifier<bool>(false),
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
final bool ignoreEvents = _shouldIgnoreFocusRequest;
|
||||
focusScopeNode.canRequestFocus = !ignoreEvents;
|
||||
|
@ -1736,7 +1736,7 @@ void main() {
|
||||
semantics.dispose();
|
||||
}, 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
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
|
Loading…
x
Reference in New Issue
Block a user