I added a Flutter Web app to my phone's home screen, expecting a glorified bookmark. Instead, it launched full-screen — no browser chrome, no URL bar, just the app. It looked and felt like a native app.
If you've never seen this happen, it's a genuine "wait, what?" moment. Here's exactly how it works and why Flutter is uniquely well-positioned for this.
PWA: The Web Standard Flutter Rides On
The full-screen behavior isn't actually Flutter magic — it's a Progressive Web App (PWA) feature that any website can use. The key ingredient is a manifest.json file:
{
"name": "My Flutter App",
"short_name": "MyApp",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#2196F3"
}
The "display": "standalone" line is what removes the browser chrome. When a user adds a standalone PWA to their home screen, the OS launches it in its own window — no address bar, no tabs, no navigation buttons.
Flutter Web automatically generates this manifest. When you run flutter build web, the output includes a properly configured manifest.json with standalone display mode. You get PWA behavior for free.
What Flutter Web Actually Compiles To
Here's where it gets interesting. Flutter has three compilation targets, and they work very differently:
iOS/Android (native): Flutter compiles Dart to native ARM machine code via AOT (Ahead-Of-Time) compilation. The result is a genuine native binary — .ipa for iOS, .aab for Android. This runs directly on the CPU, with Flutter's own Skia-based rendering engine drawing every pixel.
Web: Flutter compiles Dart to JavaScript (or WebAssembly in newer versions). The rendering happens in one of two modes:
- HTML mode: Flutter creates regular DOM elements and CSS. Smaller bundle, better SEO, but some visual fidelity is lost.
-
CanvasKit mode: Flutter renders everything to a
<canvas>element using a WebAssembly port of Skia. Pixel-perfect with native, but larger download (~2MB for the CanvasKit runtime).
The PWA full-screen behavior works with both rendering modes because it's a browser-level feature, not a Flutter-level feature.
Why It Feels Like a Native App
Several things combine to create the native-app illusion:
1. Material Design / Cupertino widgets. Flutter's widget library matches platform design guidelines. On an iPhone, a Flutter Cupertino app has the same visual language as a native Swift app. The average user can't tell the difference.
2. 60fps animations. Flutter's rendering pipeline targets 60fps (or 120fps on ProMotion displays). Scroll physics, page transitions, and gesture responses feel native because they match the platform's expected behavior.
3. No "web-y" tells. Unlike a regular website added to the home screen, a Flutter PWA doesn't have text selection cursors on buttons, doesn't show tap highlights, and doesn't have the subtle differences that make web apps feel like web apps.
4. Service worker caching. Flutter Web generates a service worker that caches the app shell. After the first load, the app launches instantly — no network request needed. This removes the "loading... loading..." experience that makes web apps feel slow.
The Gotchas
Flutter Web PWAs are impressive but not perfect:
Bundle size. A minimal Flutter Web app with CanvasKit is 2-3MB. A minimal native iOS app is 1-2MB. For most use cases this doesn't matter (the assets dwarf the runtime), but it's worth knowing.
First load time. The initial download and compilation of the Dart-to-JS bundle takes 2-5 seconds on a mid-range phone. Subsequent loads are instant (service worker cache), but that first impression matters.
iOS PWA limitations. Apple has historically been hostile to PWAs. iOS Safari doesn't support push notifications for PWAs (as of 2025, this is partially available but buggy), and background processing is limited. If you need these features, you need a native build distributed through the App Store.
SEO. CanvasKit mode renders everything to a canvas — search engines can't index the content. If SEO matters, use HTML rendering mode or implement server-side rendering.
Google's Strategy: Flutter as the Universal UI Layer
It's worth stepping back and understanding why Google built Flutter this way. Google's bet is that the UI layer should be abstracted away from the platform.
Write your app once in Dart. Deploy to:
- iOS and Android (native binaries)
- Web (JavaScript/WebAssembly)
- Desktop (Windows, macOS, Linux)
- Embedded (cars, smart displays)
The fact that a Flutter Web app can masquerade as a native app via PWA is a feature, not a side effect. Google wants developers to stop thinking about platforms and start thinking about Flutter as the UI framework. Whether the user accesses your app through the App Store, a URL, or a home screen shortcut, the experience should be identical.
This is the same strategic play React Native attempted, but Flutter goes further by not depending on platform widgets at all — it draws every pixel itself, which is why cross-platform fidelity is higher.
When to Use Flutter Web PWA vs. Native Build
Use PWA when:
- You want instant distribution (no app store review)
- The app is a tool/dashboard (not a game or media-heavy app)
- You need to update frequently without app store delays
- Your users are on desktop AND mobile
Use native build when:
- You need push notifications (especially on iOS)
- You need background processing
- The app is performance-critical (3D rendering, real-time audio)
- App store presence is a business requirement (discovery, trust signal)
Use both when:
- The PWA is the "try before you install" experience
- The native app is the full-featured version
For internal tools — which is most of what I build — PWA is almost always the right choice. No app store. No provisioning profiles. No TestFlight. Just deploy to a URL and tell people to add it to their home screen.
I'm a semi-retired patent lawyer in Japan who started coding in December 2024. I build AI-powered search tools including PatentLLM (3.5M US patent search engine) and various local-LLM applications on a single RTX 5090.
Top comments (0)