Skip to main content

Command Palette

Search for a command to run...

Flutter Cross-Platform Fix: Solving the dart:html Error the Right Way

Published
4 min read
D

👨‍💻 Software Engineer | 💡 Kotlin Expert | 📱 Android Enthusiast

Previously SDE-III at DhiWise, I’m a Kotlin-focused developer passionate about building scalable, modern software-primarily on Android, but also exploring AI 🤖 and backend technologies. I use this space to share practical insights, clean code practices, and thoughts on the future of tech 🚀

If you’ve ever included some web-only code in your Flutter project, it might work perfectly in Chrome-until you try running it on your phone or building an APK.

Suddenly, Flutter throws errors like these:

Error: Dart library 'dart:html' is not available on this platform.
import 'dart:html' as html;
       ^

Error: Dart library 'dart:js_util' is not available on this platform.
import 'dart:js_util' as js_util;

Error: Dart library 'dart:js' is not available on this platform.
import 'dart:js' as js;

💥 Just like that, your mobile build is broken.

So what’s going on, and how do you fix it? Let’s break it down.


❌ Why This Happens

The root cause:

  • dart:html, dart:js, and dart:js_util are web-only libraries.

  • They simply don’t exist on mobile or desktop.

Why?

  • Mobile doesn’t have a DOM (no HTML elements).

  • Mobile doesn’t run JavaScript in a browser context.

  • Each platform uses its own APIs (Platform Channels, not Web APIs).

So when Flutter compiles for iOS/Android, it looks for those libraries… and can’t find them.


🛠 Two Proven Fixes (Pick One)

When you’re stuck between web and mobile, you have two safe routes forward:

  1. Conditional imports with wrapper classes (zero dependencies, more control)

  2. Use the universal_html package (simpler, one-liner fix for most teams)

Let’s dive into both.

🔹 Approach 1: Conditional Imports (Maximum Control)

Best for teams that want zero external dependencies and full control of platform-specific code.

Step 1: Create Web implementation

// File: lib/utils/web_utils_web.dart

import 'dart:html' as html;

class WebUtils {
  static String getBrowserInfo() => html.window.navigator.userAgent;
  static String getCurrentUrl() => html.window.location.href;
  static void saveToLocalStorage(String k, String v) => html.window.localStorage[k] = v;
  static String? getFromLocalStorage(String k) => html.window.localStorage[k];
}

Step 2: Create Mobile fallback

// File: lib/utils/web_utils_mobile.dart

class WebUtils {
  static String getBrowserInfo() => 'Not available on mobile';
  static String getCurrentUrl() => 'Not available on mobile';
  static void saveToLocalStorage(String k, String v) {}
  static String? getFromLocalStorage(String k) => null;
}

Step 3: Add Conditional Export

// File: lib/utils/web_utils.dart

export 'web_utils_stub.dart'
    if (dart.library.html) 'web_utils_web.dart'
    if (dart.library.io) 'web_utils_mobile.dart';
// File: lib/utils/web_utils_stub.dart

class WebUtils {
  static String getBrowserInfo() => 'Unsupported';
  static String getCurrentUrl() => 'Unsupported';
  static void saveToLocalStorage(String k, String v) {}
  static String? getFromLocalStorage(String k) => null;
}

Step 4: Use it anywhere

import 'utils/web_utils.dart';

Text('Browser: ${WebUtils.getBrowserInfo()}')

✅ Done! Flutter automatically picks the correct file depending on platform.

🔹 Approach 2: universal_html (Simplest Fix)

If you don’t want extra boilerplate, just add one package:

Step 1: Add dependency

dependencies:
  universal_html: ^2.2.4

Step 2: Replace imports

// Old ❌
import 'dart:html' as html;
import 'dart:js_util' as js_util;
import 'dart:js' as js;

// New ✅
import 'package:universal_html/html.dart' as html;
import 'package:universal_html/js_util.dart' as js_util;
import 'package:universal_html/js.dart' as js;

Step 3: Wrap in try/catch for safety

static String getBrowserInfo() {
  try {
    return html.window.navigator.userAgent;
  } catch (_) {
    return 'Not running on web';
  }
}

Now the same code runs everywhere — no more crashes on mobile builds.

⚖️ Which One Should You Use?

  • Go with Conditional Imports if:

  • You want no dependencies

  • You need platform-optimized implementations

  • Your app has strict enterprise requirements

  • Go with Universal HTML if:

  • You want the fastest, easiest fix

  • You’re working on a standard Flutter app

  • You value simplicity over boilerplate

👉 For 90% of projects, universal_html is enough.

💡 Best Practices for Cross-Platform Flutter

  1. Always test early on mobile + web
    Don’t wait until release day.
    flutter run -d chrome flutter run flutter build apk

  2. Guard your code with try/catch
    Avoid crashes if the feature isn’t supported.

  3. Use kIsWeb for quick checksimport 'package:flutter/foundation.dart'; if (kIsWeb) { ... }

  4. Document platform differences
    Your future self (and teammates) will thank you.

🎯 Final Takeaway

dart:html works great… but only on web.
If you’re building cross-platform Flutter apps, you have two safe paths:

  • Conditional imports → More control, more setup

  • Universal HTML → Less effort, works out of the box


🚀 If you found this guide helpful, consider bookmarking it or dropping a reaction to support more in-depth tutorials.

🔔 Follow @themodularmindset to stay updated with the next articles.