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:iois only available in the Dart VM — it can’t be used in code compiled to JavaScript (web). For Flutter web, usedart:htmlor the portablehttppackage.
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
#
| Class | Description |
|---|---|
File | Read/write files |
Directory | Directory operations |
HttpClient | HTTP client |
HttpServer | HTTP server |
Socket | TCP socket |
ServerSocket | TCP server socket |
SecureSocket | TLS/SSL socket |
WebSocket | WebSocket client/server |
RawDatagramSocket | UDP socket |
InternetAddress | IP addresses and DNS lookups |
NetworkInterface | Network interface info |
Process | Run external processes |
ProcessSignal | Handle OS signals |
ProcessInfo | Process memory info |
Platform | Platform/OS info |
stdin | Standard input stream |
stdout | Standard output stream |
stderr | Standard error stream |
exit() | Exit with a code |
sleep() | Synchronous pause |
Summary #
dart:iois only in the Dart VM — not available in Flutter web. Use the portablepackage:httpfor HTTP on all platforms.- The built-in
HttpClientgives full control over HTTP — timeouts, proxies, headers, redirects — but is more verbose thanpackage:http. Usepackage:httpunless 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.hasTerminalto detect whether the program runs interactively or via a pipe — useful for deciding whether to show prompts or read data directly.stderrfor error and diagnostic messages — separate fromstdoutso it can be redirected independently:dart run script.dart 2>error.log.stdin.echoMode = false+stdin.lineMode = falsefor per-character input without waiting for Enter — useful for interactive CLIs and games.Platform.environmentis 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.