diff --git a/packages/flutter/lib/src/cupertino/text_field.dart b/packages/flutter/lib/src/cupertino/text_field.dart index 0bb33fca4c..9b58ac40c9 100644 --- a/packages/flutter/lib/src/cupertino/text_field.dart +++ b/packages/flutter/lib/src/cupertino/text_field.dart @@ -4,7 +4,7 @@ import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle; -import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb; +import 'package:flutter/foundation.dart' show defaultTargetPlatform; import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; @@ -18,16 +18,6 @@ import 'theme.dart'; export 'package:flutter/services.dart' show TextInputType, TextInputAction, TextCapitalization, SmartQuotesType, SmartDashesType; -// This is a temporary fix for: https://github.com/flutter/flutter/issues/79012 -/// A map used to disable scrolling shortcuts in text fields. -final Map _webScrollShortcutOverrides = { - LogicalKeySet(LogicalKeyboardKey.space): DoNothingAndStopPropagationIntent(), - LogicalKeySet(LogicalKeyboardKey.arrowUp): DoNothingAndStopPropagationIntent(), - LogicalKeySet(LogicalKeyboardKey.arrowDown): DoNothingAndStopPropagationIntent(), - LogicalKeySet(LogicalKeyboardKey.arrowLeft): DoNothingAndStopPropagationIntent(), - LogicalKeySet(LogicalKeyboardKey.arrowRight): DoNothingAndStopPropagationIntent(), -}; - const TextStyle _kDefaultPlaceholderStyle = TextStyle( fontWeight: FontWeight.w400, color: CupertinoColors.placeholderText, @@ -1222,7 +1212,7 @@ class _CupertinoTextFieldState extends State with Restoratio ), ); - final Widget child = Semantics( + return Semantics( enabled: enabled, onTap: !enabled || widget.readOnly ? null : () { if (!controller.selection.isValid) { @@ -1248,13 +1238,5 @@ class _CupertinoTextFieldState extends State with Restoratio ), ), ); - - if (kIsWeb) { - return Shortcuts( - shortcuts: _webScrollShortcutOverrides, - child: child, - ); - } - return child; } } diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart index 7f482ff0ae..8b908e8f17 100644 --- a/packages/flutter/lib/src/material/text_field.dart +++ b/packages/flutter/lib/src/material/text_field.dart @@ -24,16 +24,6 @@ import 'theme.dart'; export 'package:flutter/services.dart' show TextInputType, TextInputAction, TextCapitalization, SmartQuotesType, SmartDashesType; -// This is a temporary fix for: https://github.com/flutter/flutter/issues/79012 -/// A map used to disable scrolling shortcuts in text fields. -final Map _webScrollShortcutOverrides = { - LogicalKeySet(LogicalKeyboardKey.space): DoNothingAndStopPropagationIntent(), - LogicalKeySet(LogicalKeyboardKey.arrowUp): DoNothingAndStopPropagationIntent(), - LogicalKeySet(LogicalKeyboardKey.arrowDown): DoNothingAndStopPropagationIntent(), - LogicalKeySet(LogicalKeyboardKey.arrowLeft): DoNothingAndStopPropagationIntent(), - LogicalKeySet(LogicalKeyboardKey.arrowRight): DoNothingAndStopPropagationIntent(), -}; - /// Signature for the [TextField.buildCounter] callback. typedef InputCounterWidgetBuilder = Widget? Function( /// The build context for the TextField. @@ -1311,7 +1301,7 @@ class _TextFieldState extends State with RestorationMixin implements semanticsMaxValueLength = null; } - child = MouseRegion( + return MouseRegion( cursor: effectiveMouseCursor, onEnter: (PointerEnterEvent event) => _handleHover(true), onExit: (PointerExitEvent event) => _handleHover(false), @@ -1339,13 +1329,5 @@ class _TextFieldState extends State with RestorationMixin implements ), ), ); - - if (kIsWeb) { - return Shortcuts( - shortcuts: _webScrollShortcutOverrides, - child: child, - ); - } - return child; } } diff --git a/packages/flutter/lib/src/widgets/default_text_editing_actions.dart b/packages/flutter/lib/src/widgets/default_text_editing_actions.dart index c4bb2a07df..aef411d846 100644 --- a/packages/flutter/lib/src/widgets/default_text_editing_actions.dart +++ b/packages/flutter/lib/src/widgets/default_text_editing_actions.dart @@ -72,7 +72,7 @@ class _DoNothingAndStopPropagationTextAction extends TextEditingAction true; + bool consumesKey(Intent intent) => false; @override void invoke(DoNothingAndStopPropagationTextIntent intent, [BuildContext? context]) {} diff --git a/packages/flutter/lib/src/widgets/text_editing_action.dart b/packages/flutter/lib/src/widgets/text_editing_action.dart index 971559ff07..02732e3142 100644 --- a/packages/flutter/lib/src/widgets/text_editing_action.dart +++ b/packages/flutter/lib/src/widgets/text_editing_action.dart @@ -61,8 +61,7 @@ abstract class TextEditingAction extends ContextAction { @override bool isEnabled(T intent) { - // The Action is disabled if there is no focused TextEditingActionTarget, or - // if the platform is web, because web lets the browser handle text editing. - return !kIsWeb && textEditingActionTarget != null; + // The Action is disabled if there is no focused TextEditingActionTarget. + return textEditingActionTarget != null; } } diff --git a/packages/flutter/test/widgets/default_text_editing_actions_test.dart b/packages/flutter/test/widgets/default_text_editing_actions_test.dart new file mode 100644 index 0000000000..c01ec8da0c --- /dev/null +++ b/packages/flutter/test/widgets/default_text_editing_actions_test.dart @@ -0,0 +1,116 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/foundation.dart'; + +class TestLeftIntent extends Intent {} +class TestRightIntent extends Intent {} + +void main() { + testWidgets('DoNothingAndStopPropagationTextIntent', (WidgetTester tester) async { + bool leftCalled = false; + bool rightCalled = false; + final TextEditingController controller = TextEditingController( + text: 'blah1 blah2', + ); + final FocusNode focusNodeTarget = FocusNode(); + final FocusNode focusNodeNonTarget = FocusNode(); + + await tester.pumpWidget(MaterialApp( + theme: ThemeData(), + home: Scaffold( + body: Builder( + builder: (BuildContext context) { + return Shortcuts( + shortcuts: { + LogicalKeySet(LogicalKeyboardKey.arrowLeft): TestLeftIntent(), + LogicalKeySet(LogicalKeyboardKey.arrowRight): TestRightIntent(), + }, + child: Shortcuts( + shortcuts: { + LogicalKeySet(LogicalKeyboardKey.arrowRight): const DoNothingAndStopPropagationTextIntent(), + }, + child: Actions( + // These Actions intercept default Intents, set a flag that they + // were called, and then call through to the default Action. + actions: >{ + TestLeftIntent: CallbackAction(onInvoke: (Intent intent) { + leftCalled = true; + }), + TestRightIntent: CallbackAction(onInvoke: (Intent intent) { + rightCalled = true; + }), + }, + child: Center( + child: Column( + children: [ + EditableText( + controller: controller, + focusNode: focusNodeTarget, + style: Typography.material2018(platform: TargetPlatform.android).black.subtitle1!, + cursorColor: Colors.blue, + backgroundCursorColor: Colors.grey, + ), + Focus( + focusNode: focusNodeNonTarget, + child: const Text('focusable'), + ), + ], + ), + ), + ), + ), + ); + }, + ), + ), + )); + + // Focus on the EditableText, which is a TextEditingActionTarget. + focusNodeTarget.requestFocus(); + await tester.pump(); + expect(focusNodeTarget.hasFocus, isTrue); + expect(focusNodeNonTarget.hasFocus, isFalse); + expect(controller.selection.isCollapsed, isTrue); + expect(controller.selection.baseOffset, 11); + + // The left arrow key's Action is called. + await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); + await tester.pump(); + expect(leftCalled, isTrue); + expect(rightCalled, isFalse); + leftCalled = false; + + // The right arrow key is blocked by DoNothingAndStopPropagationTextIntent. + await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); + await tester.pump(); + expect(rightCalled, isFalse); + expect(leftCalled, isFalse); + + // Focus on the other node, which is not a TextEditingActionTarget. + focusNodeNonTarget.requestFocus(); + await tester.pump(); + expect(focusNodeTarget.hasFocus, isFalse); + expect(focusNodeNonTarget.hasFocus, isTrue); + + // The left arrow key's Action is called as normal. + await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); + await tester.pump(); + expect(leftCalled, isTrue); + expect(rightCalled, isFalse); + leftCalled = false; + + // The right arrow key's Action is also called. That's because + // DoNothingAndStopPropagationTextIntent only applies if a + // TextEditingActionTarget is currently focused. + await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); + await tester.pump(); + expect(leftCalled, isFalse); + expect(rightCalled, isTrue); + }); +}