Make SelectableText focus traversal behavior more standard (#80110)
diff --git a/packages/flutter/lib/src/material/selectable_text.dart b/packages/flutter/lib/src/material/selectable_text.dart
index 3b852b0..df8a7f6 100644
--- a/packages/flutter/lib/src/material/selectable_text.dart
+++ b/packages/flutter/lib/src/material/selectable_text.dart
@@ -304,7 +304,9 @@
/// focusNode.addListener(() { print(myFocusNode.hasFocus); });
/// ```
///
- /// If null, this widget will create its own [FocusNode].
+ /// If null, this widget will create its own [FocusNode] with
+ /// [FocusNode.skipTraversal] parameter set to `true`, which causes the widget
+ /// to be skipped over during focus traversal.
final FocusNode? focusNode;
/// The style to use for the text.
@@ -431,7 +433,8 @@
late _TextSpanEditingController _controller;
FocusNode? _focusNode;
- FocusNode get _effectiveFocusNode => widget.focusNode ?? (_focusNode ??= FocusNode());
+ FocusNode get _effectiveFocusNode =>
+ widget.focusNode ?? (_focusNode ??= FocusNode(skipTraversal: true));
bool _showSelectionHandles = false;
diff --git a/packages/flutter/test/widgets/selectable_text_test.dart b/packages/flutter/test/widgets/selectable_text_test.dart
index c3894c0..9f454ca 100644
--- a/packages/flutter/test/widgets/selectable_text_test.dart
+++ b/packages/flutter/test/widgets/selectable_text_test.dart
@@ -1417,6 +1417,45 @@
expect(controller.selection, equals(TextRange.empty));
});
+ testWidgets('Selectable text is skipped during focus traversal',
+ (WidgetTester tester) async {
+ final FocusNode firstFieldFocus = FocusNode();
+ final FocusNode lastFieldFocus = FocusNode();
+
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Material(
+ child: Center(
+ child: Column(
+ children: <Widget>[
+ TextField(
+ focusNode: firstFieldFocus,
+ autofocus: true,
+ ),
+ const SelectableText('some text'),
+ TextField(
+ focusNode: lastFieldFocus,
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+
+ await tester.pump();
+
+ expect(firstFieldFocus.hasFocus, isTrue);
+ expect(lastFieldFocus.hasFocus, isFalse);
+
+ firstFieldFocus.nextFocus();
+ await tester.pump();
+
+ // expecting focus to skip straight to the second field
+ expect(firstFieldFocus.hasFocus, isFalse);
+ expect(lastFieldFocus.hasFocus, isTrue);
+ });
+
testWidgets('Selectable text identifies as text field in semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);