Material allows "select all" when not collapsed (#32950)
This PR enables "Select all" on MaterialTextSelection when text is partially selected.
diff --git a/bin/internal/goldens.version b/bin/internal/goldens.version
index 00adb77..ad05202 100644
--- a/bin/internal/goldens.version
+++ b/bin/internal/goldens.version
@@ -1 +1 @@
-25ab3c95b1ac02a41973c05f96e664370857ce55
+d79941e3d9c6e761189ac8aefa3a40c7fb98413e
diff --git a/packages/flutter/lib/src/material/text_selection.dart b/packages/flutter/lib/src/material/text_selection.dart
index cf33e15..b50ff0c 100644
--- a/packages/flutter/lib/src/material/text_selection.dart
+++ b/packages/flutter/lib/src/material/text_selection.dart
@@ -212,6 +212,15 @@
assert(type != null);
return null;
}
+
+ @override
+ bool canSelectAll(TextSelectionDelegate delegate) {
+ // Android allows SelectAll when selection is not collapsed, unless
+ // everything has already been selected.
+ final TextEditingValue value = delegate.textEditingValue;
+ return value.text.isNotEmpty &&
+ !(value.selection.start == 0 && value.selection.end == value.text.length);
+ }
}
/// Text selection controls that follow the Material Design specification.
diff --git a/packages/flutter/test/cupertino/text_selection_test.dart b/packages/flutter/test/cupertino/text_selection_test.dart
new file mode 100644
index 0000000..dc884da
--- /dev/null
+++ b/packages/flutter/test/cupertino/text_selection_test.dart
@@ -0,0 +1,65 @@
+// Copyright 2019 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_test/flutter_test.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter/cupertino.dart';
+
+void main() {
+ group('canSelectAll', () {
+ Widget createEditableText({
+ Key key,
+ String text,
+ TextSelection selection,
+ }) {
+ final TextEditingController controller = TextEditingController(text: text)
+ ..selection = selection ?? const TextSelection.collapsed(offset: -1);
+ return CupertinoApp(
+ home: EditableText(
+ key: key,
+ controller: controller,
+ focusNode: FocusNode(),
+ style: const TextStyle(),
+ cursorColor: const Color.fromARGB(0, 0, 0, 0),
+ backgroundCursorColor: const Color.fromARGB(0, 0, 0, 0),
+ )
+ );
+ }
+
+ testWidgets('should return false when there is no text', (WidgetTester tester) async {
+ final GlobalKey<EditableTextState> key = GlobalKey();
+ await tester.pumpWidget(createEditableText(key: key));
+ expect(cupertinoTextSelectionControls.canSelectAll(key.currentState), false);
+ });
+
+ testWidgets('should return true when there is text and collapsed selection', (WidgetTester tester) async {
+ final GlobalKey<EditableTextState> key = GlobalKey();
+ await tester.pumpWidget(createEditableText(
+ key: key,
+ text: '123',
+ ));
+ expect(cupertinoTextSelectionControls.canSelectAll(key.currentState), true);
+ });
+
+ testWidgets('should return false when there is text and partial uncollapsed selection', (WidgetTester tester) async {
+ final GlobalKey<EditableTextState> key = GlobalKey();
+ await tester.pumpWidget(createEditableText(
+ key: key,
+ text: '123',
+ selection: const TextSelection(baseOffset: 1, extentOffset: 2),
+ ));
+ expect(cupertinoTextSelectionControls.canSelectAll(key.currentState), false);
+ });
+
+ testWidgets('should return false when there is text and full selection', (WidgetTester tester) async {
+ final GlobalKey<EditableTextState> key = GlobalKey();
+ await tester.pumpWidget(createEditableText(
+ key: key,
+ text: '123',
+ selection: const TextSelection(baseOffset: 0, extentOffset: 3),
+ ));
+ expect(cupertinoTextSelectionControls.canSelectAll(key.currentState), false);
+ });
+ });
+}
diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart
index 86a7879..6f46196 100644
--- a/packages/flutter/test/material/text_field_test.dart
+++ b/packages/flutter/test/material/text_field_test.dart
@@ -483,7 +483,7 @@
await expectLater(
// The toolbar exists in the Overlay above the MaterialApp.
find.byType(Overlay),
- matchesGoldenFile('text_field_opacity_test.0.1.png'),
+ matchesGoldenFile('text_field_opacity_test.0.2.png'),
skip: !Platform.isLinux,
);
});
@@ -4921,8 +4921,8 @@
const TextSelection(baseOffset: 8, extentOffset: 12),
);
- // Selected text shows 3 toolbar buttons.
- expect(find.byType(FlatButton), findsNWidgets(3));
+ // Selected text shows 4 toolbar buttons: cut, copy, paste, select all
+ expect(find.byType(FlatButton), findsNWidgets(4));
},
);
@@ -5086,8 +5086,8 @@
const TextSelection(baseOffset: 0, extentOffset: 7),
);
- // Collapsed toolbar shows 3 buttons.
- expect(find.byType(FlatButton), findsNWidgets(3));
+ // Collapsed toolbar shows 4 buttons: cut, copy, paste, select all
+ expect(find.byType(FlatButton), findsNWidgets(4));
},
);
diff --git a/packages/flutter/test/material/text_selection_test.dart b/packages/flutter/test/material/text_selection_test.dart
new file mode 100644
index 0000000..a143a00
--- /dev/null
+++ b/packages/flutter/test/material/text_selection_test.dart
@@ -0,0 +1,65 @@
+// Copyright 2019 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_test/flutter_test.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter/material.dart';
+
+void main() {
+ group('canSelectAll', () {
+ Widget createEditableText({
+ Key key,
+ String text,
+ TextSelection selection,
+ }) {
+ final TextEditingController controller = TextEditingController(text: text)
+ ..selection = selection ?? const TextSelection.collapsed(offset: -1);
+ return MaterialApp(
+ home: EditableText(
+ key: key,
+ controller: controller,
+ focusNode: FocusNode(),
+ style: const TextStyle(),
+ cursorColor: Colors.black,
+ backgroundCursorColor: Colors.black,
+ )
+ );
+ }
+
+ testWidgets('should return false when there is no text', (WidgetTester tester) async {
+ final GlobalKey<EditableTextState> key = GlobalKey();
+ await tester.pumpWidget(createEditableText(key: key));
+ expect(materialTextSelectionControls.canSelectAll(key.currentState), false);
+ });
+
+ testWidgets('should return true when there is text and collapsed selection', (WidgetTester tester) async {
+ final GlobalKey<EditableTextState> key = GlobalKey();
+ await tester.pumpWidget(createEditableText(
+ key: key,
+ text: '123',
+ ));
+ expect(materialTextSelectionControls.canSelectAll(key.currentState), true);
+ });
+
+ testWidgets('should return true when there is text and partial uncollapsed selection', (WidgetTester tester) async {
+ final GlobalKey<EditableTextState> key = GlobalKey();
+ await tester.pumpWidget(createEditableText(
+ key: key,
+ text: '123',
+ selection: const TextSelection(baseOffset: 1, extentOffset: 2),
+ ));
+ expect(materialTextSelectionControls.canSelectAll(key.currentState), true);
+ });
+
+ testWidgets('should return false when there is text and full selection', (WidgetTester tester) async {
+ final GlobalKey<EditableTextState> key = GlobalKey();
+ await tester.pumpWidget(createEditableText(
+ key: key,
+ text: '123',
+ selection: const TextSelection(baseOffset: 0, extentOffset: 3),
+ ));
+ expect(materialTextSelectionControls.canSelectAll(key.currentState), false);
+ });
+ });
+}