flutter_email_sender
Allows sending emails from Flutter using native platform functionality.
On Android it opens an email app via an intent. When several email apps are installed, the user picks one from a chooser that lists only email apps.
On iOS MFMailComposeViewController is used to compose an email when an account is set up in Apple Mail.
On macOS NSSharingService with .composeEmail is used to compose an email.
On Linux xdg-email opens the default mail client, such as Thunderbird. Without xdg-email, the plugin opens a mailto: link, which cannot carry attachments.
When the native composer is unavailable on iOS or macOS, for example on an iPhone without an Apple Mail account, the plugin opens a mailto: link in the default mail app (such as Gmail) instead. Attachments and HTML bodies cannot be sent that way, and getCapabilities() reports them as unsupported.
The plugin exposes platform capabilities so apps can adapt their UI before sending.
The public API throws typed Dart exceptions rather than exposing raw PlatformExceptions for expected failures.
Platform Support
| Platform | cc |
bcc |
HTML body | Attachments |
|---|---|---|---|---|
| Android | Yes | Yes | Yes | Yes |
| iOS | Yes | Yes | Yes | Yes |
iOS, mailto: fallback |
Yes | Yes | No | No |
| macOS | No | No | No | Yes |
macOS, mailto: fallback |
Yes | Yes | No | No |
| Linux | Yes | Yes | No | Yes |
Linux, mailto: fallback |
Yes | Yes | No | No |
| Web | Yes | Yes | No | No |
Web support uses mailto: and depends on browser and configured mail client behavior.
On Android, HTML support depends on the email app; Gmail shows the body as plain text.
On Linux, attachments reach Thunderbird installed from the distribution's packages, Evolution and KMail. Thunderbird installed as a Snap (the Ubuntu default) or Flatpak, Claws Mail and webmail receive the email as a mailto: link and drop the attachments without an error.
Example
final capabilities = await FlutterEmailSender.getCapabilities();
if (!capabilities.canSend) {
// No configured mail account, simulator, or no available mail client.
}
final Email email = Email(
body: 'Email body',
subject: 'Email subject',
recipients: ['example@example.com'],
cc: capabilities.supportsCc ? ['cc@example.com'] : const [],
bcc: capabilities.supportsBcc ? ['bcc@example.com'] : const [],
attachmentPaths: capabilities.supportsAttachments
? ['/path/to/attachment.zip']
: null,
isHTML: capabilities.supportsHtmlBody,
);
final result = await FlutterEmailSender.send(email);
switch (result) {
case EmailSendResult.sent:
case EmailSendResult.saved:
case EmailSendResult.cancelled:
// iOS reports how the user left the composer.
break;
case EmailSendResult.unknown:
// Other platforms do not report the outcome.
break;
}
On Android and iOS, send completes when the user leaves the composer; on macOS, Linux and web, once the composer has been opened.
try {
await FlutterEmailSender.send(email);
} on FlutterEmailSenderNotAvailableException {
// Email composer is unavailable on this device right now.
} on FlutterEmailSenderUnsupportedFeatureException catch (error) {
// Requested features are unsupported on the current platform.
debugPrint('Unsupported: ${error.unsupportedFeatures}');
}
Attachments
Attach files by path, or in-memory data with a file name:
final email = Email(
recipients: ['example@example.com'],
attachmentPaths: ['/path/to/report.pdf'],
attachments: [
EmailAttachment.file('/path/to/photo.jpg'),
EmailAttachment.data(
utf8.encode(csv),
fileName: 'export.csv',
mimeType: 'text/csv',
),
],
);
Files can be anywhere the app can read; a file that cannot be read makes send throw FlutterEmailSenderPlatformException. On Android, attachments are copied into the app's cache directory before being shared, so the original file can be deleted once send returns. On iOS, the MIME type comes from the file name extension unless mimeType is given. On Linux, in-memory data is written to the temporary directory and left there, because Thunderbird reads attachments only when the email is sent.
Errors
FlutterEmailSenderNotAvailableException: no email composer is currently available.FlutterEmailSenderUnsupportedFeatureException: requested fields are unsupported on the current platform.FlutterEmailSenderPlatformException: unexpected platform/plugin error, or an email that iOS failed to send (codesend_failed).
Migrating From 10.x To 11.0.0
-
sendnow returns anEmailSendResult. Existingawait FlutterEmailSender.send(email)calls keep compiling. -
iOS:
sendcompletes when the user leaves the composer, not when it opens. Code that ran right afterawait send(...), such as an "Email sent" message, now runs later; use the result to tell a sent email from a discarded one. -
iOS without an Apple Mail account (and macOS without a native composer):
canSendis nowtrueandsendopens amailto:link instead of throwingFlutterEmailSenderNotAvailableException. Emails with attachments or an HTML body throwFlutterEmailSenderUnsupportedFeatureExceptioninstead. If you caughtFlutterEmailSenderNotAvailableExceptionto offer your own fallback, such as a share sheet, also catchFlutterEmailSenderUnsupportedFeatureException, or checksupportsAttachmentsandsupportsHtmlBodyfromgetCapabilities()before sending:try { await FlutterEmailSender.send(email); } on FlutterEmailSenderNotAvailableException { // No mail app can be opened. } on FlutterEmailSenderUnsupportedFeatureException { // For example attachments while iOS falls back to mailto:. } -
An attachment file that cannot be read now makes
sendthrowFlutterEmailSenderPlatformExceptioninstead of being left out. -
Android: if your app uses AppCompat classes, declare
androidx.appcompat:appcompatin your app'sbuild.gradle; the plugin no longer brings it in. -
Android: the
SENDTOmailtoquery that earlier versions asked you to add toAndroidManifest.xmlcan be removed. -
Tests: if you replace
FlutterEmailSenderPlatformwith a test double, stubsendWithResult;FlutterEmailSender.sendcalls it instead ofsend.
Migrating From 9.x To 10.0.0
sendandgetCapabilitiesnow throw typed Dart exceptions for expected failures.- Apps that previously caught
PlatformException(code: 'not_available')should catchFlutterEmailSenderNotAvailableExceptioninstead. - Apps that previously caught
PlatformException(code: 'unsupported')should catchFlutterEmailSenderUnsupportedFeatureExceptioninstead. - Use
getCapabilities()andEmailCapabilities.canSendto adapt UI before callingsend.
Local Development
When consuming this plugin from a local checkout before all federated packages are published, point your app dependency at packages/flutter_email_sender and override the internal federated packages in pubspec_overrides.yaml.
Example app pubspec.yaml:
dependencies:
flutter_email_sender:
path: ../flutter_email_sender/packages/flutter_email_sender
Example app pubspec_overrides.yaml:
dependency_overrides:
flutter_email_sender_linux:
path: ../flutter_email_sender/packages/flutter_email_sender_linux
flutter_email_sender_method_channel:
path: ../flutter_email_sender/packages/flutter_email_sender_method_channel
flutter_email_sender_platform_interface:
path: ../flutter_email_sender/packages/flutter_email_sender_platform_interface
flutter_email_sender_web:
path: ../flutter_email_sender/packages/flutter_email_sender_web
Adjust the relative paths to match where your app and local plugin checkout live on disk.
Android Setup
No AndroidManifest.xml changes are needed: the plugin declares the package visibility <queries> it uses. Apps that added a SENDTO mailto query for earlier versions can remove it.
iOS Setup
No setup is needed. Optionally, list mailto under LSApplicationQueriesSchemes in ios/Runner/Info.plist so that canSend is accurate when Apple Mail has no account:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>mailto</string>
</array>
Without it, iOS does not let the plugin check whether a mail app can open mailto: links, so canSend is reported as true and send throws FlutterEmailSenderNotAvailableException when none can.
Linux Setup
No setup is needed. The plugin uses xdg-email from xdg-utils, which desktop distributions usually include. Without it, send opens a mailto: link and getCapabilities() reports attachments as unsupported.