Unverified Commit e204eb2f authored by Mouad Debbar's avatar Mouad Debbar Committed by GitHub

Space and arrow keys in a text field shouldn't scroll (#74454)

parent 177fd261
...@@ -1201,25 +1201,28 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with Restoratio ...@@ -1201,25 +1201,28 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with Restoratio
), ),
); );
return Semantics( return Shortcuts(
enabled: enabled, shortcuts: scrollShortcutOverrides,
onTap: !enabled || widget.readOnly ? null : () { child: Semantics(
if (!controller.selection.isValid) { enabled: enabled,
controller.selection = TextSelection.collapsed(offset: controller.text.length); onTap: !enabled || widget.readOnly ? null : () {
} if (!controller.selection.isValid) {
_requestKeyboard(); controller.selection = TextSelection.collapsed(offset: controller.text.length);
}, }
child: IgnorePointer( _requestKeyboard();
ignoring: !enabled, },
child: Container( child: IgnorePointer(
decoration: effectiveDecoration, ignoring: !enabled,
child: _selectionGestureDetectorBuilder.buildGestureDetector( child: Container(
behavior: HitTestBehavior.translucent, decoration: effectiveDecoration,
child: Align( child: _selectionGestureDetectorBuilder.buildGestureDetector(
alignment: Alignment(-1.0, _textAlignVertical.y), behavior: HitTestBehavior.translucent,
widthFactor: 1.0, child: Align(
heightFactor: 1.0, alignment: Alignment(-1.0, _textAlignVertical.y),
child: _addTextDependentAttachments(paddedEditable, textStyle, placeholderStyle), widthFactor: 1.0,
heightFactor: 1.0,
child: _addTextDependentAttachments(paddedEditable, textStyle, placeholderStyle),
),
), ),
), ),
), ),
......
...@@ -1290,29 +1290,32 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements ...@@ -1290,29 +1290,32 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
semanticsMaxValueLength = null; semanticsMaxValueLength = null;
} }
return MouseRegion( return Shortcuts(
cursor: effectiveMouseCursor, shortcuts: scrollShortcutOverrides,
onEnter: (PointerEnterEvent event) => _handleHover(true), child: MouseRegion(
onExit: (PointerExitEvent event) => _handleHover(false), cursor: effectiveMouseCursor,
child: IgnorePointer( onEnter: (PointerEnterEvent event) => _handleHover(true),
ignoring: !_isEnabled, onExit: (PointerExitEvent event) => _handleHover(false),
child: AnimatedBuilder( child: IgnorePointer(
animation: controller, // changes the _currentLength ignoring: !_isEnabled,
builder: (BuildContext context, Widget? child) { child: AnimatedBuilder(
return Semantics( animation: controller, // changes the _currentLength
maxValueLength: semanticsMaxValueLength, builder: (BuildContext context, Widget? child) {
currentValueLength: _currentLength, return Semantics(
onTap: widget.readOnly ? null : () { maxValueLength: semanticsMaxValueLength,
if (!_effectiveController.selection.isValid) currentValueLength: _currentLength,
_effectiveController.selection = TextSelection.collapsed(offset: _effectiveController.text.length); onTap: widget.readOnly ? null : () {
_requestKeyboard(); if (!_effectiveController.selection.isValid)
}, _effectiveController.selection = TextSelection.collapsed(offset: _effectiveController.text.length);
_requestKeyboard();
},
child: child,
);
},
child: _selectionGestureDetectorBuilder.buildGestureDetector(
behavior: HitTestBehavior.translucent,
child: child, child: child,
); ),
},
child: _selectionGestureDetectorBuilder.buildGestureDetector(
behavior: HitTestBehavior.translucent,
child: child,
), ),
), ),
), ),
......
...@@ -12,6 +12,7 @@ import 'package:flutter/rendering.dart'; ...@@ -12,6 +12,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'actions.dart';
import 'autofill.dart'; import 'autofill.dart';
import 'automatic_keep_alive.dart'; import 'automatic_keep_alive.dart';
import 'basic.dart'; import 'basic.dart';
...@@ -26,6 +27,7 @@ import 'media_query.dart'; ...@@ -26,6 +27,7 @@ import 'media_query.dart';
import 'scroll_controller.dart'; import 'scroll_controller.dart';
import 'scroll_physics.dart'; import 'scroll_physics.dart';
import 'scrollable.dart'; import 'scrollable.dart';
import 'shortcuts.dart';
import 'text.dart'; import 'text.dart';
import 'text_selection.dart'; import 'text_selection.dart';
import 'ticker_provider.dart'; import 'ticker_provider.dart';
...@@ -53,6 +55,19 @@ const Duration _kCursorBlinkWaitForStart = Duration(milliseconds: 150); ...@@ -53,6 +55,19 @@ const Duration _kCursorBlinkWaitForStart = Duration(milliseconds: 150);
// is shown in an obscured text field. // is shown in an obscured text field.
const int _kObscureShowLatestCharCursorTicks = 3; const int _kObscureShowLatestCharCursorTicks = 3;
/// A map used to disable scrolling shortcuts in text fields.
///
/// This is a temporary fix for: https://github.com/flutter/flutter/issues/74191
final Map<LogicalKeySet, Intent> scrollShortcutOverrides = kIsWeb
? <LogicalKeySet, Intent>{
LogicalKeySet(LogicalKeyboardKey.space): DoNothingAndStopPropagationIntent(),
LogicalKeySet(LogicalKeyboardKey.arrowUp): DoNothingAndStopPropagationIntent(),
LogicalKeySet(LogicalKeyboardKey.arrowDown): DoNothingAndStopPropagationIntent(),
LogicalKeySet(LogicalKeyboardKey.arrowLeft): DoNothingAndStopPropagationIntent(),
LogicalKeySet(LogicalKeyboardKey.arrowRight): DoNothingAndStopPropagationIntent(),
}
: <LogicalKeySet, Intent>{};
/// A controller for an editable text field. /// A controller for an editable text field.
/// ///
/// Whenever the user modifies a text field with an associated /// Whenever the user modifies a text field with an associated
......
...@@ -4287,6 +4287,39 @@ void main() { ...@@ -4287,6 +4287,39 @@ void main() {
expect(focusNode3.hasPrimaryFocus, isTrue); expect(focusNode3.hasPrimaryFocus, isTrue);
}); });
testWidgets('Scrolling shortcuts are disabled in text fields', (WidgetTester tester) async {
bool scrollInvoked = false;
await tester.pumpWidget(
CupertinoApp(
home: Actions(
actions: <Type, Action<Intent>>{
ScrollIntent: CallbackAction<ScrollIntent>(onInvoke: (Intent intent) {
scrollInvoked = true;
}),
},
child: ListView(
children: const <Widget>[
Padding(padding: EdgeInsets.symmetric(vertical: 200)),
CupertinoTextField(),
Padding(padding: EdgeInsets.symmetric(vertical: 800)),
],
),
),
),
);
await tester.pump();
expect(scrollInvoked, isFalse);
// Set focus on the text field.
await tester.tapAt(tester.getTopLeft(find.byType(CupertinoTextField)));
await tester.sendKeyEvent(LogicalKeyboardKey.space);
expect(scrollInvoked, isFalse);
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
expect(scrollInvoked, isFalse);
});
testWidgets('Cupertino text field semantics', (WidgetTester tester) async { testWidgets('Cupertino text field semantics', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
......
...@@ -8754,6 +8754,41 @@ void main() { ...@@ -8754,6 +8754,41 @@ void main() {
expect(focusNode3.hasPrimaryFocus, isTrue); expect(focusNode3.hasPrimaryFocus, isTrue);
}); });
testWidgets('Scrolling shortcuts are disabled in text fields', (WidgetTester tester) async {
bool scrollInvoked = false;
await tester.pumpWidget(
MaterialApp(
home: Actions(
actions: <Type, Action<Intent>>{
ScrollIntent: CallbackAction<ScrollIntent>(onInvoke: (Intent intent) {
scrollInvoked = true;
}),
},
child: Material(
child: ListView(
children: const <Widget>[
Padding(padding: EdgeInsets.symmetric(vertical: 200)),
TextField(),
Padding(padding: EdgeInsets.symmetric(vertical: 800)),
],
),
),
),
),
);
await tester.pump();
expect(scrollInvoked, isFalse);
// Set focus on the text field.
await tester.tapAt(tester.getTopLeft(find.byType(TextField)));
await tester.sendKeyEvent(LogicalKeyboardKey.space);
expect(scrollInvoked, isFalse);
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
expect(scrollInvoked, isFalse);
});
testWidgets("A buildCounter that returns null doesn't affect the size of the TextField", (WidgetTester tester) async { testWidgets("A buildCounter that returns null doesn't affect the size of the TextField", (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/44909 // Regression test for https://github.com/flutter/flutter/issues/44909
......
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