skip to content

Files, Processes & Sockets

dart:io reads and writes files and directories, runs processes, and serves or calls HTTP over sockets on native targets. Interviewers ask about sync versus async file APIs and why the web lacks it.

part ofDartoverview, primer and where to startread it →
on this pageshow

explore

questions

6

Why does a Flutter web build fail when code calls dart:io's Platform.isAndroid, and how do you branch between web and native code safely?

level: middleimportance: must knowfreq 55%

answer

  1. a browser has no files, processes or sockets
  2. compiles, then fails at runtime
  3. kIsWeb is a compile-time constant
  4. defaultTargetPlatform works everywhere
  5. if (dart.library.io) conditional import

basics

~20 s

dart:io wraps operating-system files, processes and sockets that a browser does not expose, so on the web its APIs, including Platform.isAndroid, are unsupported and fail at runtime. Check kIsWeb first, use defaultTargetPlatform, or split code with conditional imports.

solid answer

~40 s

dart.dev states that only non-web Flutter apps, command-line scripts and servers can use `dart:io`. The web compilers link a stub of the library, so code importing it may compile, but calls such as `Platform.isAndroid` or `File(...).readAsString()` fail at runtime in the browser. Three tools fix it. `kIsWeb` from `package:flutter/foundation.dart` is a `const bool` defined as `bool.fromEnvironment('dart.library.js_interop')`, so `if (kIsWeb) ... else if (Platform.isAndroid)` short-circuits before touching `dart:io`. `defaultTargetPlatform` returns a `TargetPlatform` on every platform, including a guess from the browser on the web, so UI adaptation rarely needs `Platform` at all. For real native-only work, a conditional import such as `import 'stub.dart' if (dart.library.io) 'io_impl.dart' if (dart.library.js_interop) 'web_impl.dart';` keeps `dart:io` out of the web build entirely.

code

dart · 13 lines
dart
import 'dart:io' show Platform;

import 'package:flutter/foundation.dart';

String storageHint() {
  if (kIsWeb) return 'browser storage';
  if (Platform.isAndroid || Platform.isIOS) return 'app sandbox';
  return 'user home directory';
}

bool useCupertinoStyle() =>
    defaultTargetPlatform == TargetPlatform.iOS ||
    defaultTargetPlatform == TargetPlatform.macOS;

go deeper

for a junior

Recall that dart:io does not work in a browser and that kIsWeb must be checked before Platform.

for a middle

Explain why the failure appears at runtime, how kIsWeb and defaultTargetPlatform differ, and how a conditional import selects an implementation.

for a senior

Design native and web implementations behind one interface, and audit dependencies for dart:io use before shipping a web target.

for a principal

Decide how much platform-specific code a multi-target app tolerates and where the platform seam sits in the codebase.

