Flutter: Web and Desktop Multi-Platform Deployment

Write once, run everywhere — Web and Desktop take your code from mobile to the world.

📋 Prerequisites: You should have mastered the following first

1. What You Will Learn


2. A Story of Going Mobile-Only

(1) The Pain: Mobile User Growth Plateauing

Bob's ShopApp mobile DAU growth dropped from 50% per month to 5%. Market data shows: 40% of e-commerce orders come from desktop (with 2x higher average order value), and Web SEO can bring free traffic. But Bob only has one Flutter codebase — building Web/Desktop from scratch would take at least 3 months.

(2) Flutter Multi-Platform Solution

Flutter compiles the same codebase to Web and Desktop — you only need to handle layout adaptation and platform-specific features.

BASH
# Build for web
flutter build web --wasm

# Build for Windows
flutter build windows

# Build for macOS
flutter build macos
TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

(3) The Result: 0 Extra Development + 3 Days to Launch Web Version

Bob spent only 3 days on layout adaptation, and the Web version was live. The Desktop admin dashboard reused 80% of the mobile code and was completed in 2 weeks.


3. Multi-Platform Deployment Architecture

100%
graph TD
    SC[Single Codebase] --> WEB[Flutter Web]
    SC --> DSK[Desktop]
    WEB --> WASM[WASM / CanvasKit]
    WEB --> SEO2[URL Strategy / SEO]
    DSK --> WIN[Windows]
    DSK --> MAC[macOS]
    DSK --> LNX[Linux]
    DSK --> WM[window_manager]
TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

(1) Platform Differences Overview

Feature Mobile Web Desktop
Input Touch Mouse + Keyboard Mouse + Keyboard
Screen Fixed orientation Variable size Resizable window
File system Sandbox None Full access
Push FCM/APNs Notification API No native support
Resolution 1x-4x CSS pixels High DPI

4. Flutter Web Build and Optimization

(1) Web Rendering Engine Comparison

Engine First Load Render Performance Compatibility Recommended Use Case
CanvasKit Larger (2MB+) High (GPU) Good Production default
HTML renderer Smaller Low (DOM) Fair Lightweight apps
WASM (Skwasm) Medium Highest Flutter 3.22+ Future default

▶ Example

TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

: Web build configuration

BASH
# Build with CanvasKit (default)
flutter build web

# Build with WASM (Flutter 3.22+, recommended)
flutter build web --wasm

# Build with HTML renderer
flutter build web --renderer html

# Custom base href for subdirectory deployment
flutter build web --base-href "/shop/"
TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

(2) URL Strategy

▶ Example

TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

: Path URL strategy

DART
import 'package:flutter/material.dart';
import 'package:flutter_web_plugins/url_strategy.dart';

// ShopApp comes from project lib/app.dart

void main() {
  // Use path URL strategy (clean URLs: /product/42)
  usePathUrlStrategy();

  // OR use hash URL strategy (compatibility: /#/product/42)
  // useHashUrlStrategy();

  runApp(const ShopApp());
}
TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
Strategy URL Format Server Configuration SEO
Path /product/42 Requires fallback routing Good
Hash /#/product/42 No configuration needed Poor

(3) Web SEO Optimization

▶ Example

TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

: Meta tags and semantics

HTML
<!-- web/index.html -->
`<head>`
  <meta name="description" content="ShopApp - Million-level products, USD settlement">
  <meta name="keywords" content="ecommerce, shop, deals">
  <meta property="og:title" content="ShopApp">
  <meta property="og:description" content="Cross-platform ecommerce app">
  `<title>`ShopApp - Shop the World</title>
</head>
TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
💡 Tip: Flutter Web is an SPA — search engine crawlers have limited support for SPAs. For content-heavy pages, consider an SSR solution (like dart_frog) or pre-rendering.


5. Responsive Layout

▶ Example

TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

: LayoutBuilder adaptive breakpoints

DART
import 'package:flutter/material.dart';

// ResponsiveLayout: three-platform adaptive layout component

class ResponsiveLayout extends StatelessWidget {
  final Widget mobile;
  final Widget tablet;
  final Widget desktop;

  const ResponsiveLayout({
    super.key,
    required this.mobile,
    required this.tablet,
    required this.desktop,
  });

