-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Fix/ios share context menu #176199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix/ios share context menu #176199
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request correctly adds the missing 'Share' option to the default iOS SystemContextMenu when shareEnabled is true, aligning Flutter's behavior with native iOS. The change is simple and effective, and it's accompanied by a thorough widget test to ensure the new functionality works as expected. I've included a few minor suggestions to improve code consistency and readability.
| if (editableTextState.liveTextInputEnabled) const IOSSystemContextMenuItemLiveText(), | ||
| if (editableTextState.shareEnabled) const IOSSystemContextMenuItemShare(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency with the order of getters in EditableTextState, it would be better to place the shareEnabled check before liveTextInputEnabled. This improves logical grouping and maintainability, adhering to the style guide's principle of logical ordering.1
if (editableTextState.shareEnabled) const IOSSystemContextMenuItemShare(),
if (editableTextState.liveTextInputEnabled) const IOSSystemContextMenuItemLiveText(),Style Guide References
Footnotes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would agree with that but no big deal if you have a reason for this ordering, @muradhossin .
|
|
||
| testWidgets( | ||
| 'Default iOS SystemContextMenu includes Share for non-empty selection', | ||
| (WidgetTester tester) async { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The indentation for the test callback seems off. Running dart format should fix this and ensure consistency with the rest of the file. This adheres to the Flutter style guide which mandates dart format usage.1
| (WidgetTester tester) async { | |
| (WidgetTester tester) async { |
Style Guide References
Footnotes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this is right or not, I'll defer to the CI checks. If they're green it's ok.
| final List<IOSSystemContextMenuItemData> lastItems = untypedItems.map((dynamic value) { | ||
| final Map<String, dynamic> itemJson = value as Map<String, dynamic>; | ||
| return systemContextMenuItemDataFromJson(itemJson); | ||
| }).toList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This mapping can be simplified for better readability by using a collection for.
| final List<IOSSystemContextMenuItemData> lastItems = untypedItems.map((dynamic value) { | |
| final Map<String, dynamic> itemJson = value as Map<String, dynamic>; | |
| return systemContextMenuItemDataFromJson(itemJson); | |
| }).toList(); | |
| final List<IOSSystemContextMenuItemData> lastItems = <IOSSystemContextMenuItemData>[ | |
| for (final dynamic value in untypedItems) | |
| systemContextMenuItemDataFromJson(value as Map<String, dynamic>), | |
| ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems legit.
justinmc
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for fixing this. Did we just forget to add the share button here? It makes me really wish we could use a switch or something to ensure that we don't miss any other new subclasses.
Take a look at the analyzer failure, you're missing a type in your test.
| if (editableTextState.liveTextInputEnabled) const IOSSystemContextMenuItemLiveText(), | ||
| if (editableTextState.shareEnabled) const IOSSystemContextMenuItemShare(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would agree with that but no big deal if you have a reason for this ordering, @muradhossin .
|
|
||
| testWidgets( | ||
| 'Default iOS SystemContextMenu includes Share for non-empty selection', | ||
| (WidgetTester tester) async { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this is right or not, I'll defer to the CI checks. If they're green it's ok.
| final List<IOSSystemContextMenuItemData> lastItems = untypedItems.map((dynamic value) { | ||
| final Map<String, dynamic> itemJson = value as Map<String, dynamic>; | ||
| return systemContextMenuItemDataFromJson(itemJson); | ||
| }).toList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems legit.
justinmc
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good besides the accidental changes in pubspec.lock.
| for (final ContextMenuButtonItem button in editableTextState.contextMenuButtonItems) { | ||
| switch (button.type) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great, now we know all button types are handled and always will be!
| expect( | ||
| itemsReceived.last.any( | ||
| (IOSSystemContextMenuItemData e) => e is IOSSystemContextMenuItemDataShare, | ||
| ), | ||
| isTrue, | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I wonder if there is a matcher that you could use here like contains? See https://main-api.flutter.dev/flutter/package-matcher_matcher/.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you revert the changes in this file?
justinmc
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍, thanks for following through on this PR!
| for (final ContextMenuButtonItem button in editableTextState.contextMenuButtonItems) { | ||
| switch (button.type) { | ||
| case ContextMenuButtonType.copy: | ||
| items.add(const IOSSystemContextMenuItemCopy()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does editableTextState.copyEnabled etc not need to be checked any more?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
contextMenuButtonItems already applies those enable-checks for us.
So by iterating editableTextState.contextMenuButtonItems, we’re consuming the filtered set — only enabled actions are present.
That’s why we no longer need separate editableTextState.copyEnabled etc checks here — the switch is now operating on the already-screened model, which is the single source of truth for both Flutter-rendered and native menus.
|
@justinmc May I know if there’s any update on the merge? |
dkwingsmt
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thank you!
|
@muradhossin Can you fix the analyzer failure here? |
Fixes flutter#173491. This adds IOSSystemContextMenuItemShare to the default iOS system context menu items when shareEnabled is true, ensuring native 'Share' option appears for text selections.
# Conflicts: # pubspec.lock
@justinmc Could you please guide me on why many of the checks are failing? |
can you try removing your changes to pubspec.lock and then merge master? |
Roll Flutter from 13b2b91 to 01d37bc (74 revisions) flutter/flutter@13b2b91...01d37bc 2026-01-07 stuartmorgan@google.com Replace Hybrid Composition wiki page with dev-facing content (flutter/flutter#180642) 2026-01-07 116356835+AbdeMohlbi@users.noreply.github.com Improve code quality in `FlutterActivityTest.java` (flutter/flutter#180585) 2026-01-07 iinozemtsev@google.com Roll Dart SDK to 3.11.0-296.1.beta (flutter/flutter#180633) 2026-01-07 vegorov@google.com Bump target Windows version to 10 (flutter/flutter#180624) 2026-01-07 goderbauer@google.com Run hook_user_defines and link_hook integration tests on CI (flutter/flutter#180622) 2026-01-07 iliyazelenkog@gmail.com Fix division by zero in RenderTable intrinsic size methods (flutter/flutter#178217) 2026-01-07 116356835+AbdeMohlbi@users.noreply.github.com Remove more `requires 24` anotations (flutter/flutter#180116) 2026-01-07 engine-flutter-autoroll@skia.org Roll Skia from 3971dbb85d45 to c5359a4d720e (1 revision) (flutter/flutter#180631) 2026-01-07 huy@nevercode.io Fix TabBar.image does not render at initialIndex for the first time (flutter/flutter#179616) 2026-01-07 engine-flutter-autoroll@skia.org Roll Skia from 1e7ad625f6f7 to 3971dbb85d45 (3 revisions) (flutter/flutter#180627) 2026-01-07 goderbauer@google.com Unpin DDS (flutter/flutter#180571) 2026-01-07 bruno.leroux@gmail.com Fix DropdownMenuEntry.style not resolved when entry is highlighted (flutter/flutter#178873) 2026-01-07 116356835+AbdeMohlbi@users.noreply.github.com Remove unnecessary `@RequiresApi24` annotations from FlutterFragment methods (flutter/flutter#180117) 2026-01-07 engine-flutter-autoroll@skia.org Roll Skia from 7fc63228056b to 1e7ad625f6f7 (1 revision) (flutter/flutter#180616) 2026-01-07 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from QfR2ZFZ5kGTD3raO3... to dTvN_JVSCfGFRasvH... (flutter/flutter#180612) 2026-01-07 bkonyi@google.com [ Widget Preview ] Add support for `dart:ffi` imports (flutter/flutter#180586) 2026-01-07 engine-flutter-autoroll@skia.org Roll Skia from eec90000a899 to 7fc63228056b (2 revisions) (flutter/flutter#180608) 2026-01-07 ulisses.hen@hotmail.com Add --web-define flag for template variable injection in Flutter web builds (flutter/flutter#175805) 2026-01-07 codefu@google.com docs(engine): update rbe notes (flutter/flutter#180599) 2026-01-06 engine-flutter-autoroll@skia.org Roll Skia from b6249496c230 to eec90000a899 (3 revisions) (flutter/flutter#180602) 2026-01-06 146823759+brahim-guaali@users.noreply.github.com Forward proxy 404 responses to client (flutter/flutter#179858) 2026-01-06 engine-flutter-autoroll@skia.org Roll Dart SDK from 40a8c6188f7f to d2e3ce177270 (1 revision) (flutter/flutter#180598) 2026-01-06 104009581+Saqib198@users.noreply.github.com Restore CLI precedence for web headers and HTTPS over web_dev_config.yaml (flutter/flutter#179639) 2026-01-06 engine-flutter-autoroll@skia.org Roll Skia from f5d9da13d56d to b6249496c230 (1 revision) (flutter/flutter#180596) 2026-01-06 dkwingsmt@users.noreply.github.com Enable misc leak tracking (flutter/flutter#176992) 2026-01-06 dacoharkes@google.com [hooks] Don't require NDK for Android targets (flutter/flutter#180594) 2026-01-06 kjlubick@users.noreply.github.com [skia] Disable legacy non-const SkData APIs (flutter/flutter#179684) 2026-01-06 48625061+muradhossin@users.noreply.github.com Fix/ios share context menu (flutter/flutter#176199) 2026-01-06 engine-flutter-autoroll@skia.org Roll Skia from b970aeffa66f to f5d9da13d56d (5 revisions) (flutter/flutter#180588) 2026-01-06 goderbauer@google.com Manually bump dependencies (flutter/flutter#180509) 2026-01-06 engine-flutter-autoroll@skia.org Roll Dart SDK from 8150be8a0e48 to 40a8c6188f7f (2 revisions) (flutter/flutter#180582) 2026-01-06 engine-flutter-autoroll@skia.org Roll Packages from 12eb764 to d3f860d (2 revisions) (flutter/flutter#180581) 2026-01-06 engine-flutter-autoroll@skia.org Roll Fuchsia Test Scripts from MHF-UAfO6sVKqSEYk... to nR2ESa1Gd8yPcWo06... (flutter/flutter#180578) 2026-01-06 sokolovskyi.konstantin@gmail.com Add tooltip support to PlatformMenuItem and PlatformMenu. (flutter/flutter#180069) 2026-01-06 bruno.leroux@gmail.com Add DropdownMenuFormField.errorBuilder (flutter/flutter#179345) 2026-01-06 engine-flutter-autoroll@skia.org Roll Skia from 1845397e11ba to b970aeffa66f (2 revisions) (flutter/flutter#180566) 2026-01-06 oss@simonbinder.eu Don't embed unreferenced assets (flutter/flutter#179251) 2026-01-06 116356835+AbdeMohlbi@users.noreply.github.com Improve documentation about ValueNotifier's behavior (flutter/flutter#179870) 2026-01-06 engine-flutter-autoroll@skia.org Roll Skia from 904ba00331ca to 1845397e11ba (5 revisions) (flutter/flutter#180558) 2026-01-06 engine-flutter-autoroll@skia.org Roll Dart SDK from 2fb9ad834c4d to 8150be8a0e48 (1 revision) (flutter/flutter#180557) 2026-01-06 engine-flutter-autoroll@skia.org Roll Skia from 98c042dde68c to 904ba00331ca (3 revisions) (flutter/flutter#180550) 2026-01-06 engine-flutter-autoroll@skia.org Roll Dart SDK from ba9f7f790966 to 2fb9ad834c4d (2 revisions) (flutter/flutter#180548) 2026-01-06 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from ubBGcRaAKWKihQ4ac... to QfR2ZFZ5kGTD3raO3... (flutter/flutter#180547) 2026-01-06 ahmedsameha1@gmail.com Make sure that a DraggableScrollableSheet doesn't crash in 0x0 enviro… (flutter/flutter#180433) 2026-01-06 ahmedsameha1@gmail.com Make sure that a ColorFiltered doesn't crash 0x0 environment (flutter/flutter#180307) 2026-01-06 ahmedsameha1@gmail.com Make sure that a FadeInImage doesn't crash in 0x0 environment (flutter/flutter#180495) ...
This PR fixes #173491 by adding the missing 'Share' option to the default iOS SystemContextMenu when shareEnabled is true.
Changes:
Added IOSSystemContextMenuItemShare to getDefaultItems in system_context_menu.dart.
Added a widget test to ensure Share is present in the default items for non-empty selections on iOS.
Rationale:
This aligns Flutter's default iOS text selection context menu with native iOS behavior, ensuring users see the expected 'Share' option when selecting text.
Demo:
Video showing Share option in iOS context menu: https://github.com/user-attachments/assets/e04cd1f9-7d92-4147-a09b-719f03d9c625