## Why dart:io has nothing to offer a browser `dart:io` is Dart's binding to the **operating system**: files and directories, child processes, raw TCP sockets, an HTTP client and server, stdin and stdout, and the `Platform` class that reports the OS, environment variables and processor count. A web page runs in a browser sandbox that exposes none of those things - there is no file system path to open, no process to spawn and no raw socket to bind. The dart.dev library guide is explicit: only non-web Flutter apps, command-line scripts and servers can import and use `dart:io`, not web apps. In the SDK's library configuration, the web compilers map `dart:io` to a patched stub and mark it unavailable for conditional imports. The practical effect in a Flutter web app is that code which imports `dart:io` can still compile, but **calls into it fail at runtime**. `Platform.isAndroid` is the classic example: developers write it once to pick an icon or a padding and discover it only when the web build throws on that line. ## Three tools, three jobs | Tool | Where it comes from | What it answers | |---|---|---| | `kIsWeb` | `package:flutter/foundation.dart` | Is this build compiled for the web? | | `defaultTargetPlatform` | `package:flutter/foundation.dart` | Which platform's conventions should the UI follow? | | conditional import | Dart language | Which implementation file should this build compile? | ### kIsWeb `kIsWeb` is declared as `const bool kIsWeb = bool.fromEnvironment('dart.library.js_interop');`. Being a compile-time constant, a branch on it is removed by the compiler in builds where it is false. Order matters: in `if (kIsWeb) { ... } else if (Platform.isAndroid) { ... }`, the `Platform` getter is never evaluated on the web. ### defaultTargetPlatform Flutter's `defaultTargetPlatform` returns a `TargetPlatform` value on **every** platform. On native targets it is derived from `Platform.isAndroid`, `Platform.isIOS` and friends; on the web it comes from the browser's reported operating system. Tests and debugging can override it through `debugDefaultTargetPlatformOverride`. For choosing Material versus Cupertino behaviour, scroll physics or keyboard shortcuts, it is the right API and needs no `dart:io` at all. ### Conditional imports When you genuinely need `File` or `Process` on native and a different mechanism on the web, split the implementation: 1. Write a stub file with the shared function signatures. 2. Write an implementation that imports `dart:io`. 3. Write a web implementation using `dart:js_interop` or `package:web`. 4. Select one with `import 'stub.dart' if (dart.library.io) 'io_impl.dart' if (dart.library.js_interop) 'web_impl.dart';`. Flutter's own foundation library does exactly this: its platform file imports an IO implementation, or the web one when `dart.library.js_interop` is available. Prefer `dart.library.js_interop` over the older `dart.library.html` condition, which is not available when compiling to WebAssembly. ## Other consequences on the web - `Platform.environment` has no meaning in a browser; configuration comes from `--dart-define` values read with `String.fromEnvironment`. - Packages that depend on `dart:io` do not support the web platform; check a package's supported platforms before adding it. ## Common mistakes - Checking `Platform.isAndroid` before `kIsWeb` in the same condition. - Assuming a successful web compile proves `dart:io` calls are safe. - Using `Platform` just to choose a UI style when `defaultTargetPlatform` or `Theme.of(context).platform` answers it. - Using the `dart.library.html` condition, which misses Wasm builds.

  • Why is kIsWeb defined with bool.fromEnvironment rather than a runtime check?
    `bool.fromEnvironment('dart.library.js_interop')` is evaluated at compile time, so `kIsWeb` is a `const`. The compiler can drop the dead branch in each build, and the check costs nothing at runtime.
  • Why prefer dart.library.js_interop over dart.library.html in a conditional import?
    `dart:html` is a legacy library that is not available when compiling to WebAssembly, so a `dart.library.html` condition is false in a Wasm build and the wrong file is chosen. `dart.library.js_interop` is true for both JavaScript and Wasm web builds.

saying these in an interview costs you the question

  • Thinks the web compiler rejects any file that imports dart:io
  • Checks Platform.isAndroid before kIsWeb in the same expression
  • Uses Platform to pick a UI style instead of defaultTargetPlatform
  • Believes defaultTargetPlatform throws or returns null on the web
  • Uses dart.library.html to detect the web in a Wasm build
open as a page

In Dart's dart:io, what is the difference between File.readAsString() and File.readAsStringSync(), and when is the Sync variant acceptable?

level: juniorimportance: should knowfreq 45%

basics

~20 s

readAsString() returns a Future<String> so the isolate keeps handling events while the file is read; readAsStringSync() returns the String directly and blocks the isolate until the read finishes. Sync suits short scripts, not a Flutter UI isolate.

open as a page

In Dart's dart:io, when should you read a file with File.openRead() rather than readAsString(), and how does File.openWrite() report errors?

level: middleimportance: should knowfreq 35%

basics

~10 s

Use File.openRead() for large files or incremental processing: it returns a Stream<List<int>> of byte chunks instead of loading everything. File.openWrite() returns an IOSink; write errors surface on its flush(), close() and done futures.

open as a page

In a Dart command-line tool that shells out to git, when do you use Process.run versus Process.start, and why can a started child process hang?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Process.run waits for the child to exit and returns a ProcessResult with exitCode, stdout and stderr. Process.start returns a live Process with stdin, stdout and stderr streams; it hangs if you never drain stdout or stderr.

open as a page

In a Dart command-line program, why is assigning dart:io's top-level exitCode usually safer than calling exit(1) to report failure?

level: middleimportance: nice to knowfreq 22%

basics

~20 s

exit() ends the Dart VM immediately without waiting for asynchronous work or running finally blocks, so buffered output and pending writes can be lost. Setting exitCode records the status and lets the program finish normally, flushing and closing first.

open as a page

In Dart's dart:io, what are the steps of a GET request with HttpClient, and why should a command-line script call close() on the client afterwards?

level: middleimportance: nice to knowfreq 20%

basics

~20 s

getUrl() returns an HttpClientRequest; calling close() on it sends the request and returns an HttpClientResponse, a byte stream you decode. HttpClient keeps idle keep-alive connections for idleTimeout (15 s), so client.close() lets a script exit promptly.

open as a page