IO #

dart:io is Dart’s built-in library providing access to operating system input/output — files, directories, sockets, HTTP, processes, and platform information. This article focuses on the dart:io components not yet covered in depth in the advanced I/O section: Dart’s built-in HttpClient for making HTTP requests directly, InternetAddress for DNS lookups, NetworkInterface for network information, complete stdin/stdout/stderr, Platform for system information, and process management.

dart:io is only available in the Dart VM — it can’t be used in code compiled to JavaScript (web). For Flutter web, use dart:html or the portable http package.

An Overview of dart:io #

flowchart LR
    DIO["dart:io"] --> FILE["File & Directory\nFile, Directory, FileSystemEntity"]
    DIO --> NET["Networking\nSocket, HttpClient, HttpServer\nSecureSocket, WebSocket"]
    DIO --> PROC["Process\nProcess, ProcessSignal\nProcessResult"]
    DIO --> SYS["System\nPlatform, stdin, stdout\nstderr, exit, ProcessInfo"]
    DIO --> DNS["DNS & Network\nInternetAddress\nNetworkInterface"]

HttpClient — Dart’s Built-in HTTP Requests #

dart:io includes HttpClient — a lower-level HTTP client than the http package, giving full control over every aspect of a request:

import 'dart:io';
import 'dart:convert';

Future<void> contohHttpClient() async {
  final client = HttpClient();

  try {
    // GET request
    final request = await client.getUrl(
      Uri.parse('https://api.example.com/produk'),
    );

    // Add headers
    request.headers
      ..set('Authorization', 'Bearer mytoken')
      ..set('Accept', 'application/json')
      ..set(HttpHeaders.contentTypeHeader, 'application/json');

    // Send the request and wait for the response
    final response = await request.close();

    // Read the response body
    final body = await response.transform(utf8.decoder).join();

    print('Status: ${response.statusCode}');
    print('Content-Type: ${response.headers.contentType}');

    if (response.statusCode == HttpStatus.ok) {
      final data = jsonDecode(body);
      print(data);
    }

  } finally {
    client.close(); // close the client when done
  }
}

POST and Uploading Data #

import 'dart:io';
import 'dart:convert';

Future<Map<String, dynamic>> postData(
  String url,
  Map<String, dynamic> data,
) async {
  final client = HttpClient();

  try {
    final request = await client.postUrl(Uri.parse(url));
    request.headers
      ..contentType = ContentType.json
      ..set('Authorization', 'Bearer token');

    // Write the request body
    final body = jsonEncode(data);
    request.contentLength = utf8.encode(body).length;
    request.write(body);

    final response = await request.close();
    final responseBody = await response.transform(utf8.decoder).join();

    if (response.statusCode >= 400) {
      throw HttpException(
        'Request failed: ${response.statusCode}',
        uri: Uri.parse(url),
      );
    }

    return jsonDecode(responseBody) as Map<String, dynamic>;
  } finally {
    client.close();
  }
}

HttpClient Configuration #

final client = HttpClient()
  // Connection timeout
  ..connectionTimeout = Duration(seconds: 10)
  // Maximum connections per host (default: 6)
  ..maxConnectionsPerHost = 10
  // Follow redirects automatically (default: true)
  ..autoUncompress = true
  // Proxy configuration
  ..findProxy = (uri) => 'PROXY proxy.example.com:8080'
  // SSL certificate verification — DON'T disable in production!
  ..badCertificateCallback =
      (X509Certificate cert, String host, int port) => false; // false = reject

HttpClient vs the http Package #

// dart:io HttpClient — low-level, more control
final request = await client.getUrl(uri);
request.headers.set('X-Custom', 'value');
final response = await request.close();
final body = await response.transform(utf8.decoder).join();

// The http package — simpler, more idiomatic
import 'package:http/http.dart' as http;
final response = await http.get(uri, headers: {'X-Custom': 'value'});
final body = response.body;

// Use the http package unless you need very specific control

InternetAddress — DNS and IP Addresses #

import 'dart:io';

