Implement Double Tap Handling in TextField and Editable (#21264)
Thanks @NikoYuwono for getting this working! We appreciate your help! * Implement Double Tap Handling in TextField and Editable * Fix test broken by the change and add test for double tap * Fix affected tests * Remove unnecessary new * Fix test
This commit is contained in:
parent
9f0183eaaf
commit
02e87334dd
@ -496,6 +496,11 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
|
||||
_cancelCurrentSplash();
|
||||
}
|
||||
|
||||
void _handleDoubleTap() {
|
||||
_renderEditable.handleDoubleTap();
|
||||
_confirmCurrentSplash();
|
||||
}
|
||||
|
||||
void _handleLongPress() {
|
||||
_renderEditable.handleLongPress();
|
||||
_confirmCurrentSplash();
|
||||
@ -614,6 +619,7 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
|
||||
onTapDown: _handleTapDown,
|
||||
onTap: _handleTap,
|
||||
onTapCancel: _handleTapCancel,
|
||||
onDoubleTap: _handleDoubleTap,
|
||||
onLongPress: _handleLongPress,
|
||||
excludeFromSemantics: true,
|
||||
child: child,
|
||||
|
@ -32,6 +32,10 @@ enum SelectionChangedCause {
|
||||
/// of the cursor) to change.
|
||||
tap,
|
||||
|
||||
/// The user tapped twice in quick succession on the text and that caused
|
||||
/// the selection (or the location of the cursor) to change.
|
||||
doubleTap,
|
||||
|
||||
/// The user long-pressed the text and that caused the selection (or the
|
||||
/// location of the cursor) to change.
|
||||
longPress,
|
||||
@ -168,6 +172,8 @@ class RenderEditable extends RenderBox {
|
||||
_tap = TapGestureRecognizer(debugOwner: this)
|
||||
..onTapDown = _handleTapDown
|
||||
..onTap = _handleTap;
|
||||
_doubleTap = DoubleTapGestureRecognizer(debugOwner: this)
|
||||
..onDoubleTap = _handleDoubleTap;
|
||||
_longPress = LongPressGestureRecognizer(debugOwner: this)
|
||||
..onLongPress = _handleLongPress;
|
||||
}
|
||||
@ -185,7 +191,7 @@ class RenderEditable extends RenderBox {
|
||||
|
||||
/// If true [handleEvent] does nothing and it's assumed that this
|
||||
/// renderer will be notified of input gestures via [handleTapDown],
|
||||
/// [handleTap], and [handleLongPress].
|
||||
/// [handleTap], [handleDoubleTap], and [handleLongPress].
|
||||
///
|
||||
/// The default value of this property is false.
|
||||
bool ignorePointer;
|
||||
@ -1022,6 +1028,7 @@ class RenderEditable extends RenderBox {
|
||||
bool hitTestSelf(Offset position) => true;
|
||||
|
||||
TapGestureRecognizer _tap;
|
||||
DoubleTapGestureRecognizer _doubleTap;
|
||||
LongPressGestureRecognizer _longPress;
|
||||
|
||||
@override
|
||||
@ -1031,6 +1038,7 @@ class RenderEditable extends RenderBox {
|
||||
assert(debugHandleEvent(event, entry));
|
||||
if (event is PointerDownEvent && onSelectionChanged != null) {
|
||||
_tap.addPointer(event);
|
||||
_doubleTap.addPointer(event);
|
||||
_longPress.addPointer(event);
|
||||
}
|
||||
}
|
||||
@ -1070,6 +1078,25 @@ class RenderEditable extends RenderBox {
|
||||
handleTap();
|
||||
}
|
||||
|
||||
/// If [ignorePointer] is false (the default) then this method is called by
|
||||
/// the internal gesture recognizer's [DoubleTapGestureRecognizer.onDoubleTap]
|
||||
/// callback.
|
||||
///
|
||||
/// When [ignorePointer] is true, an ancestor widget must respond to long
|
||||
/// press events by calling this method.
|
||||
void handleDoubleTap() {
|
||||
_layoutText(constraints.maxWidth);
|
||||
assert(_lastTapDownPosition != null);
|
||||
if (onSelectionChanged != null) {
|
||||
final TextPosition position = _textPainter.getPositionForOffset(globalToLocal(_lastTapDownPosition));
|
||||
onSelectionChanged(_selectWordAtOffset(position), this, SelectionChangedCause.doubleTap);
|
||||
}
|
||||
}
|
||||
void _handleDoubleTap() {
|
||||
assert(!ignorePointer);
|
||||
handleDoubleTap();
|
||||
}
|
||||
|
||||
/// If [ignorePointer] is false (the default) then this method is called by
|
||||
/// the internal gesture recognizer's [LongPressRecognizer.onLongPress]
|
||||
/// callback.
|
||||
|
@ -179,6 +179,7 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.tap(find.widgetWithText(TextField, 'TextField 2'));
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
|
||||
expect(
|
||||
focusNodes.indexOf(focusNodes.singleWhere((FocusNode node) => node.hasFocus)),
|
||||
@ -189,6 +190,7 @@ void main() {
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.widgetWithText(TextField, 'TextField 1'));
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
|
||||
expect(
|
||||
focusNodes.indexOf(focusNodes.singleWhere((FocusNode node) => node.hasFocus)),
|
||||
|
@ -164,7 +164,7 @@ void main() {
|
||||
delegate: delegate,
|
||||
));
|
||||
await tester.tap(find.byTooltip('Search'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 300));
|
||||
|
||||
// Showing suggestions
|
||||
expect(find.text('Suggestions'), findsOneWidget);
|
||||
@ -173,14 +173,14 @@ void main() {
|
||||
// Typing query Wow
|
||||
delegate.querysForSuggestions.clear();
|
||||
await tester.enterText(find.byType(TextField), 'Wow');
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 300));
|
||||
|
||||
expect(delegate.query, 'Wow');
|
||||
expect(delegate.querysForSuggestions, <String>['Wow']);
|
||||
expect(delegate.querysForResults, hasLength(0));
|
||||
|
||||
await tester.tap(find.text('Suggestions'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 300));
|
||||
|
||||
// Showing Results
|
||||
expect(find.text('Suggestions'), findsNothing);
|
||||
@ -195,7 +195,7 @@ void main() {
|
||||
|
||||
// Taping search field to go back to suggestions
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 300));
|
||||
|
||||
textField = tester.widget(find.byType(TextField));
|
||||
expect(textField.focusNode.hasFocus, isTrue);
|
||||
@ -206,7 +206,7 @@ void main() {
|
||||
expect(delegate.querysForResults, <String>['Wow']);
|
||||
|
||||
await tester.enterText(find.byType(TextField), 'Foo');
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 300));
|
||||
|
||||
expect(delegate.query, 'Foo');
|
||||
expect(delegate.querysForSuggestions, <String>['Wow', 'Wow', 'Foo']);
|
||||
@ -214,7 +214,7 @@ void main() {
|
||||
|
||||
// Go to results again
|
||||
await tester.tap(find.text('Suggestions'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 300));
|
||||
|
||||
expect(find.text('Suggestions'), findsNothing);
|
||||
expect(find.text('Results'), findsOneWidget);
|
||||
|
@ -6,6 +6,8 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
void main() {
|
||||
const Duration doubleTapTimeout = Duration(milliseconds: 300);
|
||||
|
||||
testWidgets('Request focus shows keyboard', (WidgetTester tester) async {
|
||||
final FocusNode focusNode = FocusNode();
|
||||
|
||||
@ -71,7 +73,7 @@ void main() {
|
||||
expect(tester.testTextInput.isVisible, isFalse);
|
||||
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.idle();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
expect(tester.testTextInput.isVisible, isTrue);
|
||||
|
||||
@ -80,7 +82,7 @@ void main() {
|
||||
expect(tester.testTextInput.isVisible, isFalse);
|
||||
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.idle();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
expect(tester.testTextInput.isVisible, isTrue);
|
||||
|
||||
@ -113,17 +115,17 @@ void main() {
|
||||
builder: (BuildContext context) => const SimpleDialog(title: Text('Dialog')),
|
||||
);
|
||||
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
expect(tester.testTextInput.isVisible, isFalse);
|
||||
|
||||
Navigator.of(tester.element(find.text('Dialog'))).pop();
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
expect(tester.testTextInput.isVisible, isFalse);
|
||||
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.idle();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
expect(tester.testTextInput.isVisible, isTrue);
|
||||
|
||||
@ -224,7 +226,7 @@ void main() {
|
||||
|
||||
expect(tester.testTextInput.isVisible, isFalse);
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.idle();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
expect(tester.testTextInput.isVisible, isTrue);
|
||||
});
|
||||
}
|
||||
|
@ -121,32 +121,32 @@ void main() {
|
||||
cancelCount = 0;
|
||||
|
||||
await tester.tap(find.byKey(textField1));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 300));
|
||||
expect(confirmCount, 1);
|
||||
expect(cancelCount, 0);
|
||||
|
||||
// textField1 already has the focus, no new splash
|
||||
await tester.tap(find.byKey(textField1));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 300));
|
||||
expect(confirmCount, 1);
|
||||
expect(cancelCount, 0);
|
||||
|
||||
// textField2 gets the focus and a splash
|
||||
await tester.tap(find.byKey(textField2));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 300));
|
||||
expect(confirmCount, 2);
|
||||
expect(cancelCount, 0);
|
||||
|
||||
// Tap outside of textField1's editable. It still gets focus and splash.
|
||||
await tester.tapAt(tester.getTopLeft(find.byKey(textField1)));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 300));
|
||||
expect(confirmCount, 3);
|
||||
expect(cancelCount, 0);
|
||||
|
||||
// Tap in the center of textField2's editable. It still gets the focus
|
||||
// and the splash. There is no splash cancel.
|
||||
await tester.tap(find.byKey(textField2));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 300));
|
||||
expect(confirmCount, 4);
|
||||
expect(cancelCount, 0);
|
||||
});
|
||||
|
@ -118,6 +118,7 @@ void main() {
|
||||
final MockClipboard mockClipboard = MockClipboard();
|
||||
SystemChannels.platform.setMockMethodCallHandler(mockClipboard.handleMethodCall);
|
||||
|
||||
const Duration doubleTapTimeout = Duration(milliseconds: 300);
|
||||
const String kThreeLines =
|
||||
'First line of text is '
|
||||
'Second line goes until '
|
||||
@ -398,13 +399,13 @@ void main() {
|
||||
final int tapIndex = testValue.indexOf('e');
|
||||
final Offset ePos = textOffsetToPosition(tester, tapIndex);
|
||||
await tester.tapAt(ePos);
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
expect(controller.selection.baseOffset, tapIndex);
|
||||
expect(controller.selection.extentOffset, tapIndex);
|
||||
});
|
||||
|
||||
testWidgets('Can long press to select', (WidgetTester tester) async {
|
||||
testWidgets('Can long press to select a word', (WidgetTester tester) async {
|
||||
final TextEditingController controller = TextEditingController();
|
||||
|
||||
await tester.pumpWidget(
|
||||
@ -434,6 +435,40 @@ void main() {
|
||||
expect(controller.selection.extentOffset, testValue.indexOf('f')+1);
|
||||
});
|
||||
|
||||
testWidgets('Can double tap to select a word', (WidgetTester tester) async {
|
||||
final TextEditingController controller = TextEditingController();
|
||||
|
||||
await tester.pumpWidget(
|
||||
overlay(
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
const String testValue = 'abc def ghi';
|
||||
await tester.enterText(find.byType(TextField), testValue);
|
||||
expect(controller.value.text, testValue);
|
||||
await skipPastScrollingAnimation(tester);
|
||||
|
||||
expect(controller.selection.isCollapsed, true);
|
||||
|
||||
// Long press the 'e' to select 'def'.
|
||||
final Offset ePos = textOffsetToPosition(tester, testValue.indexOf('e'));
|
||||
final TestGesture firstTapGesture = await tester.startGesture(ePos);
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
await firstTapGesture.up();
|
||||
await tester.pump();
|
||||
final TestGesture secondTapGesture = await tester.startGesture(ePos);
|
||||
await tester.pump();
|
||||
await secondTapGesture.up();
|
||||
await tester.pump();
|
||||
|
||||
// 'def' is selected.
|
||||
expect(controller.selection.baseOffset, testValue.indexOf('d'));
|
||||
expect(controller.selection.extentOffset, testValue.indexOf('f')+1);
|
||||
});
|
||||
|
||||
testWidgets('Can drag handles to change selection', (WidgetTester tester) async {
|
||||
final TextEditingController controller = TextEditingController();
|
||||
|
||||
@ -512,7 +547,7 @@ void main() {
|
||||
|
||||
// Tap the selection handle to bring up the "paste / select all" menu.
|
||||
await tester.tapAt(textOffsetToPosition(tester, testValue.indexOf('e')));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.pump(const Duration(milliseconds: 200)); // skip past the frame where the opacity is zero
|
||||
RenderEditable renderEditable = findRenderEditable(tester);
|
||||
List<TextSelectionPoint> endpoints = globalize(
|
||||
@ -520,7 +555,7 @@ void main() {
|
||||
renderEditable,
|
||||
);
|
||||
await tester.tapAt(endpoints[0].point + const Offset(1.0, 1.0));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.pump(const Duration(milliseconds: 200)); // skip past the frame where the opacity is zero
|
||||
|
||||
// SELECT ALL should select all the text.
|
||||
@ -536,7 +571,7 @@ void main() {
|
||||
|
||||
// Tap again to bring back the menu.
|
||||
await tester.tapAt(textOffsetToPosition(tester, testValue.indexOf('e')));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.pump(const Duration(milliseconds: 200)); // skip past the frame where the opacity is zero
|
||||
renderEditable = findRenderEditable(tester);
|
||||
endpoints = globalize(
|
||||
@ -544,7 +579,7 @@ void main() {
|
||||
renderEditable,
|
||||
);
|
||||
await tester.tapAt(endpoints[0].point + const Offset(1.0, 1.0));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.pump(const Duration(milliseconds: 200)); // skip past the frame where the opacity is zero
|
||||
|
||||
// PASTE right before the 'e'.
|
||||
@ -570,7 +605,7 @@ void main() {
|
||||
|
||||
// Tap the selection handle to bring up the "paste / select all" menu.
|
||||
await tester.tapAt(textOffsetToPosition(tester, testValue.indexOf('e')));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.pump(const Duration(milliseconds: 200)); // skip past the frame where the opacity is zero
|
||||
final RenderEditable renderEditable = findRenderEditable(tester);
|
||||
final List<TextSelectionPoint> endpoints = globalize(
|
||||
@ -578,7 +613,7 @@ void main() {
|
||||
renderEditable,
|
||||
);
|
||||
await tester.tapAt(endpoints[0].point + const Offset(1.0, 1.0));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
// Toolbar should fade in. Starting at 0% opacity.
|
||||
final Element target = tester.element(find.text('SELECT ALL'));
|
||||
@ -1062,7 +1097,7 @@ void main() {
|
||||
|
||||
// Focus the Input. The prefix should still display.
|
||||
await tester.tap(find.byKey(secondKey));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
expect(find.text('Prefix'), findsOneWidget);
|
||||
expect(find.text('Suffix'), findsOneWidget);
|
||||
@ -1114,7 +1149,7 @@ void main() {
|
||||
expect(getOpacity(tester, find.text('Hint')), 1.0);
|
||||
|
||||
await tester.tap(find.byKey(secondKey));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 300));
|
||||
|
||||
// Focus the Input. The hint, prefix, and suffix should appear
|
||||
expect(getOpacity(tester, find.text('Prefix')), 1.0);
|
||||
@ -1180,7 +1215,7 @@ void main() {
|
||||
|
||||
// Focus the input. The label, prefix, and suffix should appear.
|
||||
await tester.tap(find.byKey(secondKey));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 300));
|
||||
|
||||
expect(getOpacity(tester, find.text('Prefix')), 1.0);
|
||||
expect(getOpacity(tester, find.text('Suffix')), 1.0);
|
||||
@ -1229,7 +1264,7 @@ void main() {
|
||||
// Focus the Input. The label should start animating upwards.
|
||||
await tester.tap(find.byKey(secondKey));
|
||||
await tester.idle();
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
|
||||
Offset newPos = tester.getTopLeft(find.text('Second'));
|
||||
@ -1364,7 +1399,7 @@ void main() {
|
||||
|
||||
// Initial state with null controller.
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
expect(tester.testTextInput.editingState['text'], isEmpty);
|
||||
|
||||
// Update the controller from null to controller1.
|
||||
@ -1502,7 +1537,7 @@ void main() {
|
||||
await skipPastScrollingAnimation(tester);
|
||||
|
||||
await tester.tapAt(textOffsetToPosition(tester, '123'.indexOf('2')));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.pump(const Duration(milliseconds: 200)); // skip past the frame where the opacity is zero
|
||||
final RenderEditable renderEditable = findRenderEditable(tester);
|
||||
final List<TextSelectionPoint> endpoints = globalize(
|
||||
@ -1510,7 +1545,7 @@ void main() {
|
||||
renderEditable,
|
||||
);
|
||||
await tester.tapAt(endpoints[0].point + const Offset(1.0, 1.0));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.pump(const Duration(milliseconds: 200)); // skip past the frame where the opacity is zero
|
||||
|
||||
Clipboard.setData(const ClipboardData(text: '一4二\n5三6'));
|
||||
@ -1606,7 +1641,7 @@ void main() {
|
||||
expect(controller1.selection, isNot(equals(TextRange.empty)));
|
||||
|
||||
await tester.tap(find.byKey(key2));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
expect(controller1.selection, equals(TextRange.empty));
|
||||
});
|
||||
|
||||
@ -1823,7 +1858,7 @@ void main() {
|
||||
|
||||
await tester.idle();
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(doubleTapTimeout);
|
||||
|
||||
sendKeyEventWithCode(22, true, true, false); // RIGHT_ARROW keydown, SHIFT_ON
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 1);
|
||||
@ -1836,7 +1871,7 @@ void main() {
|
||||
|
||||
await tester.idle();
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(doubleTapTimeout);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(22, true, true, true); // RIGHT_ARROW keydown SHIFT_ON, CONTROL_ON
|
||||
|
||||
@ -1852,7 +1887,7 @@ void main() {
|
||||
|
||||
await tester.idle();
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(doubleTapTimeout);
|
||||
|
||||
sendKeyEventWithCode(20, true, true, false); // DOWN_ARROW keydown
|
||||
await tester.pumpAndSettle();
|
||||
@ -1875,7 +1910,7 @@ void main() {
|
||||
|
||||
await tester.idle();
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(doubleTapTimeout);
|
||||
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(22, true, false, false); // RIGHT_ARROW keydown
|
||||
@ -1962,7 +1997,7 @@ void main() {
|
||||
|
||||
await tester.idle();
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(doubleTapTimeout);
|
||||
|
||||
// Select the first 5 characters
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
@ -2032,7 +2067,7 @@ void main() {
|
||||
|
||||
await tester.idle();
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(doubleTapTimeout);
|
||||
|
||||
// Select the first 5 characters
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
@ -2095,7 +2130,7 @@ void main() {
|
||||
|
||||
await tester.idle();
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(doubleTapTimeout);
|
||||
|
||||
// Select All
|
||||
sendKeyEventWithCode(_kAKeyCode, true, false, true); // keydown control A
|
||||
@ -2141,7 +2176,7 @@ void main() {
|
||||
|
||||
await tester.idle();
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(doubleTapTimeout);
|
||||
|
||||
// Delete
|
||||
for (int i = 0; i < 6; i += 1) {
|
||||
@ -2211,7 +2246,7 @@ void main() {
|
||||
|
||||
await tester.idle();
|
||||
await tester.tap(find.byType(TextField).first);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(doubleTapTimeout);
|
||||
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(22, true, true, false); // RIGHT_ARROW keydown
|
||||
@ -2298,7 +2333,7 @@ void main() {
|
||||
const String testValue = 'a big house';
|
||||
await tester.enterText(find.byType(TextField).first, testValue);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(doubleTapTimeout);
|
||||
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(22, true, true, false); // RIGHT_ARROW keydown
|
||||
@ -2313,7 +2348,7 @@ void main() {
|
||||
|
||||
await tester.enterText(find.byType(TextField).last, testValue);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(doubleTapTimeout);
|
||||
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(22, true, true, false); // RIGHT_ARROW keydown
|
||||
@ -2343,7 +2378,7 @@ void main() {
|
||||
|
||||
// Tap the selection handle to bring up the "paste / select all" menu.
|
||||
await tester.tapAt(textOffsetToPosition(tester, 0));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.pump(const Duration(milliseconds: 200)); // skip past the frame where the opacity is
|
||||
|
||||
// Confirm that the selection was updated.
|
||||
@ -2450,7 +2485,7 @@ void main() {
|
||||
), ignoreTransform: true, ignoreRect: true));
|
||||
|
||||
await tester.tap(find.byKey(key));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
expect(semantics, hasSemantics(TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
@ -2563,7 +2598,7 @@ void main() {
|
||||
|
||||
// Focus the text field
|
||||
await tester.tap(find.byKey(key));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
expect(semantics, hasSemantics(TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
@ -2637,7 +2672,7 @@ void main() {
|
||||
|
||||
// Focus the text field
|
||||
await tester.tap(find.byKey(key));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
const int inputFieldId = 1;
|
||||
|
||||
@ -2869,7 +2904,7 @@ void main() {
|
||||
), ignoreTransform: true, ignoreRect: true));
|
||||
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
expect(semantics, hasSemantics(TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
|
@ -20,6 +20,7 @@ void main() {
|
||||
final FocusScopeNode focusScopeNode = FocusScopeNode();
|
||||
const TextStyle textStyle = TextStyle();
|
||||
const Color cursorColor = Color.fromARGB(0xFF, 0xFF, 0x00, 0x00);
|
||||
const Duration doubleTapTimeout = Duration(milliseconds: 300);
|
||||
|
||||
setUp(() {
|
||||
debugResetSemanticsIdCounter();
|
||||
@ -53,6 +54,7 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.tap(find.byType(EditableText));
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.showKeyboard(find.byType(EditableText));
|
||||
controller.text = 'test';
|
||||
await tester.idle();
|
||||
@ -119,6 +121,7 @@ void main() {
|
||||
),
|
||||
);
|
||||
await tester.tap(find.byType(EditableText));
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.showKeyboard(find.byType(EditableText));
|
||||
controller.text = 'test';
|
||||
await tester.idle();
|
||||
@ -282,6 +285,7 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.tap(find.byType(EditableText));
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.showKeyboard(find.byType(EditableText));
|
||||
controller.text = 'test';
|
||||
await tester.idle();
|
||||
@ -311,6 +315,7 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.tap(find.byType(EditableText));
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.showKeyboard(find.byType(EditableText));
|
||||
controller.text = 'test';
|
||||
await tester.idle();
|
||||
@ -341,6 +346,7 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.tap(find.byType(EditableText));
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.showKeyboard(find.byType(EditableText));
|
||||
controller.text = 'test';
|
||||
await tester.idle();
|
||||
@ -373,6 +379,7 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.tap(find.byType(EditableText));
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.showKeyboard(find.byType(EditableText));
|
||||
controller.text = 'test';
|
||||
await tester.idle();
|
||||
@ -403,6 +410,7 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.tap(find.byType(EditableText));
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.showKeyboard(find.byType(EditableText));
|
||||
controller.text = 'test';
|
||||
await tester.idle();
|
||||
@ -433,6 +441,7 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.tap(find.byType(EditableText));
|
||||
await tester.pump(doubleTapTimeout);
|
||||
await tester.showKeyboard(find.byType(EditableText));
|
||||
controller.text = 'test';
|
||||
await tester.idle();
|
||||
@ -608,7 +617,7 @@ void main() {
|
||||
// Select EditableText to give it focus.
|
||||
final Finder textFinder = find.byKey(editableTextKey);
|
||||
await tester.tap(textFinder);
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
assert(focusNode.hasFocus);
|
||||
|
||||
@ -645,7 +654,7 @@ void main() {
|
||||
// Select EditableText to give it focus.
|
||||
final Finder textFinder = find.byKey(editableTextKey);
|
||||
await tester.tap(textFinder);
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
assert(focusNode.hasFocus);
|
||||
|
||||
@ -689,7 +698,7 @@ void main() {
|
||||
// Select EditableText to give it focus.
|
||||
final Finder textFinder = find.byKey(editableTextKey);
|
||||
await tester.tap(textFinder);
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
assert(focusNode.hasFocus);
|
||||
|
||||
@ -733,7 +742,7 @@ void main() {
|
||||
// Select EditableText to give it focus.
|
||||
final Finder textFinder = find.byKey(editableTextKey);
|
||||
await tester.tap(textFinder);
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
assert(focusNode.hasFocus);
|
||||
|
||||
@ -838,7 +847,7 @@ void main() {
|
||||
|
||||
await tester.tap(find.byType(EditableText));
|
||||
await tester.idle();
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
expect(
|
||||
semantics,
|
||||
@ -1479,7 +1488,7 @@ void main() {
|
||||
|
||||
await _buildApp(controls, tester);
|
||||
await tester.tap(find.byType(EditableText));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
expect(
|
||||
semantics,
|
||||
@ -1565,7 +1574,7 @@ void main() {
|
||||
when(controls.canPaste(any)).thenReturn(true);
|
||||
await _buildApp(controls, tester);
|
||||
await tester.tap(find.byType(EditableText));
|
||||
await tester.pump();
|
||||
await tester.pump(doubleTapTimeout);
|
||||
|
||||
final SemanticsOwner owner = tester.binding.pipelineOwner.semanticsOwner;
|
||||
const int expectedNodeId = 4;
|
||||
|
Loading…
x
Reference in New Issue
Block a user