  static bool isMobile(BuildContext context) => MediaQuery.sizeOf(context).width < 600;
  static bool isTablet(BuildContext context) =>
      MediaQuery.sizeOf(context).width >= 600 && MediaQuery.sizeOf(context).width < 1024;
  static bool isDesktop(BuildContext context) => MediaQuery.sizeOf(context).width >= 1024;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth >= 1024) return desktop;
        if (constraints.maxWidth >= 600) return tablet;
        return mobile;
      },
    );
  }
}

// Usage
ResponsiveLayout(
  mobile: const MobileProductGrid(crossAxisCount: 2),
  tablet: const TabletProductGrid(crossAxisCount: 3),
  desktop: const DesktopProductLayout(), // Sidebar + grid
)
TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

▶ Example

TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

: ShopApp Web homepage layout

DART
import 'package:flutter/material.dart';

// CategorySidebar / CartPanel / productCards come from the ShopApp project

class WebHomePage extends StatelessWidget {
  const WebHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ShopApp'),
        actions: [
          // Desktop-only: search bar in AppBar
          if (ResponsiveLayout.isDesktop(context))
            const SizedBox(width: 300, child: SearchBar()),
          IconButton(icon: const Icon(Icons.shopping_cart), onPressed: () {}),
          IconButton(icon: const Icon(Icons.person), onPressed: () {}),
        ],
      ),
      body: ResponsiveLayout(
        mobile: _buildMobileLayout(),
        tablet: _buildTabletLayout(),
        desktop: _buildDesktopLayout(),
      ),
    );
  }

  Widget _buildDesktopLayout() {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        // Sidebar categories
        SizedBox(
          width: 240,
          child: CategorySidebar(),
        ),
        // Main content
        Expanded(
          child: GridView.count(
            crossAxisCount: 4,
            children: productCards,
          ),
        ),
        // Cart panel (desktop only)
        SizedBox(
          width: 300,
          child: CartPanel(),
        ),
      ],
    );
  }
}
TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

6. Desktop Adaptation

▶ Example

TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

: window_manager for window management

DART
import 'package:flutter/material.dart';
import 'package:window_manager/window_manager.dart';

// Dependency: window_manager: ^0.3.0 (add to dependencies)
// ShopApp comes from project lib/app.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await windowManager.ensureInitialized();

  WindowOptions windowOptions = const WindowOptions(
    size: Size(1200, 800),
    minimumSize: Size(800, 600),
    center: true,
    backgroundColor: Colors.transparent,
    skipTaskbar: false,
    titleBarStyle: TitleBarStyle.normal,
    title: 'ShopApp',
  );

  windowManager.waitUntilReadyToShow(windowOptions, () async {
    await windowManager.show();
    await windowManager.focus();
  });

  runApp(const ShopApp());
}
TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

▶ Example

TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

: Keyboard shortcuts

DART
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

// Dependency: go_router: ^13.0.0 (add to dependencies)
// showSearch / router come from the ShopApp project

class ShortcutWrapper extends StatelessWidget {
  final Widget child;
  const ShortcutWrapper({super.key, required this.child});

