Skip to main content
RapidDev - Software Development Agency
flutterflow-integrationsCustom Action (Dart)

Outlook

Connect FlutterFlow to Outlook using a Custom Action that wraps the `enough_mail` Dart package — no Azure app registration required. The action connects to Outlook's IMAP server (outlook.office365.com:993) to read your inbox and to the SMTP server (smtp.office365.com:587) to send mail. Authentication uses an App Password or OAuth2 XOAUTH2 since Microsoft no longer allows basic username-and-password login.

What you'll learn

  • Why IMAP/SMTP requires a Custom Action rather than FlutterFlow's API Calls panel
  • How to generate an Outlook App Password for use with the `enough_mail` package
  • How to add the `enough_mail` dependency through FlutterFlow's Custom Code panel
  • How to write a Dart Custom Action that reads the Outlook inbox and returns messages to FlutterFlow
  • How to write a Dart Custom Action that sends email via Outlook SMTP and bind it to a compose form
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate16 min read40 minutesCommunicationLast updated July 2026RapidDev Engineering Team
TL;DR

Connect FlutterFlow to Outlook using a Custom Action that wraps the `enough_mail` Dart package — no Azure app registration required. The action connects to Outlook's IMAP server (outlook.office365.com:993) to read your inbox and to the SMTP server (smtp.office365.com:587) to send mail. Authentication uses an App Password or OAuth2 XOAUTH2 since Microsoft no longer allows basic username-and-password login.

Quick facts about this guide
FactValue
ToolOutlook
CategoryCommunication
MethodCustom Action (Dart)
DifficultyIntermediate
Time required40 minutes
Last updatedJuly 2026

Reading and Sending Outlook Email from a FlutterFlow App (No Azure Required)

Most tutorials for Outlook in FlutterFlow lead you down the Microsoft Graph + Azure app registration path. That approach is powerful but requires Azure portal access, an application registration, OAuth2 scopes, a token proxy, and significantly more setup time. There's a lighter-weight alternative for consumer Outlook.com accounts and many Microsoft 365 mailboxes: connect over IMAP (port 993, TLS) and SMTP (port 587, STARTTLS) using the `enough_mail` Dart package, with an App Password handling authentication. No Azure portal, no Microsoft Graph, no app registration.

The key constraint is Microsoft's authentication policy. Since late 2022, Microsoft has progressively disabled basic username-and-password authentication for Outlook IMAP/SMTP connections. To use IMAP/SMTP today you need either an **App Password** (generated in your Microsoft account security settings when MFA is enabled) or OAuth2 XOAUTH2. The App Password approach is significantly simpler for a FlutterFlow integration because it works as a plain string credential in the `enough_mail` connection call.

One important distinction: this page covers **consumer Outlook.com** accounts and most Microsoft 365 personal/family plans. Work and school Microsoft 365 accounts managed by an IT administrator may have IMAP disabled at the tenant level as a security policy — in that case, the Microsoft Graph API path (covered on the `outlook-365-api` page) is the only option. For simple personal email features in a FlutterFlow app — checking your inbox, sending a quick email, or building a lightweight email client — the IMAP/SMTP custom action approach is the fastest path to a working feature.

Integration method

Custom Action (Dart)

FlutterFlow's standard API Calls panel cannot speak the IMAP or SMTP protocols — they are not HTTP REST. Instead, you write Dart Custom Actions that wrap the `enough_mail` pub.dev package, which handles the IMAP/SMTP handshake, TLS connection, and auth negotiation for you. One action reads the inbox over IMAP; another sends email over SMTP. Both actions run natively on the device; they do not run in FlutterFlow's web preview mode and must be tested on a real device or Test Mode build.

