mirror of
https://github.com/flutter/flutter
synced 2024-10-13 03:32:55 +00:00
Disable arrow key focus navigation for text fields (#42533)
This disables the arrow key focus navigation for text fields. This was non-standard behavior for text fields, although it remains useful for other kinds of controls. Fixes #42259
This commit is contained in:
parent
1d4d63ace1
commit
6b2cc85546
|
@ -599,6 +599,17 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
|
|||
bool get selectionEnabled => widget.selectionEnabled;
|
||||
// End of API for TextSelectionGestureDetectorBuilderDelegate.
|
||||
|
||||
// Disables all directional focus actions inside of a text field, since up and
|
||||
// down shouldn't go to another field, even in a single line text field. We
|
||||
// remap the keys rather than the actions, since someone might want to invoke
|
||||
// a directional navigation action from another key binding.
|
||||
final Map<LogicalKeySet, Intent> _disabledNavigationKeys = <LogicalKeySet, Intent>{
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowUp): const Intent(DoNothingAction.key),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowDown): const Intent(DoNothingAction.key),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowLeft): const Intent(DoNothingAction.key),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowRight): const Intent(DoNothingAction.key),
|
||||
};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
@ -859,48 +870,51 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
|
|||
final Widget paddedEditable = Padding(
|
||||
padding: widget.padding,
|
||||
child: RepaintBoundary(
|
||||
child: EditableText(
|
||||
key: editableTextKey,
|
||||
controller: controller,
|
||||
readOnly: widget.readOnly,
|
||||
toolbarOptions: widget.toolbarOptions,
|
||||
showCursor: widget.showCursor,
|
||||
showSelectionHandles: _showSelectionHandles,
|
||||
focusNode: _effectiveFocusNode,
|
||||
keyboardType: widget.keyboardType,
|
||||
textInputAction: widget.textInputAction,
|
||||
textCapitalization: widget.textCapitalization,
|
||||
style: textStyle,
|
||||
strutStyle: widget.strutStyle,
|
||||
textAlign: widget.textAlign,
|
||||
autofocus: widget.autofocus,
|
||||
obscureText: widget.obscureText,
|
||||
autocorrect: widget.autocorrect,
|
||||
maxLines: widget.maxLines,
|
||||
minLines: widget.minLines,
|
||||
expands: widget.expands,
|
||||
selectionColor: CupertinoTheme.of(context).primaryColor.withOpacity(0.2),
|
||||
selectionControls: widget.selectionEnabled
|
||||
? cupertinoTextSelectionControls : null,
|
||||
onChanged: widget.onChanged,
|
||||
onSelectionChanged: _handleSelectionChanged,
|
||||
onEditingComplete: widget.onEditingComplete,
|
||||
onSubmitted: widget.onSubmitted,
|
||||
inputFormatters: formatters,
|
||||
rendererIgnoresPointer: true,
|
||||
cursorWidth: widget.cursorWidth,
|
||||
cursorRadius: widget.cursorRadius,
|
||||
cursorColor: cursorColor,
|
||||
cursorOpacityAnimates: true,
|
||||
cursorOffset: cursorOffset,
|
||||
paintCursorAboveText: true,
|
||||
backgroundCursorColor: CupertinoDynamicColor.resolve(CupertinoColors.inactiveGray, context),
|
||||
scrollPadding: widget.scrollPadding,
|
||||
keyboardAppearance: keyboardAppearance,
|
||||
dragStartBehavior: widget.dragStartBehavior,
|
||||
scrollController: widget.scrollController,
|
||||
scrollPhysics: widget.scrollPhysics,
|
||||
enableInteractiveSelection: widget.enableInteractiveSelection,
|
||||
child: Shortcuts(
|
||||
shortcuts: _disabledNavigationKeys,
|
||||
child: EditableText(
|
||||
key: editableTextKey,
|
||||
controller: controller,
|
||||
readOnly: widget.readOnly,
|
||||
toolbarOptions: widget.toolbarOptions,
|
||||
showCursor: widget.showCursor,
|
||||
showSelectionHandles: _showSelectionHandles,
|
||||
focusNode: _effectiveFocusNode,
|
||||
keyboardType: widget.keyboardType,
|
||||
textInputAction: widget.textInputAction,
|
||||
textCapitalization: widget.textCapitalization,
|
||||
style: textStyle,
|
||||
strutStyle: widget.strutStyle,
|
||||
textAlign: widget.textAlign,
|
||||
autofocus: widget.autofocus,
|
||||
obscureText: widget.obscureText,
|
||||
autocorrect: widget.autocorrect,
|
||||
maxLines: widget.maxLines,
|
||||
minLines: widget.minLines,
|
||||
expands: widget.expands,
|
||||
selectionColor: CupertinoTheme.of(context).primaryColor.withOpacity(0.2),
|
||||
selectionControls: widget.selectionEnabled
|
||||
? cupertinoTextSelectionControls : null,
|
||||
onChanged: widget.onChanged,
|
||||
onSelectionChanged: _handleSelectionChanged,
|
||||
onEditingComplete: widget.onEditingComplete,
|
||||
onSubmitted: widget.onSubmitted,
|
||||
inputFormatters: formatters,
|
||||
rendererIgnoresPointer: true,
|
||||
cursorWidth: widget.cursorWidth,
|
||||
cursorRadius: widget.cursorRadius,
|
||||
cursorColor: cursorColor,
|
||||
cursorOpacityAnimates: true,
|
||||
cursorOffset: cursorOffset,
|
||||
paintCursorAboveText: true,
|
||||
backgroundCursorColor: CupertinoDynamicColor.resolve(CupertinoColors.inactiveGray, context),
|
||||
scrollPadding: widget.scrollPadding,
|
||||
keyboardAppearance: keyboardAppearance,
|
||||
dragStartBehavior: widget.dragStartBehavior,
|
||||
scrollController: widget.scrollController,
|
||||
scrollPhysics: widget.scrollPhysics,
|
||||
enableInteractiveSelection: widget.enableInteractiveSelection,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -705,6 +705,17 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
|
|||
|
||||
bool _isHovering = false;
|
||||
|
||||
// Disables all directional focus actions inside of a text field, since up and
|
||||
// down shouldn't go to another field, even in a single line text field. We
|
||||
// remap the keys rather than the actions, since someone might want to invoke
|
||||
// a directional navigation action from another key binding.
|
||||
final Map<LogicalKeySet, Intent> _disabledNavigationKeys = <LogicalKeySet, Intent>{
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowUp): const Intent(DoNothingAction.key),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowDown): const Intent(DoNothingAction.key),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowLeft): const Intent(DoNothingAction.key),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowRight): const Intent(DoNothingAction.key),
|
||||
};
|
||||
|
||||
bool get needsCounter => widget.maxLength != null
|
||||
&& widget.decoration != null
|
||||
&& widget.decoration.counterText == null;
|
||||
|
@ -936,49 +947,52 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
|
|||
}
|
||||
|
||||
Widget child = RepaintBoundary(
|
||||
child: EditableText(
|
||||
key: editableTextKey,
|
||||
readOnly: widget.readOnly,
|
||||
toolbarOptions: widget.toolbarOptions,
|
||||
showCursor: widget.showCursor,
|
||||
showSelectionHandles: _showSelectionHandles,
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
keyboardType: widget.keyboardType,
|
||||
textInputAction: widget.textInputAction,
|
||||
textCapitalization: widget.textCapitalization,
|
||||
style: style,
|
||||
strutStyle: widget.strutStyle,
|
||||
textAlign: widget.textAlign,
|
||||
textDirection: widget.textDirection,
|
||||
autofocus: widget.autofocus,
|
||||
obscureText: widget.obscureText,
|
||||
autocorrect: widget.autocorrect,
|
||||
maxLines: widget.maxLines,
|
||||
minLines: widget.minLines,
|
||||
expands: widget.expands,
|
||||
selectionColor: themeData.textSelectionColor,
|
||||
selectionControls: widget.selectionEnabled ? textSelectionControls : null,
|
||||
onChanged: widget.onChanged,
|
||||
onSelectionChanged: _handleSelectionChanged,
|
||||
onEditingComplete: widget.onEditingComplete,
|
||||
onSubmitted: widget.onSubmitted,
|
||||
onSelectionHandleTapped: _handleSelectionHandleTapped,
|
||||
inputFormatters: formatters,
|
||||
rendererIgnoresPointer: true,
|
||||
cursorWidth: widget.cursorWidth,
|
||||
cursorRadius: cursorRadius,
|
||||
cursorColor: cursorColor,
|
||||
cursorOpacityAnimates: cursorOpacityAnimates,
|
||||
cursorOffset: cursorOffset,
|
||||
paintCursorAboveText: paintCursorAboveText,
|
||||
backgroundCursorColor: CupertinoColors.inactiveGray,
|
||||
scrollPadding: widget.scrollPadding,
|
||||
keyboardAppearance: keyboardAppearance,
|
||||
enableInteractiveSelection: widget.enableInteractiveSelection,
|
||||
dragStartBehavior: widget.dragStartBehavior,
|
||||
scrollController: widget.scrollController,
|
||||
scrollPhysics: widget.scrollPhysics,
|
||||
child: Shortcuts(
|
||||
shortcuts: _disabledNavigationKeys,
|
||||
child: EditableText(
|
||||
key: editableTextKey,
|
||||
readOnly: widget.readOnly,
|
||||
toolbarOptions: widget.toolbarOptions,
|
||||
showCursor: widget.showCursor,
|
||||
showSelectionHandles: _showSelectionHandles,
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
keyboardType: widget.keyboardType,
|
||||
textInputAction: widget.textInputAction,
|
||||
textCapitalization: widget.textCapitalization,
|
||||
style: style,
|
||||
strutStyle: widget.strutStyle,
|
||||
textAlign: widget.textAlign,
|
||||
textDirection: widget.textDirection,
|
||||
autofocus: widget.autofocus,
|
||||
obscureText: widget.obscureText,
|
||||
autocorrect: widget.autocorrect,
|
||||
maxLines: widget.maxLines,
|
||||
minLines: widget.minLines,
|
||||
expands: widget.expands,
|
||||
selectionColor: themeData.textSelectionColor,
|
||||
selectionControls: widget.selectionEnabled ? textSelectionControls : null,
|
||||
onChanged: widget.onChanged,
|
||||
onSelectionChanged: _handleSelectionChanged,
|
||||
onEditingComplete: widget.onEditingComplete,
|
||||
onSubmitted: widget.onSubmitted,
|
||||
onSelectionHandleTapped: _handleSelectionHandleTapped,
|
||||
inputFormatters: formatters,
|
||||
rendererIgnoresPointer: true,
|
||||
cursorWidth: widget.cursorWidth,
|
||||
cursorRadius: cursorRadius,
|
||||
cursorColor: cursorColor,
|
||||
cursorOpacityAnimates: cursorOpacityAnimates,
|
||||
cursorOffset: cursorOffset,
|
||||
paintCursorAboveText: paintCursorAboveText,
|
||||
backgroundCursorColor: CupertinoColors.inactiveGray,
|
||||
scrollPadding: widget.scrollPadding,
|
||||
keyboardAppearance: keyboardAppearance,
|
||||
enableInteractiveSelection: widget.enableInteractiveSelection,
|
||||
dragStartBehavior: widget.dragStartBehavior,
|
||||
scrollController: widget.scrollController,
|
||||
scrollPhysics: widget.scrollPhysics,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
@ -3870,4 +3870,90 @@ void main() {
|
|||
},
|
||||
);
|
||||
});
|
||||
testWidgets("Arrow keys don't move input focus", (WidgetTester tester) async {
|
||||
final TextEditingController controller1 = TextEditingController();
|
||||
final TextEditingController controller2 = TextEditingController();
|
||||
final TextEditingController controller3 = TextEditingController();
|
||||
final TextEditingController controller4 = TextEditingController();
|
||||
final TextEditingController controller5 = TextEditingController();
|
||||
final FocusNode focusNode1 = FocusNode(debugLabel: 'Field 1');
|
||||
final FocusNode focusNode2 = FocusNode(debugLabel: 'Field 2');
|
||||
final FocusNode focusNode3 = FocusNode(debugLabel: 'Field 3');
|
||||
final FocusNode focusNode4 = FocusNode(debugLabel: 'Field 4');
|
||||
final FocusNode focusNode5 = FocusNode(debugLabel: 'Field 5');
|
||||
|
||||
// Lay out text fields in a "+" formation, and focus the center one.
|
||||
await tester.pumpWidget(CupertinoApp(
|
||||
home: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 100.0,
|
||||
child: CupertinoTextField(
|
||||
controller: controller1,
|
||||
focusNode: focusNode1,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 100.0,
|
||||
child: CupertinoTextField(
|
||||
controller: controller2,
|
||||
focusNode: focusNode2,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 100.0,
|
||||
child: CupertinoTextField(
|
||||
controller: controller3,
|
||||
focusNode: focusNode3,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 100.0,
|
||||
child: CupertinoTextField(
|
||||
controller: controller4,
|
||||
focusNode: focusNode4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
width: 100.0,
|
||||
child: CupertinoTextField(
|
||||
controller: controller5,
|
||||
focusNode: focusNode5,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
focusNode3.requestFocus();
|
||||
await tester.pump();
|
||||
expect(focusNode3.hasPrimaryFocus, isTrue);
|
||||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
|
||||
await tester.pump();
|
||||
expect(focusNode3.hasPrimaryFocus, isTrue);
|
||||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
|
||||
await tester.pump();
|
||||
expect(focusNode3.hasPrimaryFocus, isTrue);
|
||||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft);
|
||||
await tester.pump();
|
||||
expect(focusNode3.hasPrimaryFocus, isTrue);
|
||||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pump();
|
||||
expect(focusNode3.hasPrimaryFocus, isTrue);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7256,4 +7256,92 @@ void main() {
|
|||
// visible.
|
||||
expect(scrollController.offset, 44.0);
|
||||
});
|
||||
testWidgets("Arrow keys don't move input focus", (WidgetTester tester) async {
|
||||
final TextEditingController controller1 = TextEditingController();
|
||||
final TextEditingController controller2 = TextEditingController();
|
||||
final TextEditingController controller3 = TextEditingController();
|
||||
final TextEditingController controller4 = TextEditingController();
|
||||
final TextEditingController controller5 = TextEditingController();
|
||||
final FocusNode focusNode1 = FocusNode(debugLabel: 'Field 1');
|
||||
final FocusNode focusNode2 = FocusNode(debugLabel: 'Field 2');
|
||||
final FocusNode focusNode3 = FocusNode(debugLabel: 'Field 3');
|
||||
final FocusNode focusNode4 = FocusNode(debugLabel: 'Field 4');
|
||||
final FocusNode focusNode5 = FocusNode(debugLabel: 'Field 5');
|
||||
|
||||
// Lay out text fields in a "+" formation, and focus the center one.
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
theme: ThemeData(),
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 100.0,
|
||||
child: TextField(
|
||||
controller: controller1,
|
||||
focusNode: focusNode1,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 100.0,
|
||||
child: TextField(
|
||||
controller: controller2,
|
||||
focusNode: focusNode2,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 100.0,
|
||||
child: TextField(
|
||||
controller: controller3,
|
||||
focusNode: focusNode3,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 100.0,
|
||||
child: TextField(
|
||||
controller: controller4,
|
||||
focusNode: focusNode4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
width: 100.0,
|
||||
child: TextField(
|
||||
controller: controller5,
|
||||
focusNode: focusNode5,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),);
|
||||
|
||||
focusNode3.requestFocus();
|
||||
await tester.pump();
|
||||
expect(focusNode3.hasPrimaryFocus, isTrue);
|
||||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
|
||||
await tester.pump();
|
||||
expect(focusNode3.hasPrimaryFocus, isTrue);
|
||||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
|
||||
await tester.pump();
|
||||
expect(focusNode3.hasPrimaryFocus, isTrue);
|
||||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft);
|
||||
await tester.pump();
|
||||
expect(focusNode3.hasPrimaryFocus, isTrue);
|
||||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pump();
|
||||
expect(focusNode3.hasPrimaryFocus, isTrue);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue