Unverified Commit 52c715fe authored by Jaime Blasco's avatar Jaime Blasco Committed by GitHub

Add textSelectionControls to TextField etc. (#66785)

Enables custom text selection menus by allowing selectionControls to be passed to TextField et. al.
parent 16029c38
...@@ -270,6 +270,7 @@ class CupertinoTextField extends StatefulWidget { ...@@ -270,6 +270,7 @@ class CupertinoTextField extends StatefulWidget {
this.scrollPadding = const EdgeInsets.all(20.0), this.scrollPadding = const EdgeInsets.all(20.0),
this.dragStartBehavior = DragStartBehavior.start, this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection = true, this.enableInteractiveSelection = true,
this.selectionControls,
this.onTap, this.onTap,
this.scrollController, this.scrollController,
this.scrollPhysics, this.scrollPhysics,
...@@ -559,6 +560,9 @@ class CupertinoTextField extends StatefulWidget { ...@@ -559,6 +560,9 @@ class CupertinoTextField extends StatefulWidget {
/// {@macro flutter.widgets.editableText.enableInteractiveSelection} /// {@macro flutter.widgets.editableText.enableInteractiveSelection}
final bool enableInteractiveSelection; final bool enableInteractiveSelection;
/// {@macro flutter.widgets.editableText.selectionControls}
final TextSelectionControls? selectionControls;
/// {@macro flutter.widgets.scrollable.dragStartBehavior} /// {@macro flutter.widgets.scrollable.dragStartBehavior}
final DragStartBehavior dragStartBehavior; final DragStartBehavior dragStartBehavior;
...@@ -615,6 +619,7 @@ class CupertinoTextField extends StatefulWidget { ...@@ -615,6 +619,7 @@ class CupertinoTextField extends StatefulWidget {
properties.add(DiagnosticsProperty<Radius>('cursorRadius', cursorRadius, defaultValue: null)); properties.add(DiagnosticsProperty<Radius>('cursorRadius', cursorRadius, defaultValue: null));
properties.add(createCupertinoColorProperty('cursorColor', cursorColor, defaultValue: null)); properties.add(createCupertinoColorProperty('cursorColor', cursorColor, defaultValue: null));
properties.add(FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled')); properties.add(FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled'));
properties.add(DiagnosticsProperty<TextSelectionControls>('selectionControls', selectionControls, defaultValue: null));
properties.add(DiagnosticsProperty<ScrollController>('scrollController', scrollController, defaultValue: null)); properties.add(DiagnosticsProperty<ScrollController>('scrollController', scrollController, defaultValue: null));
properties.add(DiagnosticsProperty<ScrollPhysics>('scrollPhysics', scrollPhysics, defaultValue: null)); properties.add(DiagnosticsProperty<ScrollPhysics>('scrollPhysics', scrollPhysics, defaultValue: null));
properties.add(EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: TextAlign.start)); properties.add(EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: TextAlign.start));
...@@ -874,6 +879,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with Restoratio ...@@ -874,6 +879,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with Restoratio
assert(debugCheckHasDirectionality(context)); assert(debugCheckHasDirectionality(context));
final TextEditingController controller = _effectiveController; final TextEditingController controller = _effectiveController;
final List<TextInputFormatter> formatters = widget.inputFormatters ?? <TextInputFormatter>[]; final List<TextInputFormatter> formatters = widget.inputFormatters ?? <TextInputFormatter>[];
final TextSelectionControls textSelectionControls = widget.selectionControls ?? cupertinoTextSelectionControls;
final bool enabled = widget.enabled ?? true; final bool enabled = widget.enabled ?? true;
final Offset cursorOffset = Offset(_iOSHorizontalCursorOffsetPixels / MediaQuery.of(context)!.devicePixelRatio, 0); final Offset cursorOffset = Offset(_iOSHorizontalCursorOffsetPixels / MediaQuery.of(context)!.devicePixelRatio, 0);
if (widget.maxLength != null && widget.maxLengthEnforced) { if (widget.maxLength != null && widget.maxLengthEnforced) {
...@@ -957,7 +963,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with Restoratio ...@@ -957,7 +963,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with Restoratio
expands: widget.expands, expands: widget.expands,
selectionColor: selectionColor, selectionColor: selectionColor,
selectionControls: widget.selectionEnabled selectionControls: widget.selectionEnabled
? cupertinoTextSelectionControls : null, ? textSelectionControls : null,
onChanged: widget.onChanged, onChanged: widget.onChanged,
onSelectionChanged: _handleSelectionChanged, onSelectionChanged: _handleSelectionChanged,
onEditingComplete: widget.onEditingComplete, onEditingComplete: widget.onEditingComplete,
......
...@@ -193,6 +193,7 @@ class SelectableText extends StatefulWidget { ...@@ -193,6 +193,7 @@ class SelectableText extends StatefulWidget {
this.cursorColor, this.cursorColor,
this.dragStartBehavior = DragStartBehavior.start, this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection = true, this.enableInteractiveSelection = true,
this.selectionControls,
this.onTap, this.onTap,
this.scrollPhysics, this.scrollPhysics,
this.textHeightBehavior, this.textHeightBehavior,
...@@ -245,6 +246,7 @@ class SelectableText extends StatefulWidget { ...@@ -245,6 +246,7 @@ class SelectableText extends StatefulWidget {
this.cursorColor, this.cursorColor,
this.dragStartBehavior = DragStartBehavior.start, this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection = true, this.enableInteractiveSelection = true,
this.selectionControls,
this.onTap, this.onTap,
this.scrollPhysics, this.scrollPhysics,
this.textHeightBehavior, this.textHeightBehavior,
...@@ -353,6 +355,9 @@ class SelectableText extends StatefulWidget { ...@@ -353,6 +355,9 @@ class SelectableText extends StatefulWidget {
/// {@macro flutter.widgets.editableText.enableInteractiveSelection} /// {@macro flutter.widgets.editableText.enableInteractiveSelection}
final bool enableInteractiveSelection; final bool enableInteractiveSelection;
/// {@macro flutter.widgets.editableText.selectionControls}
final TextSelectionControls? selectionControls;
/// {@macro flutter.widgets.scrollable.dragStartBehavior} /// {@macro flutter.widgets.scrollable.dragStartBehavior}
final DragStartBehavior dragStartBehavior; final DragStartBehavior dragStartBehavior;
...@@ -416,6 +421,7 @@ class SelectableText extends StatefulWidget { ...@@ -416,6 +421,7 @@ class SelectableText extends StatefulWidget {
properties.add(DiagnosticsProperty<Radius>('cursorRadius', cursorRadius, defaultValue: null)); properties.add(DiagnosticsProperty<Radius>('cursorRadius', cursorRadius, defaultValue: null));
properties.add(DiagnosticsProperty<Color>('cursorColor', cursorColor, defaultValue: null)); properties.add(DiagnosticsProperty<Color>('cursorColor', cursorColor, defaultValue: null));
properties.add(FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled')); properties.add(FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled'));
properties.add(DiagnosticsProperty<TextSelectionControls>('selectionControls', selectionControls, defaultValue: null));
properties.add(DiagnosticsProperty<ScrollPhysics>('scrollPhysics', scrollPhysics, defaultValue: null)); properties.add(DiagnosticsProperty<ScrollPhysics>('scrollPhysics', scrollPhysics, defaultValue: null));
properties.add(DiagnosticsProperty<TextHeightBehavior>('textHeightBehavior', textHeightBehavior, defaultValue: null)); properties.add(DiagnosticsProperty<TextHeightBehavior>('textHeightBehavior', textHeightBehavior, defaultValue: null));
} }
...@@ -565,7 +571,7 @@ class _SelectableTextState extends State<SelectableText> with AutomaticKeepAlive ...@@ -565,7 +571,7 @@ class _SelectableTextState extends State<SelectableText> with AutomaticKeepAlive
final TextSelectionThemeData selectionTheme = TextSelectionTheme.of(context); final TextSelectionThemeData selectionTheme = TextSelectionTheme.of(context);
final FocusNode focusNode = _effectiveFocusNode; final FocusNode focusNode = _effectiveFocusNode;
final TextSelectionControls textSelectionControls; TextSelectionControls? textSelectionControls = widget.selectionControls;
final bool paintCursorAboveText; final bool paintCursorAboveText;
final bool cursorOpacityAnimates; final bool cursorOpacityAnimates;
Offset? cursorOffset; Offset? cursorOffset;
...@@ -578,7 +584,7 @@ class _SelectableTextState extends State<SelectableText> with AutomaticKeepAlive ...@@ -578,7 +584,7 @@ class _SelectableTextState extends State<SelectableText> with AutomaticKeepAlive
case TargetPlatform.macOS: case TargetPlatform.macOS:
final CupertinoThemeData cupertinoTheme = CupertinoTheme.of(context); final CupertinoThemeData cupertinoTheme = CupertinoTheme.of(context);
forcePressEnabled = true; forcePressEnabled = true;
textSelectionControls = cupertinoTextSelectionControls; textSelectionControls ??= cupertinoTextSelectionControls;
paintCursorAboveText = true; paintCursorAboveText = true;
cursorOpacityAnimates = true; cursorOpacityAnimates = true;
cursorColor ??= selectionTheme.cursorColor ?? cupertinoTheme.primaryColor; cursorColor ??= selectionTheme.cursorColor ?? cupertinoTheme.primaryColor;
...@@ -592,7 +598,7 @@ class _SelectableTextState extends State<SelectableText> with AutomaticKeepAlive ...@@ -592,7 +598,7 @@ class _SelectableTextState extends State<SelectableText> with AutomaticKeepAlive
case TargetPlatform.linux: case TargetPlatform.linux:
case TargetPlatform.windows: case TargetPlatform.windows:
forcePressEnabled = false; forcePressEnabled = false;
textSelectionControls = materialTextSelectionControls; textSelectionControls ??= materialTextSelectionControls;
paintCursorAboveText = false; paintCursorAboveText = false;
cursorOpacityAnimates = false; cursorOpacityAnimates = false;
cursorColor ??= selectionTheme.cursorColor ?? theme.colorScheme.primary; cursorColor ??= selectionTheme.cursorColor ?? theme.colorScheme.primary;
......
...@@ -372,6 +372,7 @@ class TextField extends StatefulWidget { ...@@ -372,6 +372,7 @@ class TextField extends StatefulWidget {
this.scrollPadding = const EdgeInsets.all(20.0), this.scrollPadding = const EdgeInsets.all(20.0),
this.dragStartBehavior = DragStartBehavior.start, this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection = true, this.enableInteractiveSelection = true,
this.selectionControls,
this.onTap, this.onTap,
this.mouseCursor, this.mouseCursor,
this.buildCounter, this.buildCounter,
...@@ -674,6 +675,9 @@ class TextField extends StatefulWidget { ...@@ -674,6 +675,9 @@ class TextField extends StatefulWidget {
/// {@macro flutter.widgets.editableText.enableInteractiveSelection} /// {@macro flutter.widgets.editableText.enableInteractiveSelection}
final bool enableInteractiveSelection; final bool enableInteractiveSelection;
/// {@macro flutter.widgets.editableText.selectionControls}
final TextSelectionControls? selectionControls;
/// {@macro flutter.widgets.scrollable.dragStartBehavior} /// {@macro flutter.widgets.scrollable.dragStartBehavior}
final DragStartBehavior dragStartBehavior; final DragStartBehavior dragStartBehavior;
...@@ -818,6 +822,7 @@ class TextField extends StatefulWidget { ...@@ -818,6 +822,7 @@ class TextField extends StatefulWidget {
properties.add(DiagnosticsProperty<Brightness>('keyboardAppearance', keyboardAppearance, defaultValue: null)); properties.add(DiagnosticsProperty<Brightness>('keyboardAppearance', keyboardAppearance, defaultValue: null));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('scrollPadding', scrollPadding, defaultValue: const EdgeInsets.all(20.0))); properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('scrollPadding', scrollPadding, defaultValue: const EdgeInsets.all(20.0)));
properties.add(FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled')); properties.add(FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled'));
properties.add(DiagnosticsProperty<TextSelectionControls>('selectionControls', selectionControls, defaultValue: null));
properties.add(DiagnosticsProperty<ScrollController>('scrollController', scrollController, defaultValue: null)); properties.add(DiagnosticsProperty<ScrollController>('scrollController', scrollController, defaultValue: null));
properties.add(DiagnosticsProperty<ScrollPhysics>('scrollPhysics', scrollPhysics, defaultValue: null)); properties.add(DiagnosticsProperty<ScrollPhysics>('scrollPhysics', scrollPhysics, defaultValue: null));
} }
...@@ -1092,7 +1097,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements ...@@ -1092,7 +1097,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
if (widget.maxLength != null && widget.maxLengthEnforced) if (widget.maxLength != null && widget.maxLengthEnforced)
formatters.add(LengthLimitingTextInputFormatter(widget.maxLength)); formatters.add(LengthLimitingTextInputFormatter(widget.maxLength));
final TextSelectionControls textSelectionControls; TextSelectionControls? textSelectionControls = widget.selectionControls;
final bool paintCursorAboveText; final bool paintCursorAboveText;
final bool cursorOpacityAnimates; final bool cursorOpacityAnimates;
Offset? cursorOffset; Offset? cursorOffset;
...@@ -1106,7 +1111,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements ...@@ -1106,7 +1111,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
case TargetPlatform.macOS: case TargetPlatform.macOS:
final CupertinoThemeData cupertinoTheme = CupertinoTheme.of(context); final CupertinoThemeData cupertinoTheme = CupertinoTheme.of(context);
forcePressEnabled = true; forcePressEnabled = true;
textSelectionControls = cupertinoTextSelectionControls; textSelectionControls ??= cupertinoTextSelectionControls;
paintCursorAboveText = true; paintCursorAboveText = true;
cursorOpacityAnimates = true; cursorOpacityAnimates = true;
cursorColor ??= selectionTheme.cursorColor ?? cupertinoTheme.primaryColor; cursorColor ??= selectionTheme.cursorColor ?? cupertinoTheme.primaryColor;
...@@ -1121,7 +1126,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements ...@@ -1121,7 +1126,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
case TargetPlatform.linux: case TargetPlatform.linux:
case TargetPlatform.windows: case TargetPlatform.windows:
forcePressEnabled = false; forcePressEnabled = false;
textSelectionControls = materialTextSelectionControls; textSelectionControls ??= materialTextSelectionControls;
paintCursorAboveText = false; paintCursorAboveText = false;
cursorOpacityAnimates = false; cursorOpacityAnimates = false;
cursorColor ??= selectionTheme.cursorColor ?? theme.colorScheme.primary; cursorColor ??= selectionTheme.cursorColor ?? theme.colorScheme.primary;
......
...@@ -182,6 +182,7 @@ class TextFormField extends FormField<String> { ...@@ -182,6 +182,7 @@ class TextFormField extends FormField<String> {
Brightness? keyboardAppearance, Brightness? keyboardAppearance,
EdgeInsets scrollPadding = const EdgeInsets.all(20.0), EdgeInsets scrollPadding = const EdgeInsets.all(20.0),
bool enableInteractiveSelection = true, bool enableInteractiveSelection = true,
TextSelectionControls? selectionControls,
InputCounterWidgetBuilder? buildCounter, InputCounterWidgetBuilder? buildCounter,
ScrollPhysics? scrollPhysics, ScrollPhysics? scrollPhysics,
Iterable<String>? autofillHints, Iterable<String>? autofillHints,
...@@ -276,6 +277,7 @@ class TextFormField extends FormField<String> { ...@@ -276,6 +277,7 @@ class TextFormField extends FormField<String> {
scrollPhysics: scrollPhysics, scrollPhysics: scrollPhysics,
keyboardAppearance: keyboardAppearance, keyboardAppearance: keyboardAppearance,
enableInteractiveSelection: enableInteractiveSelection, enableInteractiveSelection: enableInteractiveSelection,
selectionControls: selectionControls,
buildCounter: buildCounter, buildCounter: buildCounter,
autofillHints: autofillHints, autofillHints: autofillHints,
); );
......
...@@ -875,6 +875,7 @@ class EditableText extends StatefulWidget { ...@@ -875,6 +875,7 @@ class EditableText extends StatefulWidget {
/// value is set to the ambient [ThemeData.textSelectionColor]. /// value is set to the ambient [ThemeData.textSelectionColor].
final Color? selectionColor; final Color? selectionColor;
/// {@template flutter.widgets.editableText.selectionControls}
/// Optional delegate for building the text selection handles and toolbar. /// Optional delegate for building the text selection handles and toolbar.
/// ///
/// The [EditableText] widget used on its own will not trigger the display /// The [EditableText] widget used on its own will not trigger the display
...@@ -889,6 +890,7 @@ class EditableText extends StatefulWidget { ...@@ -889,6 +890,7 @@ class EditableText extends StatefulWidget {
/// * [TextField], a Material Design themed wrapper of [EditableText], which /// * [TextField], a Material Design themed wrapper of [EditableText], which
/// shows the selection toolbar upon appropriate user events based on the /// shows the selection toolbar upon appropriate user events based on the
/// user's platform set in [ThemeData.platform]. /// user's platform set in [ThemeData.platform].
/// {@endtemplate}
final TextSelectionControls? selectionControls; final TextSelectionControls? selectionControls;
/// {@template flutter.widgets.editableText.keyboardType} /// {@template flutter.widgets.editableText.keyboardType}
......
...@@ -29,6 +29,36 @@ class MockClipboard { ...@@ -29,6 +29,36 @@ class MockClipboard {
} }
} }
class MockTextSelectionControls extends TextSelectionControls {
@override
Widget buildHandle(BuildContext context, TextSelectionHandleType type,
double textLineHeight) {
throw UnimplementedError();
}
@override
Widget buildToolbar(
BuildContext context,
Rect globalEditableRegion,
double textLineHeight,
Offset position,
List<TextSelectionPoint> endpoints,
TextSelectionDelegate delegate,
ClipboardStatusNotifier clipboardStatus) {
throw UnimplementedError();
}
@override
Offset getHandleAnchor(TextSelectionHandleType type, double textLineHeight) {
throw UnimplementedError();
}
@override
Size getHandleSize(double textLineHeight) {
throw UnimplementedError();
}
}
class PathBoundsMatcher extends Matcher { class PathBoundsMatcher extends Matcher {
const PathBoundsMatcher({ const PathBoundsMatcher({
this.rectMatcher, this.rectMatcher,
...@@ -4123,4 +4153,20 @@ void main() { ...@@ -4123,4 +4153,20 @@ void main() {
matchesGoldenFile('text_field_golden.TextSelectionStyle.2.png'), matchesGoldenFile('text_field_golden.TextSelectionStyle.2.png'),
); );
}); });
testWidgets('textSelectionControls is passed to EditableText', (WidgetTester tester) async {
final MockTextSelectionControls selectionControl = MockTextSelectionControls();
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoTextField(
selectionControls: selectionControl
),
),
),
);
final EditableText widget = tester.widget(find.byType(EditableText));
expect(widget.selectionControls, equals(selectionControl));
});
} }
...@@ -6295,6 +6295,84 @@ void main() { ...@@ -6295,6 +6295,84 @@ void main() {
expect(find.byType(TextButton), findsNWidgets(4)); expect(find.byType(TextButton), findsNWidgets(4));
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows }));
testWidgets('Custom toolbar test - Android text selection controls', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure',
);
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Center(
child: TextField(
controller: controller,
selectionControls: materialTextSelectionControls
),
),
),
),
);
final Offset textfieldStart = tester.getTopLeft(find.byType(TextField));
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0));
await tester.pump(const Duration(milliseconds: 50));
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0));
await tester.pumpAndSettle();
// Selected text shows 4 toolbar buttons: cut, copy, paste, select all
expect(find.byType(TextButton), findsNWidgets(4));
}, variant: TargetPlatformVariant.all());
testWidgets(
'Custom toolbar test - Cupertino text selection controls',
(WidgetTester tester) async {
final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure',
);
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Center(
child: TextField(
controller: controller,
selectionControls: cupertinoTextSelectionControls,
),
),
),
),
);
final Offset textfieldStart = tester.getTopLeft(find.byType(TextField));
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0));
await tester.pump(const Duration(milliseconds: 50));
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0));
await tester.pumpAndSettle();
// Selected text shows 3 toolbar buttons: cut, copy, paste
expect(find.byType(CupertinoButton), findsNWidgets(3));
}, variant: TargetPlatformVariant.all());
testWidgets('selectionControls is passed to EditableText',
(WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Scaffold(
body: TextField(
selectionControls: materialTextSelectionControls,
),
),
),
),
);
final EditableText widget = tester.widget(find.byType(EditableText));
expect(widget.selectionControls, equals(materialTextSelectionControls));
});
testWidgets( testWidgets(
'double tap on top of cursor also selects word', 'double tap on top of cursor also selects word',
(WidgetTester tester) async { (WidgetTester tester) async {
......
...@@ -514,4 +514,21 @@ void main() { ...@@ -514,4 +514,21 @@ void main() {
); );
}, throwsAssertionError); }, throwsAssertionError);
}); });
testWidgets('textSelectionControls is passed to super', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Scaffold(
body: TextFormField(
selectionControls: materialTextSelectionControls,
),
),
),
),
);
final TextField widget = tester.widget(find.byType(TextField));
expect(widget.selectionControls, equals(materialTextSelectionControls));
});
} }
...@@ -2771,6 +2771,93 @@ void main() { ...@@ -2771,6 +2771,93 @@ void main() {
}, },
); );
testWidgets(
'long press selects word and shows custom toolbar (Android)',
(WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Center(
child: SelectableText('Atwater Peel Sherbrooke Bonaventure',
selectionControls: cupertinoTextSelectionControls,
),
),
),
),
);
final Offset selectableTextStart = tester.getTopLeft(find.byType(SelectableText));
await tester.longPressAt(selectableTextStart + const Offset(50.0, 5.0));
await tester.pump();
final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first);
final TextEditingController controller = editableTextWidget.controller;
// The longpressed word is selected.
expect(
controller.selection,
const TextSelection(
baseOffset: 0,
extentOffset: 7,
),
);
// Toolbar shows one button.
expect(find.byType(CupertinoButton), findsNWidgets(1));
}, variant: TargetPlatformVariant.all());
testWidgets(
'long press selects word and shows custom toolbar (iOS)',
(WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Center(
child: SelectableText('Atwater Peel Sherbrooke Bonaventure',
selectionControls: materialTextSelectionControls,
),
),
),
),
);
final Offset selectableTextStart = tester.getTopLeft(find.byType(SelectableText));
await tester.longPressAt(selectableTextStart + const Offset(50.0, 5.0));
await tester.pump();
final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first);
final TextEditingController controller = editableTextWidget.controller;
expect(
controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7),
);
// Collapsed toolbar shows 2 buttons: copy, select all
expect(find.byType(TextButton), findsNWidgets(2));
}, variant: TargetPlatformVariant.all());
testWidgets('textSelectionControls is passed to EditableText',
(WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Scaffold(
body: SelectableText('Atwater Peel Sherbrooke Bonaventure',
selectionControls: materialTextSelectionControls,
),
),
),
),
);
final EditableText widget = tester.widget(find.byType(EditableText));
expect(widget.selectionControls, equals(materialTextSelectionControls));
});
testWidgets( testWidgets(
'long press tap cannot initiate a double tap', 'long press tap cannot initiate a double tap',
(WidgetTester tester) async { (WidgetTester tester) async {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment