Add enableInteractiveSelection to CupertinoTextField and test it (#32823)

Adds a field that already exists in Material's TextField.
This commit is contained in:
Justin McCandless 2019-05-17 07:56:11 -07:00 committed by GitHub
parent 4aaeb4e11f
commit 95eed87640
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 135 additions and 17 deletions

View File

@ -186,6 +186,7 @@ class CupertinoTextField extends StatefulWidget {
this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0),
this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection,
this.scrollPhysics,
}) : assert(textAlign != null),
assert(autofocus != null),
@ -423,12 +424,20 @@ class CupertinoTextField extends StatefulWidget {
/// {@macro flutter.widgets.editableText.scrollPadding}
final EdgeInsets scrollPadding;
/// {@macro flutter.widgets.editableText.enableInteractiveSelection}
final bool enableInteractiveSelection;
/// {@macro flutter.widgets.scrollable.dragStartBehavior}
final DragStartBehavior dragStartBehavior;
/// {@macro flutter.widgets.edtiableText.scrollPhysics}
final ScrollPhysics scrollPhysics;
/// {@macro flutter.rendering.editable.selectionEnabled}
bool get selectionEnabled {
return enableInteractiveSelection ?? !obscureText;
}
@override
_CupertinoTextFieldState createState() => _CupertinoTextFieldState();
@ -456,6 +465,7 @@ class CupertinoTextField extends StatefulWidget {
properties.add(IntProperty('maxLength', maxLength, defaultValue: null));
properties.add(FlagProperty('maxLengthEnforced', value: maxLengthEnforced, ifTrue: 'max length enforced'));
properties.add(DiagnosticsProperty<Color>('cursorColor', cursorColor, defaultValue: null));
properties.add(FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled'));
properties.add(DiagnosticsProperty<ScrollPhysics>('scrollPhysics', scrollPhysics, defaultValue: null));
}
}
@ -530,11 +540,13 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
}
void _handleForcePressStarted(ForcePressDetails details) {
if (widget.selectionEnabled) {
_renderEditable.selectWordsInRange(
from: details.globalPosition,
cause: SelectionChangedCause.forcePress,
);
}
}
void _handleForcePressEnded(ForcePressDetails details) {
_renderEditable.selectWordsInRange(
@ -546,23 +558,29 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
}
void _handleSingleTapUp(TapUpDetails details) {
if (widget.selectionEnabled) {
_renderEditable.selectWordEdge(cause: SelectionChangedCause.tap);
}
_requestKeyboard();
}
void _handleSingleLongTapStart(LongPressStartDetails details) {
if (widget.selectionEnabled) {
_renderEditable.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.longPress,
);
}
}
void _handleSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) {
if (widget.selectionEnabled) {
_renderEditable.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.longPress,
);
}
}
void _handleSingleLongTapEnd(LongPressEndDetails details) {
if (_shouldShowSelectionToolbar)
@ -570,10 +588,12 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
}
void _handleDoubleTapDown(TapDownDetails details) {
if (widget.selectionEnabled) {
_renderEditable.selectWord(cause: SelectionChangedCause.tap);
if (_shouldShowSelectionToolbar)
_editableText.showToolbar();
}
}
bool _shouldShowSelectionHandles(SelectionChangedCause cause) {
// When the text field is activated by something that doesn't trigger the
@ -782,7 +802,8 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
minLines: widget.minLines,
expands: widget.expands,
selectionColor: _kSelectionHighlightColor,
selectionControls: cupertinoTextSelectionControls,
selectionControls: widget.selectionEnabled
? cupertinoTextSelectionControls : null,
onChanged: widget.onChanged,
onSelectionChanged: _handleSelectionChanged,
onEditingComplete: widget.onEditingComplete,
@ -800,6 +821,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
keyboardAppearance: keyboardAppearance,
dragStartBehavior: widget.dragStartBehavior,
scrollPhysics: widget.scrollPhysics,
enableInteractiveSelection: widget.enableInteractiveSelection,
),
),
);

View File

@ -1399,6 +1399,102 @@ void main() {
},
);
testWidgets(
'An obscured CupertinoTextField is not selectable by default',
(WidgetTester tester) async {
final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure',
);
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoTextField(
controller: controller,
obscureText: true,
),
),
),
);
final Offset textfieldStart = tester.getTopLeft(find.byType(CupertinoTextField));
await tester.tapAt(textfieldStart + const Offset(150.0, 5.0));
await tester.pump(const Duration(milliseconds: 50));
final TestGesture gesture =
await tester.startGesture(textfieldStart + const Offset(150.0, 5.0));
// Hold the press.
await tester.pump(const Duration(milliseconds: 500));
// Nothing is selected despite the double tap long press gesture.
expect(
controller.selection,
const TextSelection(baseOffset: 35, extentOffset: 35),
);
// The selection menu is not present.
expect(find.byType(CupertinoButton), findsNWidgets(0));
await gesture.up();
await tester.pump();
// Still nothing selected and no selection menu.
expect(
controller.selection,
const TextSelection(baseOffset: 35, extentOffset: 35),
);
expect(find.byType(CupertinoButton), findsNWidgets(0));
},
);
testWidgets(
'An obscured CupertinoTextField is selectable when enabled',
(WidgetTester tester) async {
final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure',
);
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoTextField(
controller: controller,
obscureText: true,
enableInteractiveSelection: true,
),
),
),
);
final Offset textfieldStart = tester.getTopLeft(find.byType(CupertinoTextField));
await tester.tapAt(textfieldStart + const Offset(150.0, 5.0));
await tester.pump(const Duration(milliseconds: 50));
final TestGesture gesture =
await tester.startGesture(textfieldStart + const Offset(150.0, 5.0));
// Hold the press.
await tester.pump(const Duration(milliseconds: 500));
// The obscured text is not broken into words, so only one letter is
// selected at a time.
expect(
controller.selection,
const TextSelection(baseOffset: 9, extentOffset: 10),
);
// Selected text shows 3 toolbar buttons.
expect(find.byType(CupertinoButton), findsNWidgets(3));
await gesture.up();
await tester.pump();
// Still selected.
expect(
controller.selection,
const TextSelection(baseOffset: 9, extentOffset: 10),
);
expect(find.byType(CupertinoButton), findsNWidgets(3));
},
);
testWidgets(
'long press moves cursor to the exact long press position and shows toolbar',
(WidgetTester tester) async {