  @override
  Widget build(BuildContext context) {
    return Shortcuts(
      shortcuts: {
        LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyF): const Intent(#search),
        LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyK): const Intent(#cart),
        LogicalKeySet(LogicalKeyboardKey.escape): const Intent(#back),
      },
      child: Actions(
        actions: {
          Intent(#search): CallbackAction(onInvoke: (_) => showSearch(context)),
          Intent(#cart): CallbackAction(onInvoke: (_) => context.go('/cart')),
          Intent(#back): CallbackAction(onInvoke: (_) => Navigator.maybePop(context)),
        },
        child: child,
      ),
    );
  }
}
TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

7. Platform Difference Handling

▶ Example

TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

: Conditional imports and platform detection

DART
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:io' show Platform;

// Note: dart:io is unavailable on Web — real projects need conditional imports
// NativePayButton / CreditCardOption / PayPalOption come from the ShopApp project

class PlatformHelper {
  static bool get isWeb => kIsWeb;
  static bool get isMobile => !kIsWeb && (Platform.isAndroid || Platform.isIOS);
  static bool get isDesktop => !kIsWeb && (Platform.isWindows || Platform.isMacOS || Platform.isLinux);
  static bool get isApple => !kIsWeb && (Platform.isIOS || Platform.isMacOS);
  static bool get supportsNativePay => isApple || (!kIsWeb && Platform.isAndroid);
}

// Usage in widget
Widget buildPaymentOptions() {
  return Column(children: [
    if (PlatformHelper.supportsNativePay)
      NativePayButton(),
    CreditCardOption(),
    if (!PlatformHelper.isWeb)
      PayPalOption(), // PayPal SDK not available on web
  ]);
}
TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

8. Complete Example: ShopApp Multi-Platform Entry

DART
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:window_manager/window_manager.dart';
import 'dart:io' show Platform;

// Dependencies: flutter_riverpod: ^2.0.0, firebase_core: ^2.0.0, window_manager: ^0.3.0
// DefaultFirebaseOptions / initHive / PlatformHelper / ShortcutWrapper / router / ShopApp come from the ShopApp project
// themeModeNotifierProvider / shopAppLightTheme / shopAppDarkTheme come from the ShopApp theme module

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Platform-specific initialization
  if (PlatformHelper.isDesktop) {
    await _initDesktop();
  }

  // Common initialization
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  await initHive();

  runApp(ProviderScope(child: const ShopApp()));
}

Future<void> _initDesktop() async {
  await windowManager.ensureInitialized();
  const windowOptions = WindowOptions(
    size: Size(1200, 800),
    minimumSize: Size(800, 600),
    title: 'ShopApp Admin',
  );
  windowManager.waitUntilReadyToShow(windowOptions, () async {
    await windowManager.show();
  });
}

class ShopApp extends ConsumerWidget {
  const ShopApp({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final themeMode = ref.watch(themeModeNotifierProvider);
    return MaterialApp.router(
      title: PlatformHelper.isDesktop ? 'ShopApp Admin' : 'ShopApp',
      theme: shopAppLightTheme,
      darkTheme: shopAppDarkTheme,
      themeMode: themeMode,
      routerConfig: router,
      builder: (context, child) {
        // Desktop: add shortcuts
        if (PlatformHelper.isDesktop) {
          return ShortcutWrapper(child: child!);
        }
        return child!;
      },
    );
  }
}
TEXT
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; please use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.

❓ FAQ

Q Is Flutter Web suitable for SEO sites?
A Not really. Flutter Web is an SPA — search engine crawlers struggle to index it. For content-heavy SEO sites, use Nuxt/Next.js instead. Flutter Web is better suited for tool-like applications.
Q What's the difference between WASM and CanvasKit?
A CanvasKit renders using JS + WebGL; WASM uses C++ compiled WebAssembly for higher performance. Flutter 3.22+ recommends WASM.
Q Can Desktop apps be published to the Mac App Store?
A Yes. You need an Apple Developer account, use Xcode Archive to package as DMG/PKG, and submit via App Store Connect for review.
Q How do I share code between Web and Mobile?
A 90% of the code is fully shared. Only platform-specific features (payment/push/file system) need conditional logic. Use PlatformHelper to encapsulate platform differences.
Q What are the best breakpoints for responsive layout?
A Mobile < 600px, Tablet 600-1024px, Desktop > 1024px. Refer to the Material Design breakpoint specification.
Q How does Desktop app performance compare to native?
A Flutter Desktop performance is close to native (GPU rendering), but startup is slower than native (needs to load the engine). It outperforms Electron in complex animation scenarios.

📖 Summary


📝 Exercises

  1. Basic (Difficulty ⭐): Build a Web version with flutter build web, run it in a browser, and configure Path URL strategy.
  2. Intermediate (Difficulty ⭐⭐): Implement ResponsiveLayout with three-platform adaptation: mobile 2-column grid, tablet 3-column, desktop sidebar + 4-column.
  3. Challenge (Difficulty ⭐⭐⭐): Implement a complete ShopApp multi-platform version: Web responsive e-commerce frontend + Desktop admin dashboard (window_manager + keyboard shortcuts + menu bar), sharing 90% code.

← Previous Lesson | Next Lesson →

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