| // 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/material.dart'; |
| import 'package:flutter/services.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| class TestLeftIntent extends Intent { |
| const TestLeftIntent(); |
| } |
| class TestRightIntent extends Intent { |
| const TestRightIntent(); |
| } |
| |
| 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: const <ShortcutActivator, Intent>{ |
| SingleActivator(LogicalKeyboardKey.arrowLeft): TestLeftIntent(), |
| SingleActivator(LogicalKeyboardKey.arrowRight): TestRightIntent(), |
| }, |
| child: Shortcuts( |
| shortcuts: const <ShortcutActivator, Intent>{ |
| SingleActivator(LogicalKeyboardKey.arrowRight): 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().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); |
| }, variant: KeySimulatorTransitModeVariant.all()); |
| } |