Using Azure with Flutter Apps: The Services I Actually Reach For
Using Azure with Flutter apps: App Service for APIs, Blob Storage uploads with SAS tokens, Azure Functions, Notification Hubs vs FCM, and Application Insights.

Azure has a couple of hundred services. A typical Flutter app needs about five of them. The hard part isn’t learning Azure; it’s ignoring most of it.
Firebase gets most of the attention in Flutter circles, and for good reason. But plenty of teams already run on Microsoft: their backend is .NET, their identity lives in Entra ID, their builds run in Azure DevOps. For those teams, Azure is the obvious home for the mobile backend too. I used Azure for cloud services and real-time sync on Nightingale, and I work with Azure DevOps pipelines day to day. This is the short list I keep coming back to.
The rule that comes before everything else
A Flutter app is a public client. Anything you put in it, whether in Dart code, a .env file bundled as an asset, or a native config file, can be pulled out of the binary by anyone with a free afternoon.
So the app never holds a storage account key, a database connection string, a Notification Hubs full-access key, or a Functions master key. It holds a user token and talks to your API. Your API holds the secrets, ideally fetched from Key Vault or replaced entirely by a managed identity. Every pattern below follows from this.
App Service for the API
For a straightforward REST API, Azure App Service is my default. You get a managed host for ASP.NET Core with deployment slots, autoscale, TLS, custom domains and health checks, without touching containers or Kubernetes.
The setup I use:
- A Linux App Service plan running the .NET runtime.
- A system-assigned managed identity on the web app, so it can reach Blob Storage, Key Vault and the database without stored credentials.
- App settings for configuration, with Key Vault references for anything secret.
- A staging slot. Deploy there, warm it up, then swap. Mobile apps can’t be rolled back once installed, but your API can be, and slots make that a quick operation.
I walk through the API itself in building a .NET API for a Flutter app. If you already need containers, Azure Container Apps is the next step up. For most mobile backends I’ve seen, App Service is plenty.
Blob Storage uploads with SAS tokens
User uploads like photos, documents and voice notes belong in Blob Storage, not in your database and not streamed through your API. The pattern that keeps keys off the device is a short-lived SAS (shared access signature) URL:
- The app asks your API for permission to upload.
- The API checks the user is allowed to, picks the blob name (never trust a client-supplied path), and returns a SAS URL that allows writing to that one blob for a few minutes.
- The app uploads directly to Blob Storage with that URL.
- The app tells the API the upload is done, and the API records it.
On the API side, I prefer a user delegation SAS, signed with a key obtained through the app’s managed identity rather than the account key:
public async Task<Uri> CreateUploadUrlAsync(string blobName)
{
var container = _blobService.GetBlobContainerClient("uploads");
var blob = container.GetBlobClient(blobName);
var startsOn = DateTimeOffset.UtcNow.AddMinutes(-5);
var expiresOn = DateTimeOffset.UtcNow.AddMinutes(10);
UserDelegationKey key =
await _blobService.GetUserDelegationKeyAsync(startsOn, expiresOn);
var sas = new BlobSasBuilder
{
BlobContainerName = container.Name,
BlobName = blobName,
Resource = "b",
StartsOn = startsOn,
ExpiresOn = expiresOn,
};
sas.SetPermissions(BlobSasPermissions.Create | BlobSasPermissions.Write);
return new BlobUriBuilder(blob.Uri)
{
Sas = sas.ToSasQueryParameters(key, _blobService.AccountName),
}.ToUri();
}
Here _blobService is a BlobServiceClient created with DefaultAzureCredential, and the managed identity needs a role that lets it request delegation keys and write blobs, such as Storage Blob Data Contributor. The starting time is backdated a few minutes to tolerate clock skew.
On the Flutter side, the upload is a single PUT. The one header people forget is x-ms-blob-type:
Future<void> uploadToBlob(Uri sasUrl, Uint8List bytes) async {
final response = await http.put(
sasUrl,
headers: {
'x-ms-blob-type': 'BlockBlob',
'Content-Type': 'image/jpeg',
},
body: bytes,
);
if (response.statusCode != 201) {
throw Exception('Upload failed: ${response.statusCode}');
}
}
For upload progress, the same request works with dio’s onSendProgress. For reading private files, the API hands out read-only SAS URLs the same way, or serves them through a CDN. Compress images before any of this happens; there’s no reason to push a full-resolution camera photo over mobile data.
Azure Functions for the glue
Functions are where I put work that doesn’t belong in a request/response cycle:
- Generating thumbnails when a blob lands in the uploads container (a Blob or Event Grid trigger).
- Nightly cleanup jobs on a timer trigger.
- Processing webhooks from payment providers, then queuing follow-up work.
- Sending notifications in response to queue messages.
I don’t build the whole mobile API out of Functions. Cold starts on the consumption plan are noticeable on a screen the user is staring at, and a pile of loosely related functions is harder to version and test than one API project. API in App Service, background work in Functions is a split that has served me well.
Notification Hubs vs FCM directly
Azure Notification Hubs sits in front of FCM and APNs and adds device registration management, tags, templates and broadcast to large audiences. Under the hood it still delivers through Google’s and Apple’s services, so your Flutter app still uses firebase_messaging (or native APNs) to get a device token.
My rule of thumb:
- Send through FCM directly if you’re already using Firebase in the app, your sends are mostly per-user, and your backend can store device tokens itself. The FCM HTTP v1 API handles both Android and iOS. It’s one less moving part. See push notifications in Flutter with FCM.
- Use Notification Hubs if you need tag-based audience targeting at scale, want to keep everything inside Azure for compliance or billing reasons, or have several apps sharing one notification backend.
Either way, sending happens from your backend. The app never holds credentials that can send pushes.
Identity: Entra ID and the B2C question
If your users are employees of an organization, Microsoft Entra ID is the answer, and MSAL is the standard way to sign in. There’s no first-party MSAL for Flutter, so you either use a community wrapper around the native MSAL libraries or a generic OAuth/OIDC package that uses the system browser with PKCE.
For consumer-facing apps, Azure AD B2C used to be the default recommendation. Microsoft has since moved new customers toward Entra External ID, so check the current status before you start a new project on B2C. Whichever you use, the Flutter side is the same: the system browser, PKCE, tokens in secure storage, and your API validating JWTs. None of that changes with the identity provider.
Application Insights: watch the backend
Application Insights, part of Azure Monitor, is excellent for the API and Functions: request rates, failures, slow dependencies, and distributed traces across services. Turn it on through OpenTelemetry in ASP.NET Core and you get most of that with very little code.
For the Flutter app itself, I still use a dedicated mobile crash reporter. Application Insights doesn’t offer an official Flutter SDK, and crash grouping, native symbolication and release health are what mobile tools are built for. The trick is joining the two: send a request ID header from the app on every call, log it in Application Insights, and attach it to breadcrumbs in your mobile crash reporting. When a user reports a failure, you can follow it from the device to the exact backend request.
Shipping it all with Azure DevOps
The last piece is a pipeline that builds the API, runs tests, deploys to the staging slot and swaps, alongside the pipelines that build and sign the Flutter app. Keeping both in one place means one set of variable groups, one set of service connections and one place to look when something breaks. That’s in Flutter CI/CD with Azure DevOps.
Takeaways
- The Flutter app holds user tokens, never Azure keys or connection strings.
- App Service with managed identity, Key Vault references and a staging slot covers most mobile APIs.
- Upload directly to Blob Storage with short-lived, single-blob SAS URLs issued by your API, preferably user delegation SAS.
- Use Functions for event-driven and scheduled work, not for the main request path.
- Send pushes via FCM directly unless you need Notification Hubs’ targeting at scale.
- Use Application Insights for the backend, a mobile crash reporter for the app, and a shared request ID to connect them.
If your team is on the Microsoft stack and needs a Flutter app that fits into it cleanly, get in touch.
Comments
Questions, corrections or your own experience — leave a comment below (GitHub sign-in).