Skip to content

Commit e070417

Browse files
authored
Add 'Share' button to the selection toolbar on Android (#139479)
## Description This PR adds the 'Share' button to the text selection toolbar on Android. ## Related Issue Fixes flutter/flutter#138728 ## Tests Refactor a lot of existing tests in order to: - make them more readable (avoid duplication by introducing helper functions, specify explictly check which buttons are expected). - make them more accurate (check that expected buttons are visible instead of just checking the number of buttons). For instance, previous tests contained sections such as: ```dart // Collapsed toolbar shows 3 buttons. expect( find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3) ); ``` Where the comment is obsolete, the two cases (6 widgets and 3 widgets) are not explicit (which buttons are expected?), and not accurate (will pass if the number of buttons is right but the buttons are the wrong ones).
1 parent c674161 commit e070417

10 files changed

Lines changed: 725 additions & 496 deletions

packages/flutter/lib/src/material/adaptive_text_selection_toolbar.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ class AdaptiveTextSelectionToolbar extends StatelessWidget {
226226
case ContextMenuButtonType.searchWeb:
227227
return localizations.searchWebButtonLabel;
228228
case ContextMenuButtonType.share:
229-
return localizations.searchWebButtonLabel;
229+
return localizations.shareButtonLabel;
230230
case ContextMenuButtonType.liveTextInput:
231231
return localizations.scanTextButtonLabel;
232232
case ContextMenuButtonType.custom:

packages/flutter/lib/src/widgets/editable_text.dart

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,6 +1882,10 @@ class EditableText extends StatefulWidget {
18821882
// If the paste button is enabled, don't render anything until the state
18831883
// of the clipboard is known, since it's used to determine if paste is
18841884
// shown.
1885+
1886+
// On Android, the share button is before the select all button.
1887+
final bool showShareBeforeSelectAll = defaultTargetPlatform == TargetPlatform.android;
1888+
18851889
resultButtonItem.addAll(<ContextMenuButtonItem>[
18861890
if (onCut != null)
18871891
ContextMenuButtonItem(
@@ -1898,6 +1902,11 @@ class EditableText extends StatefulWidget {
18981902
onPressed: onPaste,
18991903
type: ContextMenuButtonType.paste,
19001904
),
1905+
if (onShare != null && showShareBeforeSelectAll)
1906+
ContextMenuButtonItem(
1907+
onPressed: onShare,
1908+
type: ContextMenuButtonType.share,
1909+
),
19011910
if (onSelectAll != null)
19021911
ContextMenuButtonItem(
19031912
onPressed: onSelectAll,
@@ -1913,7 +1922,7 @@ class EditableText extends StatefulWidget {
19131922
onPressed: onSearchWeb,
19141923
type: ContextMenuButtonType.searchWeb,
19151924
),
1916-
if (onShare != null)
1925+
if (onShare != null && !showShareBeforeSelectAll)
19171926
ContextMenuButtonItem(
19181927
onPressed: onShare,
19191928
type: ContextMenuButtonType.share,
@@ -2300,13 +2309,18 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
23002309

23012310
@override
23022311
bool get shareEnabled {
2303-
if (defaultTargetPlatform != TargetPlatform.iOS) {
2304-
return false;
2312+
switch (defaultTargetPlatform) {
2313+
case TargetPlatform.android:
2314+
case TargetPlatform.iOS:
2315+
return !widget.obscureText
2316+
&& !textEditingValue.selection.isCollapsed
2317+
&& textEditingValue.selection.textInside(textEditingValue.text).trim() != '';
2318+
case TargetPlatform.macOS:
2319+
case TargetPlatform.fuchsia:
2320+
case TargetPlatform.linux:
2321+
case TargetPlatform.windows:
2322+
return false;
23052323
}
2306-
2307-
return !widget.obscureText
2308-
&& !textEditingValue.selection.isCollapsed
2309-
&& textEditingValue.selection.textInside(textEditingValue.text).trim() != '';
23102324
}
23112325

23122326
@override
@@ -2516,9 +2530,9 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
25162530
}
25172531

25182532
/// Launch the share interface for the current selection,
2519-
/// as in the "Share" edit menu button on iOS.
2533+
/// as in the "Share..." edit menu button on iOS.
25202534
///
2521-
/// Currently this is only implemented for iOS.
2535+
/// Currently this is only implemented for iOS and Android.
25222536
///
25232537
/// When 'obscureText' is true or the selection is empty,
25242538
/// this function will not do anything

packages/flutter/test/cupertino/adaptive_text_selection_toolbar_test.dart

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
1010

1111
import '../widgets/clipboard_utils.dart';
1212
import '../widgets/live_text_utils.dart';
13+
import '../widgets/text_selection_toolbar_utils.dart';
1314

1415
void main() {
1516
final MockClipboard mockClipboard = MockClipboard();
@@ -31,13 +32,6 @@ void main() {
3132
);
3233
});
3334

34-
Finder findOverflowNextButton() {
35-
return find.byWidgetPredicate((Widget widget) =>
36-
widget is CustomPaint &&
37-
'${widget.painter?.runtimeType}' == '_RightCupertinoChevronPainter',
38-
);
39-
}
40-
4135
testWidgetsWithLeakTracking('Builds the right toolbar on each platform, including web, and shows buttonItems', (WidgetTester tester) async {
4236
const String buttonText = 'Click me';
4337

@@ -188,27 +182,55 @@ void main() {
188182
));
189183

190184
expect(find.byKey(key), findsOneWidget);
191-
expect(find.text('Copy'), findsOneWidget);
192-
expect(find.text('Cut'), findsOneWidget);
193-
expect(find.text('Select All'), findsOneWidget);
194-
expect(find.text('Paste'), findsOneWidget);
195-
expect(find.text('Look Up'), findsOneWidget);
196185

197186
switch (defaultTargetPlatform) {
198187
case TargetPlatform.android:
199188
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(6));
189+
expect(find.text('Cut'), findsOneWidget);
190+
expect(find.text('Copy'), findsOneWidget);
191+
expect(find.text('Paste'), findsOneWidget);
192+
expect(find.text('Select All'), findsOneWidget);
193+
expect(find.text('Share...'), findsOneWidget);
194+
expect(findCupertinoOverflowNextButton(), findsOneWidget);
195+
196+
await tapCupertinoOverflowNextButton(tester);
197+
198+
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(4));
199+
expect(findCupertinoOverflowBackButton(), findsOneWidget);
200+
expect(find.text('Look Up'), findsOneWidget);
201+
expect(find.text('Search Web'), findsOneWidget);
202+
expect(findLiveTextButton(), findsOneWidget);
203+
200204
case TargetPlatform.fuchsia:
201-
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(6));
202205
case TargetPlatform.iOS:
203206
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(6));
204-
expect(findOverflowNextButton(), findsOneWidget);
205-
await tester.tapAt(tester.getCenter(findOverflowNextButton()));
206-
await tester.pumpAndSettle();
207+
expect(find.text('Cut'), findsOneWidget);
208+
expect(find.text('Copy'), findsOneWidget);
209+
expect(find.text('Paste'), findsOneWidget);
210+
expect(find.text('Select All'), findsOneWidget);
211+
expect(find.text('Look Up'), findsOneWidget);
212+
expect(findCupertinoOverflowNextButton(), findsOneWidget);
213+
214+
await tapCupertinoOverflowNextButton(tester);
215+
216+
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(4));
217+
expect(findCupertinoOverflowBackButton(), findsOneWidget);
218+
expect(find.text('Search Web'), findsOneWidget);
219+
expect(find.text('Share...'), findsOneWidget);
207220
expect(findLiveTextButton(), findsOneWidget);
221+
208222
case TargetPlatform.macOS:
209223
case TargetPlatform.linux:
210224
case TargetPlatform.windows:
211225
expect(find.byType(CupertinoDesktopTextSelectionToolbarButton), findsNWidgets(8));
226+
expect(find.text('Cut'), findsOneWidget);
227+
expect(find.text('Copy'), findsOneWidget);
228+
expect(find.text('Paste'), findsOneWidget);
229+
expect(find.text('Select All'), findsOneWidget);
230+
expect(find.text('Share...'), findsOneWidget);
231+
expect(find.text('Look Up'), findsOneWidget);
232+
expect(find.text('Search Web'), findsOneWidget);
233+
expect(findLiveTextButton(), findsOneWidget);
212234
}
213235
},
214236
skip: kIsWeb, // [intended] on web the browser handles the context menu.

0 commit comments

Comments
 (0)