Skip to content

feat(share_plus): add Android Save Action#3914

Open
vchrisb wants to merge 1 commit into
fluttercommunity:mainfrom
vchrisb:share_plus_android_save
Open

feat(share_plus): add Android Save Action#3914
vchrisb wants to merge 1 commit into
fluttercommunity:mainfrom
vchrisb:share_plus_android_save

Conversation

@vchrisb

@vchrisb vchrisb commented Jun 29, 2026

Copy link
Copy Markdown

Description

Adds an opt-in Save action to the Android Sharesheet when sharing files, providing an experience similar to the native Save option available in the iOS share sheet.

This PR:

  • Adds androidIncludeSaveAction and customizable AndroidSaveActionLabels to ShareParams.
  • Saves images to Pictures, videos to Movies, and other files to Downloads via MediaStore.
  • Requires no storage permission.
  • Shows progress and localized success or failure feedback.
  • Handles cache cleanup and rolls back partially saved batches on failure.
  • Adds documentation, an example, platform-interface tests, and Android instrumentation tests.

Why EXTRA_CHOOSER_CUSTOM_ACTIONS?

Android does not provide an equivalent Save option in its default Sharesheet. Intent.EXTRA_CHOOSER_CUSTOM_ACTIONS is the official extension point that most closely matches the native iOS experience: Save appears directly in the system Sharesheet alongside its other actions, instead of being presented as a separate share target or custom Flutter UI.

This API is only available on Android 14 (API level 34) and later. On older Android versions, other platforms, or shares without files, the option is ignored gracefully.

ShareResult semantics

The returned ShareResult reports the user’s interaction with the Sharesheet, including selection of the Save action. It does not wait for or report the result of the asynchronous MediaStore copy.

Saving progress and completion are handled natively on Android through the progress activity and a success or failure message.

Why does Share.kt contain many changes?

The Save action does not copy files inside the original share() call. Selecting it launches a native activity that performs the MediaStore copy on a background executor, and that copy may take some time.

Share.kt therefore needs to manage the shared files for the lifetime of the asynchronous save:

  • Each share receives a request-specific cache directory.
  • The cache directory and file URIs are passed to the Save activity.
  • Once saving starts, the directory is marked active so another share cannot delete it while files are being copied.
  • The directory is deleted only after the MediaStore copy finishes or fails.
  • Partially prepared cache directories are removed if creating the share fails.
  • Duplicate filenames are preserved instead of overwriting one another.
  • Path-boundary checks prevent files inside the share cache from being shared recursively.

Alternative considered

One alternative would be to expose arbitrary EXTRA_CHOOSER_CUSTOM_ACTIONS through ShareParams and notify Flutter when an action is selected. The application developer would then implement the selected action’s behavior.

Another option is not to provide a Save action in share_plus at all. Developers would instead build a Flutter UI that lets users choose between sharing and saving before opening the system Sharesheet. Sharing would remain handled by share_plus, while saving would require a separate package or application-specific implementation.

These approaches keep file saving outside the scope of share_plus and offer more flexibility, but they do not provide an integrated native Sharesheet experience. They also require each application to implement its own UI, file handling, localization, progress reporting, and error handling.

This PR instead provides a focused, ready-to-use Save action that behaves consistently and more closely matches the native iOS share-sheet experience.

Related Issues

Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I titled the PR using Conventional Commits.
  • I did not modify the CHANGELOG.md nor the plugin version in pubspec.yaml files.
  • All existing and new tests are passing.
  • The analyzer (flutter analyze) does not report any problems on my PR.

Breaking Change

Does your PR require plugin users to manually update their apps to accommodate your change?

  • Yes, this is a breaking change (please indicate that with a ! in the title as explained in Conventional Commits).
  • No, this is not a breaking change.

Add an opt-in Save action when sharing files through the Android 14+ Sharesheet. Use Intent.EXTRA_CHOOSER_CUSTOM_ACTIONS because it is Android's closest native equivalent to the Save option provided by the iOS share sheet.

Save images to Pictures, videos to Movies, and other files to Downloads through MediaStore without requiring storage permission. Support localized progress and result labels, preserve request-scoped cache files during asynchronous saves, and roll back partially saved batches on failure.

Ignore the option on older Android versions, other platforms, and shares without files. Document the API and add platform-interface and Android instrumentation coverage.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Request]: add a saveFiles option to share_plus

1 participant