diff --git a/AUTHORS b/AUTHORS index 397a652a05d..156418850fc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -42,4 +42,5 @@ Igor Katsuba Diego Velásquez Simon Lightfoot Sarbagya Dhaubanjar +Rody Davis Jr Robin Jespersen diff --git a/packages/flutter/lib/src/cupertino/text_field.dart b/packages/flutter/lib/src/cupertino/text_field.dart index b249066cfba..78c8e9ce6a0 100644 --- a/packages/flutter/lib/src/cupertino/text_field.dart +++ b/packages/flutter/lib/src/cupertino/text_field.dart @@ -265,7 +265,7 @@ class CupertinoTextField extends StatefulWidget { assert(prefixMode != null), assert(suffixMode != null), keyboardType = keyboardType ?? (maxLines == 1 ? TextInputType.text : TextInputType.multiline), - toolbarOptions = toolbarOptions ?? obscureText ? + toolbarOptions = toolbarOptions ?? (obscureText ? const ToolbarOptions( selectAll: true, paste: true, @@ -275,7 +275,7 @@ class CupertinoTextField extends StatefulWidget { cut: true, selectAll: true, paste: true, - ), + )), super(key: key); /// Controls the text being edited. diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart index f86be7ddebc..5813746d727 100644 --- a/packages/flutter/lib/src/material/text_field.dart +++ b/packages/flutter/lib/src/material/text_field.dart @@ -346,7 +346,7 @@ class TextField extends StatefulWidget { ), assert(maxLength == null || maxLength == TextField.noMaxLength || maxLength > 0), keyboardType = keyboardType ?? (maxLines == 1 ? TextInputType.text : TextInputType.multiline), - toolbarOptions = toolbarOptions ?? obscureText ? + toolbarOptions = toolbarOptions ?? (obscureText ? const ToolbarOptions( selectAll: true, paste: true, @@ -356,7 +356,7 @@ class TextField extends StatefulWidget { cut: true, selectAll: true, paste: true, - ), + )), super(key: key); /// Controls the text being edited. diff --git a/packages/flutter/test/cupertino/text_field_test.dart b/packages/flutter/test/cupertino/text_field_test.dart index 91986d0a44b..7418f1dbe72 100644 --- a/packages/flutter/test/cupertino/text_field_test.dart +++ b/packages/flutter/test/cupertino/text_field_test.dart @@ -1290,6 +1290,49 @@ void main() { expect(text.style.fontWeight, FontWeight.w400); }); + + testWidgets('text field toolbar options correctly changes options', (WidgetTester tester) async { + final TextEditingController controller = TextEditingController( + text: 'Atwater Peel Sherbrooke Bonaventure', + ); + await tester.pumpWidget( + CupertinoApp( + home: Column( + children: [ + CupertinoTextField( + controller: controller, + toolbarOptions: const ToolbarOptions(copy: true), + ), + ], + ), + ), + ); + + // Long press to put the cursor after the "w". + const int index = 3; + await tester.longPressAt(textOffsetToPosition(tester, index)); + await tester.pump(); + expect( + controller.selection, + const TextSelection.collapsed(offset: index), + ); + + // Double tap on the same location to select the word around the cursor. + await tester.tapAt(textOffsetToPosition(tester, index)); + await tester.pump(const Duration(milliseconds: 50)); + await tester.tapAt(textOffsetToPosition(tester, index)); + await tester.pump(); + expect( + controller.selection, + const TextSelection(baseOffset: 0, extentOffset: 7), + ); + + // Selected text shows 'Copy'. + expect(find.text('Paste'), findsNothing); + expect(find.text('Copy'), findsOneWidget); + expect(find.text('Cut'), findsNothing); + expect(find.text('Select All'), findsNothing); + }); testWidgets('Read only text field', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(text: 'readonly'); diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index f44b38fc3b5..930f394e9e5 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -505,6 +505,89 @@ void main() { ); }, skip: isBrowser); + testWidgets('text field toolbar options correctly changes options (iOS)', + (WidgetTester tester) async { + final TextEditingController controller = TextEditingController( + text: 'Atwater Peel Sherbrooke Bonaventure', + ); + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(platform: TargetPlatform.iOS), + home: Material( + child: Center( + child: TextField( + controller: controller, + toolbarOptions: const ToolbarOptions(copy: true), + ), + ), + ), + ), + ); + + final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); + + // This tap just puts the cursor somewhere different than where the double + // tap will occur to test that the double tap moves the existing cursor first. + await tester.tapAt(textfieldStart + const Offset(50.0, 5.0)); + await tester.pump(const Duration(milliseconds: 500)); + + await tester.tapAt(textfieldStart + const Offset(150.0, 5.0)); + await tester.pump(const Duration(milliseconds: 50)); + // First tap moved the cursor. + expect( + controller.selection, + const TextSelection.collapsed( + offset: 8, affinity: TextAffinity.downstream), + ); + await tester.tapAt(textfieldStart + const Offset(150.0, 5.0)); + await tester.pump(); + + // Second tap selects the word around the cursor. + expect( + controller.selection, + const TextSelection(baseOffset: 8, extentOffset: 12), + ); + + // Selected text shows 'Copy', and not 'Paste', 'Cut', 'Select All'. + expect(find.text('Paste'), findsNothing); + expect(find.text('Copy'), findsOneWidget); + expect(find.text('Cut'), findsNothing); + expect(find.text('Select All'), findsNothing); + }, skip: isBrowser); + + testWidgets('text field toolbar options correctly changes options (Android)', + (WidgetTester tester) async { + final TextEditingController controller = TextEditingController( + text: 'Atwater Peel Sherbrooke Bonaventure', + ); + await tester.pumpWidget( + MaterialApp( + home: Material( + child: Center( + child: TextField( + controller: controller, + toolbarOptions: const ToolbarOptions(copy: true), + ), + ), + ), + ), + ); + + final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); + + await tester.tapAt(textfieldStart + const Offset(150.0, 5.0)); + await tester.pump(const Duration(milliseconds: 50)); + + await tester.tapAt(textfieldStart + const Offset(150.0, 5.0)); + await tester.pump(); + + // Selected text shows 'COPY', and not 'PASTE', 'CUT', 'SELECT ALL'. + expect(find.text('PASTE'), findsNothing); + expect(find.text('COPY'), findsOneWidget); + expect(find.text('CUT'), findsNothing); + expect(find.text('SELECT ALL'), findsNothing); + }, skip: isBrowser); + // TODO(hansmuller): restore these tests after the fix for #24876 has landed. /* testWidgets('cursor layout has correct width', (WidgetTester tester) async {