blob: 8718d710ce0ed923523add6747deb0eaea6070ca [file] [log] [blame]
// 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(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);
}, variant: KeySimulatorTransitModeVariant.all());
}