Fix web text field shortcuts (#79056)
This commit is contained in:
parent
dfc134dd9a
commit
971881c8c5
@ -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<LogicalKeySet, Intent> _webScrollShortcutOverrides = <LogicalKeySet, Intent>{
|
||||
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<CupertinoTextField> 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<CupertinoTextField> with Restoratio
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (kIsWeb) {
|
||||
return Shortcuts(
|
||||
shortcuts: _webScrollShortcutOverrides,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
@ -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<LogicalKeySet, Intent> _webScrollShortcutOverrides = <LogicalKeySet, Intent>{
|
||||
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<TextField> 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<TextField> with RestorationMixin implements
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (kIsWeb) {
|
||||
return Shortcuts(
|
||||
shortcuts: _webScrollShortcutOverrides,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class _DoNothingAndStopPropagationTextAction extends TextEditingAction<DoNothing
|
||||
_DoNothingAndStopPropagationTextAction();
|
||||
|
||||
@override
|
||||
bool consumesKey(Intent intent) => true;
|
||||
bool consumesKey(Intent intent) => false;
|
||||
|
||||
@override
|
||||
void invoke(DoNothingAndStopPropagationTextIntent intent, [BuildContext? context]) {}
|
||||
|
@ -61,8 +61,7 @@ abstract class TextEditingAction<T extends Intent> extends ContextAction<T> {
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
@ -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, Intent>{
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowLeft): TestLeftIntent(),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowRight): TestRightIntent(),
|
||||
},
|
||||
child: Shortcuts(
|
||||
shortcuts: <LogicalKeySet, Intent>{
|
||||
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: <Type, Action<Intent>>{
|
||||
TestLeftIntent: CallbackAction<TestLeftIntent>(onInvoke: (Intent intent) {
|
||||
leftCalled = true;
|
||||
}),
|
||||
TestRightIntent: CallbackAction<TestRightIntent>(onInvoke: (Intent intent) {
|
||||
rightCalled = true;
|
||||
}),
|
||||
},
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
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);
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user