Prerequisites

  • An Outlook.com consumer account or a Microsoft 365 personal/family plan (not a work/school account with IMAP disabled by IT)
  • An App Password generated in your Microsoft account security settings (requires MFA to be enabled on the account)
  • A FlutterFlow project on a paid plan (Custom Actions require a paid plan)
  • Basic familiarity with FlutterFlow's Custom Code panel and Action Flow Editor
  • Access to a physical Android or iOS device, or the ability to install a Test Mode APK, for testing (IMAP/SMTP actions don't run in web preview)

Step-by-step guide

1

Generate an App Password for your Outlook account

Microsoft requires either an App Password or OAuth2 XOAUTH2 for IMAP/SMTP access — standard username and password login no longer works. App Passwords are the simpler option for a FlutterFlow integration and are available when your Microsoft account has two-factor authentication (MFA) enabled. Open a browser and go to account.microsoft.com/security. Sign in with the Outlook account you want to use. Click 'Advanced security options' (you may need to scroll down). Under 'App passwords', click 'Create a new app password'. Microsoft will generate a 16-character password like `abcd efgh ijkl mnop` — copy it immediately because it's only shown once. You'll use this string in your Dart custom action instead of your normal Outlook password. If you don't see the App Passwords option, it means MFA is not yet enabled on your account. Scroll up to 'Two-step verification' and follow the prompts to enable it (takes about 3 minutes), then return to Advanced security options to generate the App Password. For Microsoft 365 work accounts, your IT administrator controls whether IMAP and App Passwords are allowed. If IMAP is blocked at the tenant level, you'll receive an 'Authentication failed' error even with a correct App Password — in that case, switch to the Microsoft Graph approach covered on the `outlook-365-api` page. Store the App Password securely. Do not hard-code it in your Dart custom action as a string literal — you'll pass it in as a parameter from FlutterFlow's secure App State, which is set once during a login or settings screen.

Pro tip: The generated App Password includes spaces for readability — when entering it in your app, include the spaces exactly as shown or strip them all out (both formats work with enough_mail).

Expected result: You have a 16-character App Password visible in your Microsoft account security settings, copied and stored securely. Your normal Outlook account password is unchanged.

2

Add the `enough_mail` dependency in FlutterFlow Custom Code

FlutterFlow manages pub.dev dependencies through the Custom Code panel — you don't use a terminal or edit pubspec.yaml directly. In the FlutterFlow editor, click the Custom Code icon in the left navigation sidebar (it looks like angle brackets `</>`). At the top of the Custom Code panel, click the Dependencies button (or the gear/settings icon near the top). A dialog opens showing your current pub.dev packages. Click '+ Add Dependency'. In the search field type `enough_mail`. The package should appear in results — it's a well-maintained IMAP/SMTP/POP3 Dart library. Select it and choose a version — use the latest stable release (check pub.dev/packages/enough_mail for the current version; as of mid-2026 it is in the 2.x range). Click Add. FlutterFlow will validate the package and show it in your dependency list with a green checkmark when resolved. If you see a version conflict warning, try specifying a slightly older pinned version. Also add `enough_mail_html` if you want to render HTML email bodies (optional — not required for plain text). For showing HTML content you'd pair it with the `flutter_html` or `flutter_widget_from_html` package. After the dependency is added, click the Custom Code panel back button to return to the main Custom Code view. You're now ready to write the IMAP and SMTP custom actions that import from `enough_mail`.

Pro tip: Custom Code dependencies in FlutterFlow are project-level. If you duplicate this project, the dependencies carry over — no need to re-add them.

Expected result: The `enough_mail` package appears in FlutterFlow's Custom Code dependency list with a green checkmark. No build errors are shown in the dependency panel.

3

Write the 'Fetch Inbox' IMAP Custom Action

In the Custom Code panel click + Add, then select Action. Name it `fetchOutlookInbox`. Set the Return Type to JSON (you'll return a list of message objects). Add two String parameters: `emailAddress` (the user's Outlook address) and `appPassword` (the App Password, passed from secure App State). In the Dart code editor, write the action. The `enough_mail` package provides `ImapClient` for making a TLS connection to Outlook's IMAP server, logging in with plain credentials (which enough_mail sends over the encrypted TLS connection), selecting the INBOX folder, fetching the latest messages, and parsing them into subject/from/date fields. Key IMAP connection details for Outlook: host = `outlook.office365.com`, port = `993`, security = `SslTls`. After connecting, call `login(emailAddress, appPassword)`. Then call `selectInbox()` to open the folder and `fetchMessageSequence(...)` to retrieve the most recent messages. Each fetched message gives you a `MimeMessage` object from which you can extract `decodeSubject()`, `from`, `decodeDate()`, and `decodeTextPlainPart()` for the plain-text body. Note: Custom actions do not run in FlutterFlow's web preview (Run mode in the browser). You must test this on a physical device or install a Test Mode APK. This is a FlutterFlow platform limitation with custom Dart code — not an `enough_mail` issue.

custom_action.dart
1// FlutterFlow Custom Action: fetchOutlookInbox
2// Parameters: emailAddress (String), appPassword (String)
3// Return type: JSON (List of message objects)
4
5import 'package:enough_mail/enough_mail.dart';
6
7Future<dynamic> fetchOutlookInbox(
8 String emailAddress,
9 String appPassword,
10) async {
11 final client = ImapClient(isLogEnabled: false);
12 try {
13 await client.connectToServer(
14 'outlook.office365.com',
15 993,
16 isSecure: true,
17 );
18 await client.login(emailAddress, appPassword);
19 await client.selectInbox();
20
21 // Fetch last 20 messages (reverse chronological)
22 final mailbox = client.selectedMailbox!;
23 final totalMessages = mailbox.messagesExists;
24 if (totalMessages == 0) {
25 await client.logout();
26 return [];
27 }
28 final start = totalMessages > 20 ? totalMessages - 19 : 1;
29 final sequence = MessageSequence.fromRange(start, totalMessages);
30 final messages = await client.fetchMessageSequence(
31 sequence,
32 fetchPreference: FetchPreference.envelope,
33 );
34
35 final result = messages.map((msg) {
36 return {
37 'uid': msg.uid ?? 0,
38 'subject': msg.decodeSubject() ?? '(no subject)',
39 'from': msg.from?.first.email ?? '',
40 'fromName': msg.from?.first.personalName ?? '',
41 'date': msg.decodeDate()?.toIso8601String() ?? '',
42 'isSeen': msg.isSeen,
43 };
44 }).toList();
45
46 await client.logout();
47 return result.reversed.toList(); // newest first
48 } catch (e) {
49 await client.logout().catchError((_) {});
50 throw Exception('IMAP error: $e');
51 }
52}

Pro tip: Pass `emailAddress` and `appPassword` in from secure App State rather than hardcoding them — this lets users enter credentials once at a settings screen without baking them into the action.

Expected result: The `fetchOutlookInbox` action appears in FlutterFlow's Custom Code panel without compile errors. When called on a real device with valid credentials, it returns a JSON list of message objects with subject, from, and date fields.

4

Write the 'Send Email' SMTP Custom Action

Add a second Custom Action named `sendOutlookEmail`. Set the Return Type to Boolean (true = sent, false/exception = failed). Add four String parameters: `emailAddress`, `appPassword`, `toAddress`, `subject`, and `body`. Outlook SMTP connection details: host = `smtp.office365.com`, port = `587`, security = `starttls` (STARTTLS upgrades a plain connection to TLS — different from IMAP's direct TLS on 993). The `enough_mail` `SmtpClient` handles STARTTLS automatically when you set `isSecure: false` and then call `startTls()`, or you can use `SmtpClient.connectToServer` with the appropriate security enum. Build a `MimeMessage` using enough_mail's `MessageBuilder`: set `from`, `to`, `subject`, and the text body. Call `smtpClient.sendMessage(message)` to deliver it. Return `true` on success. In FlutterFlow, add a compose screen with TextFields for recipient address, subject, and body. Wire the Send button's Action Flow: call the `sendOutlookEmail` custom action with the four parameters bound to the text fields and App State credentials. Chain a SnackBar action to show 'Email sent!' on `true` return or 'Send failed' on exception. Remember: this action won't run in web preview — test on device. If you're building a web-first app, the IMAP/SMTP approach is not ideal; consider the Microsoft Graph route on the `outlook-365-api` page instead.

custom_action.dart
1// FlutterFlow Custom Action: sendOutlookEmail
2// Parameters: emailAddress (String), appPassword (String),
3// toAddress (String), subject (String), body (String)
4// Return type: Boolean
5
6import 'package:enough_mail/enough_mail.dart';
7
8Future<bool> sendOutlookEmail(
9 String emailAddress,
10 String appPassword,
11 String toAddress,
12 String subject,
13 String body,
14) async {
15 final smtpClient = SmtpClient('outlook.office365.com', isLogEnabled: false);
16 try {
17 await smtpClient.connectToServer(
18 'smtp.office365.com',
19 587,
20 isSecure: false, // STARTTLS — not direct TLS
21 );
22 await smtpClient.ehlo();
23 await smtpClient.startTls();
24 await smtpClient.authenticate(
25 emailAddress,
26 appPassword,
27 AuthMechanism.plain,
28 );
29
30 final builder = MessageBuilder.prepareMultipartAlternativeMessage();
31 builder.from = [MailAddress('', emailAddress)];
32 builder.to = [MailAddress('', toAddress)];
33 builder.subject = subject;
34 builder.addTextPlain(body);
35 final mimeMessage = builder.buildMimeMessage();
36
37 final sendResponse = await smtpClient.sendMessage(mimeMessage);
38 await smtpClient.disconnect();
39 return sendResponse.isOkStatus;
40 } catch (e) {
41 await smtpClient.disconnect().catchError((_) {});
42 throw Exception('SMTP error: $e');
43 }
44}

Pro tip: The `AuthMechanism.plain` enum sends credentials over the already-encrypted STARTTLS connection — this is secure and is what most mail servers expect with App Passwords.

Expected result: The `sendOutlookEmail` action compiles without errors in FlutterFlow. Running it on a real device with valid credentials sends an email and returns `true`. The sent message appears in your Outlook Sent Items folder.

5

Bind the actions to your FlutterFlow UI and test on device

With both custom actions ready, wire them into your FlutterFlow screens. For the inbox screen, add a Page-Level action (or an On Page Load action) that calls `fetchOutlookInbox` with the emailAddress and appPassword from secure App State. Store the result in an App State variable of type JSON. Use a ListView widget on the page and set its data source to the App State JSON variable. Add a ListTile child that uses JSON path binding to show `$.subject`, `$.fromName`, and `$.date` for each item. To populate App State with the user's credentials securely, create a settings or login screen where the user enters their Outlook address and App Password. Store these in FlutterFlow's Secure App State (FlutterFlow uses flutter_secure_storage under the hood for Secure App State fields). Never store the App Password in a regular (non-secure) App State variable or in a public Firestore collection. For the compose screen, create a Page with three TextField widgets (to address, subject, body). Add a Send Button whose action calls `sendOutlookEmail` with the field values and the credential App State variables. Chain a Conditional action after the custom action: if the return value is `true`, show a SnackBar and navigate back to the inbox; if `false`, show an error SnackBar. To test: click 'Test Mode' in FlutterFlow (top right) to get a QR code or APK. Install it on a physical Android device. Enter your Outlook address and App Password on the settings screen. Navigate to the inbox — emails should populate the list. Try sending a test email to yourself. If you see no data in web preview (clicking Run), this is expected — IMAP/SMTP custom actions are disabled in FlutterFlow web preview. Device testing is required.

Pro tip: Add a loading spinner while the inbox action runs — IMAP connections take 2–5 seconds even on a fast connection, and a blank screen during that delay looks like a crash to users.

Expected result: On a real device, the inbox screen loads and shows the 20 most recent Outlook emails. Tapping the send button on the compose screen delivers an email via SMTP and the SnackBar confirms success. No emails are visible in FlutterFlow web preview — this is normal.

Common use cases

Personal productivity app that shows your Outlook inbox

A FlutterFlow productivity app fetches the 20 most recent emails from the user's Outlook inbox using the IMAP custom action and displays them in a scrollable ListView with sender name, subject, and preview. Tapping a row opens a detail screen showing the full message body.

FlutterFlow Prompt

Build an inbox screen in FlutterFlow that calls the 'fetchInbox' custom action on page load, stores the result in an App State list, and shows each email as a ListTile with sender, subject, and date.

Copy this prompt to try it in FlutterFlow

Notification app that sends email alerts from a company Outlook account

A FlutterFlow admin tool lets managers type a message and tap 'Send Alert' to email their team via a shared Outlook mailbox. The SMTP custom action sends the email with a fixed From address and BCC list defined in App State, then shows a confirmation SnackBar.

FlutterFlow Prompt

Add a 'Send Team Alert' screen to my FlutterFlow admin app: a TextField for the message body, a Send button that calls the 'sendEmail' custom action with our shared Outlook address, and a success SnackBar when done.

Copy this prompt to try it in FlutterFlow

Customer support app with email ticketing via Outlook

A FlutterFlow support app monitors a dedicated support@company.com Outlook inbox over IMAP, displays open tickets, and lets agents reply via SMTP. The inbox action polls for unread messages on a timer; the reply action composes an email with the original thread's Message-ID in the References header.

FlutterFlow Prompt

Create a support ticket inbox in FlutterFlow that polls the support Outlook mailbox every 60 seconds using the fetchInbox action and shows unread emails; tapping a ticket opens a reply compose form that calls sendEmail.

Copy this prompt to try it in FlutterFlow

Troubleshooting

IMAP or SMTP connection throws 'Authentication failed' or returns error code 535

Cause: Basic username-and-password login is blocked by Microsoft. Either MFA is not enabled (so App Passwords aren't available), the App Password was entered incorrectly, or the account's IMAP access has been disabled by an IT administrator.

Solution: Go to account.microsoft.com/security and confirm MFA is enabled. Regenerate a new App Password — copy the full 16-character string including spaces. If this is a work/school Microsoft 365 account managed by IT, check with your administrator whether IMAP access is allowed; if not, use the Microsoft Graph path from the `outlook-365-api` page.

Custom action works on Android but throws 'MissingPluginException' or does nothing on iOS

Cause: iOS simulator or a missing platform setup — enough_mail uses standard Dart socket APIs that are supported on iOS, but the FlutterFlow iOS build must include the correct network usage description in Info.plist.

Solution: In FlutterFlow, go to Settings & Integrations → App Settings → Permissions and ensure network access is not restricted. For iOS, check that 'App Transport Security' is not blocking non-HTTPS sockets (enough_mail uses TLS sockets, which should pass ATS). Test on a physical iOS device rather than the iOS simulator.

Action runs but returns an empty list even though the Outlook inbox has emails

Cause: The `MessageSequence.fromRange(start, total)` calculation may produce an out-of-bounds range if the mailbox reports 0 messages on first connect, or the selected folder may not be INBOX (some Outlook accounts have a different default folder name).

Solution: Add a null check on `mailbox.messagesExists` and ensure it's greater than 0 before building the sequence. Try explicitly selecting 'INBOX' with `client.selectMailboxByPath('INBOX')`. Also verify the IMAP login succeeds by logging the greeting response before selecting the folder.

typescript
1final mailbox = await client.selectMailboxByPath('INBOX');
2final total = mailbox.messagesExists;
3if (total == 0) return [];

SMTP error: 'Username and Password not accepted' even with App Password

Cause: The SMTP host or port is wrong, or STARTTLS is not being initiated before authentication. Using port 465 (direct SSL) instead of 587 (STARTTLS) requires a different connection sequence.

Solution: Use port 587 with STARTTLS as shown in the code above — call `ehlo()` then `startTls()` before `authenticate()`. Do not use port 465 (SSL) with the `SmtpClient` code pattern shown — that requires a different connection call. Double-check that `smtp.office365.com` is the host, not `outlook.office365.com` (that's the IMAP host).

Best practices

  • Always store the Outlook App Password in FlutterFlow's Secure App State (backed by flutter_secure_storage) — never in a regular App State variable, a Firestore document, or hard-coded in the Dart action.
  • Never hard-code the email address or password as string literals inside the Custom Action body — always pass them in as parameters from App State so users can enter their own credentials.
  • Test IMAP and SMTP custom actions on a real physical device or Test Mode APK — these actions are explicitly disabled in FlutterFlow's browser-based Run/Preview mode and will appear to do nothing in the editor.
  • Add a loading state App State variable (Boolean `isLoadingInbox`) and show a CircularProgressIndicator while the IMAP action runs — IMAP connections take 2–5 seconds, which feels like a hang without visual feedback.
  • Use enough_mail's `FetchPreference.envelope` for the inbox list (headers only — subject, from, date) and only fetch the full message body when the user taps a specific email to avoid unnecessary data transfer on mobile connections.
  • If your app targets Microsoft 365 work accounts, check with users' IT administrators whether IMAP is allowed before shipping — many corporate tenants disable IMAP as a security policy, and the IMAP approach silently fails with auth errors on those accounts.
  • Warn users that this integration uses an App Password tied to their personal Microsoft account — if they revoke it in Microsoft account settings, the app will stop working until they generate and re-enter a new one.

Alternatives

Frequently asked questions

Will this work with my work or school Microsoft 365 email account?

It depends on your organization's IT policy. Corporate Microsoft 365 tenants can disable IMAP access for security reasons, in which case this approach fails with authentication errors regardless of the App Password. Ask your IT administrator whether IMAP is enabled for your account. If it's disabled, use the Microsoft Graph route covered on the `outlook-365-api` page.

Why can't I use my normal Outlook password instead of an App Password?

Microsoft disabled basic authentication (username + regular password) for IMAP and SMTP connections starting in 2022 to improve security. App Passwords are a workaround specifically designed for apps that can't support OAuth2 interactively — they're separate from your main account password and can be revoked individually without changing your primary login.

Can I use this integration for web builds of my FlutterFlow app?

IMAP and SMTP custom actions use Dart socket connections that don't work inside a browser environment due to Web platform restrictions — browsers can't open raw TCP sockets for security reasons. If you need email functionality in a FlutterFlow web app, use the Microsoft Graph REST API (covered on the `outlook-365-api` page) via FlutterFlow's standard API Calls panel, which does work on web.

Is this the same as connecting to Outlook 365 / Microsoft Graph?

No — these are two distinct paths. This page covers the IMAP/SMTP approach using the `enough_mail` Dart package and an App Password, which works for consumer Outlook.com accounts and some Microsoft 365 plans with IMAP enabled, and requires no Azure portal. The `outlook-365-api` page covers the Microsoft Graph REST API with Azure app registration and OAuth2 token exchange, which is more complex but works for all Microsoft 365 account types including enterprise tenants.

How do I let multiple users connect their own Outlook accounts?

Create a settings screen where each user enters their Outlook email address and App Password. Store these in FlutterFlow's Secure App State (or in an encrypted Firestore field per user if you need them to persist across sessions and devices). Pass the credentials as parameters to the custom actions each time they're called. Never share a single hard-coded mailbox credential across all users.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Integrations are where projects stall

We wire FlutterFlow integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.