Future<void> contohInternetAddress() async {
  // DNS lookup — resolve a hostname to IP addresses
  final addresses = await InternetAddress.lookup('dart.dev');
  for (final addr in addresses) {
    print('${addr.host}: ${addr.address} (${addr.type.name})');
    // dart.dev: 35.219.196.5 (InternetAddressType.IPv4)
  }

  // Reverse lookup — IP to hostname
  final reverseResult = await InternetAddress('8.8.8.8').reverse();
  print('8.8.8.8 → ${reverseResult.host}'); // dns.google

  // Address types
  final ipv4 = InternetAddress('192.168.1.1');
  final ipv6 = InternetAddress('::1');
  final loopback4 = InternetAddress.loopbackIPv4; // 127.0.0.1
  final loopback6 = InternetAddress.loopbackIPv6; // ::1
  final any4 = InternetAddress.anyIPv4;           // 0.0.0.0
  final any6 = InternetAddress.anyIPv6;           // ::

  print(ipv4.type.name);        // InternetAddressType.IPv4
  print(ipv6.type.name);        // InternetAddressType.IPv6
  print(ipv4.isLoopback);       // false
  print(loopback4.isLoopback);  // true
  print(ipv4.isMulticast);      // false
  print(ipv4.rawAddress);       // Uint8List of the IP bytes
}

NetworkInterface — Local Network Information #

import 'dart:io';

Future<void> infoJaringan() async {
  // Get all network interfaces on this machine
  final interfaces = await NetworkInterface.list(
    includeLoopback: false,        // ignore 127.0.0.1/::1
    includeLinkLocal: false,       // ignore link-local (169.254.x.x)
    type: InternetAddressType.any, // IPv4 and IPv6
  );

  for (final iface in interfaces) {
    print('\nInterface: ${iface.name}'); // eth0, en0, wlan0, etc.
    print('Index: ${iface.index}');

    for (final addr in iface.addresses) {
      print('  ${addr.address} (${addr.type.name})');
      print('  Loopback: ${addr.isLoopback}');
      print('  Multicast: ${addr.isMulticast}');
    }
  }
}

// Get the machine's local IP to show to users
Future<String?> dapatIPLokal() async {
  final interfaces = await NetworkInterface.list(
    includeLoopback: false,
    type: InternetAddressType.IPv4,
  );

  for (final iface in interfaces) {
    for (final addr in iface.addresses) {
      if (!addr.isLoopback && !addr.isMulticast) {
        return addr.address;
      }
    }
  }
  return null;
}

stdin, stdout, stderr — Standard I/O in Depth #

import 'dart:io';
import 'dart:convert';

// stdout — standard output
stdout.write('Without a newline: ');
stdout.writeln('With a newline');
stdout.writeAll(['a', 'b', 'c'], ', '); // 'a, b, c'

// print() is a shortcut for stdout.writeln()
print('Same as stdout.writeln()');

// stderr — error output (a separate stream from stdout)
stderr.writeln('ERROR: Something went wrong');
// Useful because stderr can be redirected independently from stdout:
// dart run script.dart 2>error.log

// stdin — standard input
// Read one line (synchronous — fine for CLIs)
stdout.write('Enter your name: ');
final nama = stdin.readLineSync();
print('Hello, $nama!');

// Read with a specific encoding
final namaUtf8 = stdin.readLineSync(encoding: utf8);

// stdin as a Stream (for piped data)
// echo "data" | dart run script.dart
await stdin
    .transform(utf8.decoder)
    .transform(const LineSplitter())
    .forEach((baris) {
      print('Received: $baris');
    });

// Check whether stdin has data (to detect pipe vs terminal)
print('stdin is terminal: ${stdin.hasTerminal}');
print('stdout is terminal: ${stdout.hasTerminal}');

// Set the encoding
stdin.encoding = utf8;
stdout.encoding = utf8;

A Better Interactive CLI #

import 'dart:io';

class CLI {
  // Read input with a prompt and validation
  static String baca(
    String prompt, {
    bool wajib = true,
    String? Function(String)? validasi,
  }) {
    while (true) {
      stdout.write(prompt);
      final input = stdin.readLineSync()?.trim() ?? '';

      if (wajib && input.isEmpty) {
        stderr.writeln('Input cannot be empty.');
        continue;
      }

      final error = validasi?.call(input);
      if (error != null) {
        stderr.writeln('Error: $error');
        continue;
      }

      return input;
    }
  }

