Back to Blog
Tutorial8 min read2026-07-17

How to Deploy a Flutter Web App in 2026 (Static Hosting Done Right)

Flutter isn't just for phones — `flutter build web` gives you a static bundle you can host anywhere. Here's how to deploy it on PandaStack with correct routing, caching, and a CanvasKit vs HTML renderer decision.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

Flutter's web target has quietly gotten good. flutter build web spits out a folder of HTML, JS, and WASM that runs the same widget tree you ship to iOS and Android. It's a static bundle — which means hosting it is genuinely simple, *if* you get three things right: the renderer choice, single-page-app routing, and cache headers. I run PandaStack, so I'll deploy one here, but the build steps are the same wherever you host.

Step 1: Build for web

flutter build web --release

The output lands in build/web/. That directory is your entire deployable — index.html, a main.dart.js, and asset files. No server required.

The renderer decision (do this consciously)

Flutter web can render two ways, and the default has shifted across versions, so pick on purpose:

  • CanvasKit (WASM-based Skia): pixel-identical to mobile, great for graphics-heavy or canvas-drawing apps, but ships a larger initial payload (the Skia WASM).
  • HTML renderer: lighter, faster first paint, better for text-heavy content sites, but less fidelity for complex custom painting.

Force one explicitly if you care:

flutter build web --release --web-renderer canvaskit
# or
flutter build web --release --web-renderer html

Rule of thumb: content/marketing app → HTML renderer for speed; interactive/graphical app → CanvasKit for fidelity. The official guidance lives at https://docs.flutter.dev/platform-integration/web/renderers.

Step 2: Fix SPA routing before you deploy

Flutter web uses client-side routing. If a user hard-refreshes on /dashboard, the host must still serve index.html or they get a 404. On PandaStack's static site hosting, SPA fallback is handled for you — every unknown path serves index.html, so flutter's router picks it up. No .htaccess, no Nginx try_files, nothing to configure.

One caveat: use path URL strategy, not the default hash strategy, for clean URLs. In main.dart:

import 'package:flutter_web_plugins/url_strategy.dart';

void main() {
  usePathUrlStrategy();
  runApp(const MyApp());
}

Step 3: Deploy on PandaStack

Two ways.

Dashboard: https://dashboard.pandastack.io → New Site → Static → connect your repo. Set:

SettingValue
Build commandflutter build web --release
Output directorybuild/web

If Flutter isn't in the default build image, commit a tiny build script or use a Dockerfile-based container build; but for most repos the static build with a specified command works.

CLI:

npm install -g @pandastack/cli
panda login
flutter build web --release
panda deploy --type static --dir build/web

Either way, every push to your default branch rebuilds and redeploys. SSL on the *.pandastack.io domain (and any custom domain you add) is automatic.

Step 4: Caching — the one manual optimization worth doing

Flutter web fingerprints most assets, but index.html and the service worker must not be cached long, or users get stuck on a stale version after you deploy. The good news: flutter build already writes a flutter_service_worker.js that manages versioned asset caching. Your job is just to make sure the HTML entry point is served fresh. PandaStack serves static assets with sensible defaults and gzip/Brotli compression out of the box, so in practice you deploy and it works — but if you see users on old builds, hard-refresh clears it and the service worker updates on next visit.

Step 5: Custom domain

App → Domains → Add Domain → add a CNAME at your DNS provider pointing to the shown hostname. SSL provisions automatically once DNS resolves.

Gotchas I've hit

  • Big initial download. CanvasKit apps can feel heavy on first load. Enable --release (never ship a debug build), consider the HTML renderer for content apps, and lazy-load heavy screens.
  • CORS on your API. If your Flutter web app calls a separate API, that API must send CORS headers — the browser enforces this even though your mobile build never cared.
  • base href. If you deploy under a subpath, set --base-href /subpath/. Deploying at the domain root (the normal case) needs nothing.

Honest note

Flutter web is excellent for app-like experiences and dashboards. For a pure content/SEO site, a framework that server-renders HTML will out-SEO a Flutter SPA — Flutter web content is JS-rendered, and while crawlers execute JS, HTML-first frameworks still have the edge. Choose Flutter web when the *interactivity* is the point.

Wrap-up

flutter build web → static folder → PandaStack static hosting with automatic SPA fallback, SSL, and compression. Pick your renderer deliberately, use the path URL strategy, and you're live.

Docs: https://docs.pandastack.io. Start free: https://dashboard.pandastack.io.

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also