Flutter 2 for web and desktop: is one codebase finally real?

Flutter 2 for web and desktop: is one codebase finally real?
By Marco Lopes · June 9, 2021 · Mobile & Flutter
Flutter 2 gives us a tempting proposition: build a product once, then bring it to phones, browsers and desktop computers. For teams balancing delivery speed with limited engineering capacity, that sounds like a useful shortcut.
At Kordspace, we work remotely across collaborative software, AI, Web3, hardware and design. We care about shared code because it can make products easier to evolve, not because a single repository is an achievement by itself.
As of June 2021, our answer is qualified: one codebase is increasingly realistic; one identical implementation everywhere is not. Flutter’s web support is now stable, while desktop support remains in beta. That distinction should shape our estimates, architecture and promises.
What Flutter 2 actually changes
Flutter 2, released in March, moves web support into the stable channel. That makes the browser a supported production target rather than an experiment attached to a mobile framework. Flutter 2.2, released in May, continues that work with improvements across the framework and tooling.
The release also brings sound null safety through Dart 2.12. For shared application code, clearer handling of nullable values helps us catch mistakes earlier. Migration still depends on our dependencies, so we should inspect package compatibility before treating an existing application as a straightforward upgrade.
Desktop is a different proposition. Windows, macOS and Linux support is available in beta, but we should not interpret availability as equal maturity across every platform integration or distribution workflow.
We can investigate desktop seriously today. We should still budget for platform-specific work and validate the exact capabilities our product needs before committing to a launch date.
What we mean by one codebase
The useful definition is a shared application foundation: domain models, validation, state management, networking and a substantial portion of the interface. It does not mean eliminating operating-system knowledge.
A shared Flutter project still contains platform-specific configuration. Signing, permissions, application identifiers and release packaging remain different. Browser URLs have no direct equivalent in a conventional mobile screen stack, and a desktop file picker has different expectations from a phone’s document flow.
We prefer to divide the product into three layers:
- Shared product logic: entities, rules, API clients and application state.
- Adaptive presentation: common components arranged for different sizes and input methods.
- Platform adapters: storage, authentication redirects, notifications, files and device integrations.
This keeps differences explicit instead of spreading platform checks through every widget. The aim is fewer duplicated decisions, not an arbitrary target for shared lines of code.
Web is strongest when the product behaves like an app
Flutter web makes the most sense for interactive experiences: internal tools, account areas, operational dashboards and companion applications. These products benefit from shared behaviour and often already require users to sign in.
A browser-based version of a mobile workflow can also reduce friction. We can send someone a link rather than asking them to install an application before completing a task.
We would be more cautious about a public, content-heavy website. Search visibility, document semantics, initial loading and familiar browser behaviour matter enormously for editorial pages and marketing content. Flutter’s own web positioning distinguishes app-centric experiences from document-centric websites.
For many products, the sensible architecture is therefore mixed: a conventional HTML website for discovery and content, with Flutter powering the interactive application. We do not need to force the homepage, blog and dashboard into the same rendering system to benefit from shared application code.
Measure web performance before choosing a renderer
Flutter web offers HTML and CanvasKit rendering options. Broadly, the HTML renderer uses browser technologies including HTML and CSS, while CanvasKit uses a WebAssembly-based version of Skia to render the interface.
CanvasKit can provide greater rendering consistency with other Flutter targets, but it introduces additional download weight. The HTML renderer has different trade-offs around payload and visual rendering. Automatic selection is useful, but it does not replace measurement.
We should build representative release versions and test both where appropriate. Our test should include a realistic first screen, actual fonts and images, and the slowest device and connection we intend to support.
We should also separate first-load performance from subsequent interaction. A smooth dashboard after startup can still feel disappointing if the initial experience is an unexplained wait. Asset discipline and a considered loading state remain product work.
Responsive layouts are not enough
A layout that stretches to fill a monitor is not necessarily a desktop interface. Large screens change how people compare information, navigate between tasks and use several windows at once.
We should adapt the information structure, not simply enlarge a mobile layout. A bottom navigation bar might become a navigation rail. A single-column detail flow might become a list-and-detail view. Dialogs should have sensible width limits rather than occupying all available space.
Input needs equal attention. We should test keyboard traversal, focus visibility, shortcuts, hover states, scrolling and text selection. Touch-friendly spacing should coexist with efficient mouse interaction.
Flutter gives us tools such as LayoutBuilder, focus widgets and shortcuts, but it does not make these design decisions for us. Breakpoints should follow the space a task needs, not assumptions about which device owns a particular screen width.
Desktop starts with an integration audit
Before promising a desktop release, we should list every capability outside ordinary rendering and networking. This often reveals the real scope more clearly than a successful demo build.
Does the application require a system tray, global shortcuts, serial communication, multiple windows, background execution or a particular authentication flow? Does it need installers, automatic updates or managed deployment inside an organisation?
Plugin support varies by platform. A package that works well on Android and iOS may lack a web or desktop implementation, or expose different capabilities on each target. We should review its platform declarations, maintenance activity and unresolved issues, then test it ourselves.
Where a capability is missing, we may need a native implementation behind a plugin or platform channel. That can still be worthwhile, but it belongs in the estimate. Shared UI does not make operating-system integration disappear.
Keep browser restrictions out of shared business logic
The browser is not simply another operating system with a different screen size. It has a sandbox, an origin model and restrictions around local files, credentials and network access.
Code built around dart:io cannot simply run unchanged on the web. Checking kIsWeb inside a method is not a substitute for keeping unsupported imports out of the web build. We should use abstractions and conditional imports where implementations genuinely differ.
For example, our application can depend on a document-storage interface. Mobile and desktop implementations might use local filesystem capabilities, while the browser implementation uses browser-supported storage or an explicit upload and download flow.
The same approach helps with authentication and networking. Redirect handling, cookie behaviour and CORS deserve early testing. We should never mistake CORS for authorisation: access control belongs on the server regardless of which client sends the request.
Treat URLs and accessibility as product requirements
On the web, navigation is part of the interface beyond our application window. People expect refresh, back, forward, bookmarks and shared links to work predictably.
We should decide which application states deserve addresses and test direct entry into those routes. Flutter’s Router API and Navigator 2.0 can support URL-aware navigation, but choosing a routing approach still requires deliberate design. Hosting configuration must also serve the application correctly when a user opens a nested route.
Accessibility needs its own acceptance criteria. Flutter provides semantics support, yet we still need to verify meaningful labels, reading order, focus behaviour, contrast and screen-reader interaction on our target platforms.
Neither a passing widget test nor a visually accurate screenshot proves these behaviours. We should test them in the browsers and assistive technologies our audience uses.
Choose cloud services independently of the UI framework
Flutter does not require a particular backend. At Kordspace, we generally prefer Google Cloud, but AWS and Azure can fit the product’s technical and organisational requirements just as well.
For a straightforward browser deployment, Firebase Hosting can serve the built web application. We can use Firebase Authentication where its flows fit, and place APIs or specialised processing on Cloud Run. Firestore may suit some data models, while relational workloads may be better served by a service such as Cloud SQL.
Those are choices, not a mandatory bundle. We should evaluate data access patterns, operational ownership, permissions and cost behaviour before selecting services.
AI inference, privileged Web3 operations and hardware-gateway access should follow the same trust boundaries. Secrets belong in controlled backend environments, not in a compiled client. Sharing a Flutter frontend does not change that rule.
A practical validation plan
We would not begin by rebuilding an entire product for every target. We would choose a thin vertical slice containing a meaningful user journey and the riskiest integration.
For a collaborative tool, that might include sign-in, opening a shared workspace, editing an item and observing an update from another session. We would run that slice on a phone, in a browser and on the intended desktop operating system.
Our short checklist would be:
- Confirm package support and null-safety readiness for every target.
- Measure release-mode startup and interaction on representative hardware.
- Test keyboard, pointer, touch and accessibility behaviour.
- Verify deep links, refresh, authentication and error recovery.
- Prove the hardest native or browser integration early.
- Rehearse building, signing and deploying each release artifact.
We should record limitations as product decisions: accept them, design around them or fund a platform-specific implementation.
Shared code still needs separate release confidence
One codebase can reduce duplicated implementation, but it does not reduce the supported platform matrix to a single test environment. Unit tests should cover shared rules, widget tests should cover interface states, and integration checks should exercise critical workflows on each shipping target.
Release processes also differ. Browser updates can reach users quickly, while installed applications have their own distribution and update paths. Backend changes should tolerate clients on different versions rather than assuming every user upgrades together.
We should also verify web caching and update behaviour deliberately. A successful deployment is not enough if an existing browser session continues using an incompatible application build.
So, is one codebase finally real?
For an app-shaped product with compatible dependencies and thoughtfully separated platform concerns, Flutter 2 makes a shared mobile-and-web codebase a credible production strategy today. Desktop is promising, but its beta status calls for narrower commitments and explicit technical validation.
The best outcome is not identical pixels everywhere. It is a coherent product with shared rules, reusable components and interfaces that respect each environment. We should adopt Flutter when that model fits the work, not merely because the target list is impressive.
If we can help turn your idea into a practical cross-platform roadmap, start a project with Kordspace. To explore how we combine product design, software engineering and cloud delivery, read our related services pages.
Conversation (0)
No comments yet. Start the conversation.
Join the conversation
Create a free member account to comment.