  // Read a number
  static int bacaInt(String prompt, {int? min, int? max}) {
    return int.parse(baca(
      prompt,
      validasi: (s) {
        final n = int.tryParse(s);
        if (n == null) return 'Enter a valid number';
        if (min != null && n < min) return 'Minimum: $min';
        if (max != null && n > max) return 'Maximum: $max';
        return null;
      },
    ));
  }

  // Yes/no confirmation
  static bool konfirmasi(String prompt) {
    final input = baca('$prompt (y/n): ').toLowerCase();
    return input == 'y' || input == 'ya' || input == 'yes';
  }

  // Choose from a menu
  static int pilihMenu(String judul, List<String> pilihan) {
    stdout.writeln('\n=== $judul ===');
    for (int i = 0; i < pilihan.length; i++) {
      stdout.writeln('${(i + 1).toString().padLeft(2)}. ${pilihan[i]}');
    }

    return bacaInt('Choose (1-${pilihan.length}): ',
        min: 1, max: pilihan.length);
  }
}

// Usage
void main() {
  final nama = CLI.baca('Name: ');
  final umur = CLI.bacaInt('Age: ', min: 1, max: 150);
  final konfirm = CLI.konfirmasi('Save the data?');

  final pilihan = CLI.pilihMenu('Choose an action', ['Save', 'Edit', 'Delete', 'Cancel']);
  print('Choice: $pilihan');
}

Platform — System Information #

import 'dart:io';

void infoSistem() {
  // Operating system
  print(Platform.operatingSystem);        // 'linux', 'macos', 'windows', 'android', 'ios'
  print(Platform.operatingSystemVersion); // 'Linux 5.15.0-...' or 'macOS 14.x...'
  print(Platform.localHostname);          // 'my-laptop' or 'DESKTOP-ABC123'
  print(Platform.localeName);            // 'en_US.UTF-8' or 'id_ID.UTF-8'
  print(Platform.numberOfProcessors);    // the number of logical CPU cores
  print(Platform.pathSeparator);         // '/' on Unix, '\' on Windows

  // Boolean shortcuts
  print(Platform.isLinux);     // true/false
  print(Platform.isMacOS);     // true/false
  print(Platform.isWindows);   // true/false
  print(Platform.isAndroid);   // true/false (only in Flutter)
  print(Platform.isIOS);       // true/false (only in Flutter)
  print(Platform.isFuchsia);   // true/false

  // Environment variables
  final home = Platform.environment['HOME'];           // Unix
  final userProfile = Platform.environment['USERPROFILE']; // Windows
  final path = Platform.environment['PATH'];
  final dbUrl = Platform.environment['DATABASE_URL'] ?? 'postgresql://localhost/mydb';

  // Dart runtime information
  print(Platform.version);       // '3.x.x (stable) ...'
  print(Platform.executable);    // path to the Dart executable
  print(Platform.script);        // URI of the currently running script
  print(Platform.executableArguments); // arguments to the Dart VM
  print(Platform.packageConfig);       // path to .dart_tool/package_config.json

  // Arguments to the script from the command line
  // dart run script.dart arg1 arg2 --flag
  print(Platform.executableArguments); // arguments to dart
  // script arguments are accessed via void main(List<String> args)
}

// A common pattern: different configuration per platform
String getDatabasePath() {
  if (Platform.isWindows) {
    return '${Platform.environment['APPDATA']}\\MyApp\\database.db';
  } else if (Platform.isMacOS) {
    return '${Platform.environment['HOME']}/Library/Application Support/MyApp/database.db';
  } else {
    return '${Platform.environment['HOME']}/.local/share/MyApp/database.db';
  }
}

ProcessInfo — Process Information #

import 'dart:io';

void infoProses() {
  // Current memory usage
  final meminfo = ProcessInfo.currentRss;     // RSS (Resident Set Size) in bytes
  final maxMem = ProcessInfo.maxRss;          // the maximum RSS ever reached

  print('Current memory: ${meminfo ~/ 1024 ~/ 1024} MB');
  print('Max memory: ${maxMem ~/ 1024 ~/ 1024} MB');
}

// Monitor memory periodically (for debugging memory leaks)
void monitorMemori({Duration interval = const Duration(seconds: 5)}) {
  Timer.periodic(interval, (_) {
    final rss = ProcessInfo.currentRss;
    final mb = rss / 1024 / 1024;
    print('[${DateTime.now()}] Memory: ${mb.toStringAsFixed(1)} MB');
  });
}

exit and Signal Handling #

import 'dart:io';

void main() async {
  // Handle signals for graceful shutdown
  ProcessSignal.sigint.watch().listen((signal) async {
    print('\nReceived SIGINT, cleaning up resources...');
    await bersihkan();
    exit(0); // code 0 = success
  });

  ProcessSignal.sigterm.watch().listen((signal) async {
    print('Received SIGTERM, graceful shutdown...');
    await bersihkan();
    exit(0);
  });

  // Run the app
  await jalankan();
}

Future<void> bersihkan() async {
  // Close database connections, flush logs, etc.
  print('Connections closed, logs flushed');
}

// Conventional exit codes
// exit(0) — success
// exit(1) — general error
// exit(2) — misuse of shell/command (invalid arguments)
// exit(126) — command can't be executed
// exit(127) — command not found
// exit(130) — script terminated by Ctrl+C

// Exit with an error code
void handleError(String pesan) {
  stderr.writeln('FATAL ERROR: $pesan');
  exit(1);
}

Stdin — Raw Mode #

import 'dart:io';

// Raw mode — read every keystroke without waiting for Enter
// Useful for CLI games or interactive applications
Future<void> modeRaw() async {
  stdin.echoMode = false;  // don't display typed characters
  stdin.lineMode = false;  // read per character, not per line

  try {
    stdout.writeln('Press a key (q to quit):');

    await stdin.forEach((bytes) {
      for (final byte in bytes) {
        if (byte == 113) { // 'q'
          stdout.writeln('\nQuitting');
          exit(0);
        }
        stdout.write('Code: $byte (${String.fromCharCode(byte)})\n');
      }
    });
  } finally {
    stdin.echoMode = true;   // restore the mode
    stdin.lineMode = true;
  }
}

Quick Reference — Key dart:io Classes #

ClassDescription
FileRead/write files
DirectoryDirectory operations
HttpClientHTTP client
HttpServerHTTP server
SocketTCP socket
ServerSocketTCP server socket
SecureSocketTLS/SSL socket
WebSocketWebSocket client/server
RawDatagramSocketUDP socket
InternetAddressIP addresses and DNS lookups
NetworkInterfaceNetwork interface info
ProcessRun external processes
ProcessSignalHandle OS signals
ProcessInfoProcess memory info
PlatformPlatform/OS info
stdinStandard input stream
stdoutStandard output stream
stderrStandard error stream
exit()Exit with a code
sleep()Synchronous pause

Summary #

  • dart:io is only in the Dart VM — not available in Flutter web. Use the portable package:http for HTTP on all platforms.
  • The built-in HttpClient gives full control over HTTP — timeouts, proxies, headers, redirects — but is more verbose than package:http. Use package:http unless you need specific control.
  • InternetAddress.lookup() for DNS resolution — returns all IP addresses (IPv4 and IPv6) for a hostname. Use .reverse() for PTR lookups.
  • NetworkInterface.list() to get the machine’s local IP — useful for showing a server address to users.
  • stdin.hasTerminal to detect whether the program runs interactively or via a pipe — useful for deciding whether to show prompts or read data directly.
  • stderr for error and diagnostic messages — separate from stdout so it can be redirected independently: dart run script.dart 2>error.log.
  • stdin.echoMode = false + stdin.lineMode = false for per-character input without waiting for Enter — useful for interactive CLIs and games.
  • Platform.environment is the idiomatic way to read configuration from environment variables — the standard for 12-factor apps.
  • ProcessSignal.sigint.watch() for graceful shutdown — handle Ctrl+C cleanly, close all resources before exiting.
  • exit(0) for success, exit(1) for errors — exit codes are consumed by shell scripts and CI/CD to determine whether the program succeeded.

← Previous: Strings   Next: Math →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact