diff --git a/packages/flutter/lib/src/widgets/focus_manager.dart b/packages/flutter/lib/src/widgets/focus_manager.dart index c0c87fbef9..71e49045ac 100644 --- a/packages/flutter/lib/src/widgets/focus_manager.dart +++ b/packages/flutter/lib/src/widgets/focus_manager.dart @@ -759,16 +759,6 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier { return null; } - /// Returns the amount of additional space to reveal around the attached widget - /// when focused inside a scrolling container via [Scrollable.ensureVisible]. - /// - /// For example, a value of `EdgeInsets.all(16.0)` ensures 16 pixels of - /// the adjacent widget are visible when this node receives focus. - /// - /// By default, this returns [FocusManager.defaultEnsureVisiblePadding] from the - /// associated [FocusManager], or [EdgeInsets.zero]. - EdgeInsets get ensureVisiblePadding => _manager?.defaultEnsureVisiblePadding ?? EdgeInsets.zero; - /// Returns the size of the attached widget's [RenderObject], in logical /// units. /// @@ -1720,20 +1710,6 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier { return handled; } - /// The default amount of additonal space to reveal when a widget is focused - /// inside a scrolling container via [Scrollable.ensureVisible]. - /// - /// Defaults to [EdgeInsets.zero], which does not add any additional space - /// when widgets are revealed. - /// - /// For example, a value of `EdgeInsets.all(16.0)` ensures 16 pixels of - /// the adjacent widget are visible when focusing a widget inside of a - /// scrolling container. - /// - /// Individual [FocusNode]s may increase or decrease this padding, use - /// [FocusNode.ensureVisiblePadding] to obtain a node's desired padding. - EdgeInsets defaultEnsureVisiblePadding = EdgeInsets.zero; - /// The node that currently has the primary focus. FocusNode? get primaryFocus => _primaryFocus; FocusNode? _primaryFocus; diff --git a/packages/flutter/lib/src/widgets/focus_traversal.dart b/packages/flutter/lib/src/widgets/focus_traversal.dart index 9ed8c34e27..7a2d49030f 100644 --- a/packages/flutter/lib/src/widgets/focus_traversal.dart +++ b/packages/flutter/lib/src/widgets/focus_traversal.dart @@ -36,7 +36,7 @@ void _focusAndEnsureVisible( ScrollPositionAlignmentPolicy alignmentPolicy = ScrollPositionAlignmentPolicy.explicit, }) { node.requestFocus(); - Scrollable.ensureVisible(node.context!, alignment: 1.0, padding: node.ensureVisiblePadding, alignmentPolicy: alignmentPolicy); + Scrollable.ensureVisible(node.context!, alignment: 1.0, alignmentPolicy: alignmentPolicy); } // A class to temporarily hold information about FocusTraversalGroups when diff --git a/packages/flutter/lib/src/widgets/page_view.dart b/packages/flutter/lib/src/widgets/page_view.dart index b0c5206535..f095b4d636 100644 --- a/packages/flutter/lib/src/widgets/page_view.dart +++ b/packages/flutter/lib/src/widgets/page_view.dart @@ -346,7 +346,6 @@ class _PagePosition extends ScrollPositionWithSingleContext implements PageMetri Future ensureVisible( RenderObject object, { double alignment = 0.0, - EdgeInsets padding = EdgeInsets.zero, Duration duration = Duration.zero, Curve curve = Curves.ease, ScrollPositionAlignmentPolicy alignmentPolicy = ScrollPositionAlignmentPolicy.explicit, diff --git a/packages/flutter/lib/src/widgets/scroll_position.dart b/packages/flutter/lib/src/widgets/scroll_position.dart index 08962febe1..92b0c429d8 100644 --- a/packages/flutter/lib/src/widgets/scroll_position.dart +++ b/packages/flutter/lib/src/widgets/scroll_position.dart @@ -676,10 +676,6 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics { /// Animates the position such that the given object is as visible as possible /// by just scrolling this position. /// - /// The [padding] is used to add extra space around the [object] when revealing it. - /// For example, `EdgeInsets.only(bottom: 16.0)` will ensure an additional 16 pixels - /// of space are visible below the [object]. - /// /// The optional `targetRenderObject` parameter is used to determine which area /// of that object should be as visible as possible. If `targetRenderObject` /// is null, the entire [RenderObject] (as defined by its @@ -690,12 +686,9 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics { /// /// * [ScrollPositionAlignmentPolicy] for the way in which `alignment` is /// applied, and the way the given `object` is aligned. - /// * [FocusNode.ensureVisiblePadding] which specifies the [padding] used when - /// a widget is focused via focus traversal. Future ensureVisible( RenderObject object, { double alignment = 0.0, - EdgeInsets padding = EdgeInsets.zero, Duration duration = Duration.zero, Curve curve = Curves.ease, ScrollPositionAlignmentPolicy alignmentPolicy = ScrollPositionAlignmentPolicy.explicit, @@ -706,18 +699,14 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics { final RenderAbstractViewport viewport = RenderAbstractViewport.of(object)!; assert(viewport != null); - Rect targetRect; + Rect? targetRect; if (targetRenderObject != null && targetRenderObject != object) { targetRect = MatrixUtils.transformRect( targetRenderObject.getTransformTo(object), object.paintBounds.intersect(targetRenderObject.paintBounds), ); - } else { - targetRect = object.paintBounds; } - targetRect = padding.inflateRect(targetRect); - double target; switch (alignmentPolicy) { case ScrollPositionAlignmentPolicy.explicit: diff --git a/packages/flutter/lib/src/widgets/scrollable.dart b/packages/flutter/lib/src/widgets/scrollable.dart index 6314beabee..f5a6652475 100644 --- a/packages/flutter/lib/src/widgets/scrollable.dart +++ b/packages/flutter/lib/src/widgets/scrollable.dart @@ -311,19 +311,9 @@ class Scrollable extends StatefulWidget { /// Scrolls the scrollables that enclose the given context so as to make the /// given context visible. - /// - /// The [padding] is used to add extra space around the [context]'s - /// associated widget when revealing it. For example, `EdgeInsets.only(bottom: 16.0)` - /// will ensure an additional 16 pixels of space are visible below the widget. - /// - /// See also: - /// - /// * [FocusNode.ensureVisiblePadding] which specifies the [padding] used when - /// a widget is focused via focus traversal. static Future ensureVisible( BuildContext context, { double alignment = 0.0, - EdgeInsets padding = EdgeInsets.zero, Duration duration = Duration.zero, Curve curve = Curves.ease, ScrollPositionAlignmentPolicy alignmentPolicy = ScrollPositionAlignmentPolicy.explicit, @@ -342,7 +332,6 @@ class Scrollable extends StatefulWidget { futures.add(scrollable.position.ensureVisible( context.findRenderObject()!, alignment: alignment, - padding: padding, duration: duration, curve: curve, alignmentPolicy: alignmentPolicy, diff --git a/packages/flutter/test/widgets/focus_traversal_test.dart b/packages/flutter/test/widgets/focus_traversal_test.dart index cf38ee6ff8..ff4c6e8054 100644 --- a/packages/flutter/test/widgets/focus_traversal_test.dart +++ b/packages/flutter/test/widgets/focus_traversal_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:math'; import 'dart:ui'; import 'package:flutter/material.dart'; @@ -1875,438 +1874,6 @@ void main() { expect(controller.offset, equals(0.0)); }, skip: isBrowser, variant: KeySimulatorTransitModeVariant.all()); // https://github.com/flutter/flutter/issues/35347 - testWidgets('Focus traversal inside a vertical scrollable applies ensure visible padding.', (WidgetTester tester) async { - tester.binding.focusManager.defaultEnsureVisiblePadding = const EdgeInsets.all(50.0); - - addTearDown(() { - tester.binding.focusManager.defaultEnsureVisiblePadding = EdgeInsets.zero; - }); - - const double minScrollExtent = 0.0; - const double maxScrollExtent = 700.0; - - final List items = List.generate(11, (int index) => index).toList(); - final List nodes = List.generate(11, (int index) => FocusNode(debugLabel: 'Item ${index + 1}')).toList(); - final FocusNode topNode = FocusNode(debugLabel: 'Header'); - final FocusNode bottomNode = FocusNode(debugLabel: 'Footer'); - final ScrollController controller = ScrollController(); - await tester.pumpWidget( - MaterialApp( - home: Column( - children: [ - Focus(focusNode: topNode, child: Container(height: 100)), - Expanded( - child: ListView( - controller: controller, - children: items.map((int item) { - return Focus( - focusNode: nodes[item], - child: Container(height: 100), - ); - }).toList(), - ), - ), - Focus(focusNode: bottomNode, child: Container(height: 100)), - ], - ), - ), - ); - - // Start at the top - expect(controller.offset, equals(0.0)); - await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); - await tester.pump(); - expect(topNode.hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - - // Enter the list. - await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); - await tester.pump(); - expect(nodes[0].hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - - // Go down until we hit the bottom of the visible area, taking padding into account. - for (int i = 1; i <= 2; ++i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); - await tester.pump(); - expect(controller.offset, equals(0.0), reason: 'Focusing item $i caused a scroll'); - } - - // Now keep going down, and the scrollable should scroll automatically. - for (int i = 3; i <= 10; ++i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); - await tester.pump(); - final double expectedOffset = min(100.0 * (i - 3) + 50.0, maxScrollExtent); - expect(controller.offset, equals(expectedOffset), reason: "Focusing item $i didn't cause a scroll to $expectedOffset"); - } - - // Now go one more, and see that the footer gets focused. - - await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); - await tester.pump(); - expect(bottomNode.hasPrimaryFocus, isTrue); - expect(controller.offset, equals(maxScrollExtent)); - - await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); - await tester.pump(); - expect(nodes[10].hasPrimaryFocus, isTrue); - expect(controller.offset, equals(maxScrollExtent)); - - // Now reverse directions and go back to the top. - - // These should not cause a scroll. - final double lowestOffset = controller.offset; - for (int i = 10; i >= 9; --i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); - await tester.pump(); - expect(controller.offset, equals(lowestOffset), reason: 'Focusing item $i caused a scroll'); - } - - // These should all cause a scroll. - for (int i = 8; i >= 1; --i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); - await tester.pump(); - final double expectedOffset = max(100.0 * (i - 1) - 50.0, minScrollExtent); - expect(controller.offset, equals(expectedOffset), reason: "Focusing item $i didn't cause a scroll"); - } - - // Back at the top. - expect(nodes[0].hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - - // Now we jump to the header. - await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); - await tester.pump(); - expect(topNode.hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - }, skip: isBrowser, variant: KeySimulatorTransitModeVariant.all()); // https://github.com/flutter/flutter/issues/35347 - - testWidgets('Focus traversal inside a horizontal scrollable applies ensure visible padding.', (WidgetTester tester) async { - tester.binding.focusManager.defaultEnsureVisiblePadding = const EdgeInsets.all(50.0); - - addTearDown(() { - tester.binding.focusManager.defaultEnsureVisiblePadding = EdgeInsets.zero; - }); - - const double minScrollExtent = 0.0; - const double maxScrollExtent = 500.0; - - final List items = List.generate(11, (int index) => index).toList(); - final List nodes = List.generate(11, (int index) => FocusNode(debugLabel: 'Item ${index + 1}')).toList(); - final FocusNode leftNode = FocusNode(debugLabel: 'Left Side'); - final FocusNode rightNode = FocusNode(debugLabel: 'Right Side'); - final ScrollController controller = ScrollController(); - await tester.pumpWidget( - MaterialApp( - home: Row( - children: [ - Focus(focusNode: leftNode, child: Container(width: 100)), - Expanded( - child: ListView( - scrollDirection: Axis.horizontal, - controller: controller, - children: items.map((int item) { - return Focus( - focusNode: nodes[item], - child: Container(width: 100), - ); - }).toList(), - ), - ), - Focus(focusNode: rightNode, child: Container(width: 100)), - ], - ), - ), - ); - - // Start at the right - expect(controller.offset, equals(0.0)); - await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); - await tester.pump(); - expect(leftNode.hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - - // Enter the list. - await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); - await tester.pump(); - expect(nodes[0].hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - - // Go right until we hit the right of the visible area, taking padding into account. - for (int i = 1; i <= 4; ++i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); - await tester.pump(); - expect(controller.offset, equals(0.0), reason: 'Focusing item $i caused a scroll'); - } - - // Now keep going right, and the scrollable should scroll automatically. - for (int i = 5; i <= 10; ++i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); - await tester.pump(); - final double expectedOffset = min(100.0 * (i - 5) + 50.0, maxScrollExtent); - expect(controller.offset, equals(expectedOffset), reason: "Focusing item $i didn't cause a scroll to $expectedOffset"); - } - - // Now go one more, and see that the right edge gets focused. - - await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); - await tester.pump(); - expect(rightNode.hasPrimaryFocus, isTrue); - expect(controller.offset, equals(maxScrollExtent)); - - await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); - await tester.pump(); - expect(nodes[10].hasPrimaryFocus, isTrue); - expect(controller.offset, equals(maxScrollExtent)); - - // Now reverse directions and go back to the left. - - // These should not cause a scroll. - final double lowestOffset = controller.offset; - for (int i = 10; i >= 7; --i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); - await tester.pump(); - expect(controller.offset, equals(lowestOffset), reason: 'Focusing item $i caused a scroll'); - } - - // These should all cause a scroll. - for (int i = 6; i >= 1; --i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); - await tester.pump(); - final double expectedOffset = max(100.0 * (i - 1) - 50.0, minScrollExtent); - expect(controller.offset, equals(expectedOffset), reason: "Focusing item $i didn't cause a scroll"); - } - - // Back at the left side of the scrollable. - expect(nodes[0].hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - - // Now we jump to the left edge of the app. - await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); - await tester.pump(); - expect(leftNode.hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - }, skip: isBrowser, variant: KeySimulatorTransitModeVariant.all()); // https://github.com/flutter/flutter/issues/35347 - - testWidgets('Focus traversal inside a vertical scrollable applies asymmetric ensure visible padding.', (WidgetTester tester) async { - const double leadingPadding = 25.0; - const double trailingPadding = 50.0; - - tester.binding.focusManager.defaultEnsureVisiblePadding = const EdgeInsets.only(top: leadingPadding, bottom: trailingPadding); - - addTearDown(() { - tester.binding.focusManager.defaultEnsureVisiblePadding = EdgeInsets.zero; - }); - - const double minScrollExtent = 0.0; - const double maxScrollExtent = 700.0; - - final List items = List.generate(11, (int index) => index).toList(); - final List nodes = List.generate(11, (int index) => FocusNode(debugLabel: 'Item ${index + 1}')).toList(); - final FocusNode topNode = FocusNode(debugLabel: 'Header'); - final FocusNode bottomNode = FocusNode(debugLabel: 'Footer'); - final ScrollController controller = ScrollController(); - await tester.pumpWidget( - MaterialApp( - home: Column( - children: [ - Focus(focusNode: topNode, child: Container(height: 100)), - Expanded( - child: ListView( - controller: controller, - children: items.map((int item) { - return Focus( - focusNode: nodes[item], - child: Container(height: 100), - ); - }).toList(), - ), - ), - Focus(focusNode: bottomNode, child: Container(height: 100)), - ], - ), - ), - ); - - // Start at the top - expect(controller.offset, equals(0.0)); - await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); - await tester.pump(); - expect(topNode.hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - - // Enter the list. - await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); - await tester.pump(); - expect(nodes[0].hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - - // Go down until we hit the bottom of the visible area, taking padding into account. - for (int i = 1; i <= 2; ++i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); - await tester.pump(); - expect(controller.offset, equals(0.0), reason: 'Focusing item $i caused a scroll'); - } - - // Now keep going down, and the scrollable should scroll automatically. - for (int i = 3; i <= 10; ++i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); - await tester.pump(); - final double expectedOffset = min(100.0 * (i - 3) + trailingPadding, maxScrollExtent); - expect(controller.offset, equals(expectedOffset), reason: "Focusing item $i didn't cause a scroll to $expectedOffset"); - } - - // Now go one more, and see that the footer gets focused. - - await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); - await tester.pump(); - expect(bottomNode.hasPrimaryFocus, isTrue); - expect(controller.offset, equals(maxScrollExtent)); - - await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); - await tester.pump(); - expect(nodes[10].hasPrimaryFocus, isTrue); - expect(controller.offset, equals(maxScrollExtent)); - - // Now reverse directions and go back to the top. - - // These should not cause a scroll. - final double lowestOffset = controller.offset; - for (int i = 10; i >= 9; --i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); - await tester.pump(); - expect(controller.offset, equals(lowestOffset), reason: 'Focusing item $i caused a scroll'); - } - - // These should all cause a scroll. - for (int i = 8; i >= 1; --i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); - await tester.pump(); - final double expectedOffset = max(100.0 * (i - 1) - leadingPadding, minScrollExtent); - expect(controller.offset, equals(expectedOffset), reason: "Focusing item $i didn't cause a scroll"); - } - - // Back at the top. - expect(nodes[0].hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - - // Now we jump to the header. - await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); - await tester.pump(); - expect(topNode.hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - }, skip: isBrowser, variant: KeySimulatorTransitModeVariant.all()); // https://github.com/flutter/flutter/issues/35347 - - testWidgets('Focus traversal inside a horizontal scrollable applies asymmetric ensure visible padding.', (WidgetTester tester) async { - const double leadingPadding = 25.0; - const double trailingPadding = 50.0; - - tester.binding.focusManager.defaultEnsureVisiblePadding = const EdgeInsets.only(left: leadingPadding, right: trailingPadding); - - addTearDown(() { - tester.binding.focusManager.defaultEnsureVisiblePadding = EdgeInsets.zero; - }); - - const double minScrollExtent = 0.0; - const double maxScrollExtent = 500.0; - - final List items = List.generate(11, (int index) => index).toList(); - final List nodes = List.generate(11, (int index) => FocusNode(debugLabel: 'Item ${index + 1}')).toList(); - final FocusNode leftNode = FocusNode(debugLabel: 'Left Side'); - final FocusNode rightNode = FocusNode(debugLabel: 'Right Side'); - final ScrollController controller = ScrollController(); - await tester.pumpWidget( - MaterialApp( - home: Row( - children: [ - Focus(focusNode: leftNode, child: Container(width: 100)), - Expanded( - child: ListView( - scrollDirection: Axis.horizontal, - controller: controller, - children: items.map((int item) { - return Focus( - focusNode: nodes[item], - child: Container(width: 100), - ); - }).toList(), - ), - ), - Focus(focusNode: rightNode, child: Container(width: 100)), - ], - ), - ), - ); - - // Start at the right - expect(controller.offset, equals(0.0)); - await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); - await tester.pump(); - expect(leftNode.hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - - // Enter the list. - await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); - await tester.pump(); - expect(nodes[0].hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - - // Go right until we hit the right of the visible area, taking padding into account. - for (int i = 1; i <= 4; ++i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); - await tester.pump(); - expect(controller.offset, equals(0.0), reason: 'Focusing item $i caused a scroll'); - } - - // Now keep going right, and the scrollable should scroll automatically. - for (int i = 5; i <= 10; ++i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); - await tester.pump(); - final double expectedOffset = min(100.0 * (i - 5) + trailingPadding, maxScrollExtent); - expect(controller.offset, equals(expectedOffset), reason: "Focusing item $i didn't cause a scroll to $expectedOffset"); - } - - // Now go one more, and see that the right edge gets focused. - - await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); - await tester.pump(); - expect(rightNode.hasPrimaryFocus, isTrue); - expect(controller.offset, equals(maxScrollExtent)); - - await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); - await tester.pump(); - expect(nodes[10].hasPrimaryFocus, isTrue); - expect(controller.offset, equals(maxScrollExtent)); - - // Now reverse directions and go back to the left. - - // These should not cause a scroll. - final double lowestOffset = controller.offset; - for (int i = 10; i >= 7; --i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); - await tester.pump(); - expect(controller.offset, equals(lowestOffset), reason: 'Focusing item $i caused a scroll'); - } - - // These should all cause a scroll. - for (int i = 6; i >= 1; --i) { - await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); - await tester.pump(); - final double expectedOffset = max(100.0 * (i - 1) - leadingPadding, minScrollExtent); - expect(controller.offset, equals(expectedOffset), reason: "Focusing item $i didn't cause a scroll"); - } - - // Back at the left side of the scrollable. - expect(nodes[0].hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - - // Now we jump to the left edge of the app. - await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); - await tester.pump(); - expect(leftNode.hasPrimaryFocus, isTrue); - expect(controller.offset, equals(0.0)); - }, skip: isBrowser, variant: KeySimulatorTransitModeVariant.all()); // https://github.com/flutter/flutter/issues/35347 - testWidgets('Arrow focus traversal actions can be re-enabled for text fields.', (WidgetTester tester) async { final GlobalKey upperLeftKey = GlobalKey(debugLabel: 'upperLeftKey'); final GlobalKey upperRightKey = GlobalKey(debugLabel: 'upperRightKey');