Dart Stdout
last modified April 4, 2025
The Stdout class in Dart represents the standard output stream.
It is part of Dart's dart:io library and is accessed through the
top-level stdout variable. It is essential for command-line
applications.
Stdout extends IOSink, which itself extends
StringSink, giving it both string-writing and raw byte-writing
capabilities. It provides more control than the built-in print
function, including character code output, terminal detection, and asynchronous
stream support.
Basic Definition
Stdout is a class that extends IOSink. It is accessed
through the stdout top-level variable exported by
dart:io. A separate stderr variable provides access
to the standard error stream.
Key methods include write, writeln,
writeAll, writeCharCode, add,
addStream, and flush. Properties such as
hasTerminal, terminalColumns, and
supportsAnsiEscapes allow output to adapt to the terminal
environment.
Basic Stdout Usage
This example demonstrates basic console output using the Stdout
class. We compare write, writeln, and the built-in
print function to show how they differ.
import 'dart:io';
void main() {
stdout.write('Hello, ');
stdout.write('Dart!');
stdout.writeln();
stdout.writeln('This is a new line');
print('Using print() for comparison');
}
write appends text without a trailing newline. writeln
appends text followed by a newline. An empty writeln() call emits
only a newline. Unlike print, write does not add a
newline automatically, which is useful when building output incrementally.
$ dart main.dart Hello, Dart! This is a new line Using print() for comparison
Writing Bytes to Stdout
This example demonstrates writing raw bytes and individual characters to
standard output using add, writeCharCode, and
addStream.
import 'dart:io';
void main() async {
// add() writes a list of bytes; ASCII 72-111 spells 'Hello', 10 is newline
stdout.add([72, 101, 108, 108, 111, 10]);
// writeCharCode writes a single character by its Unicode code point
stdout.writeCharCode(68); // D
stdout.writeCharCode(97); // a
stdout.writeCharCode(114); // r
stdout.writeCharCode(116); // t
stdout.writeln();
// addStream consumes a Stream of byte lists (Stream<List<int>>)
final byteStream = Stream.fromIterable([
[66, 121, 101], // 'Bye'
]);
await stdout.addStream(byteStream);
stdout.writeln();
}
add writes a List<int> of byte values directly
to the stream. writeCharCode writes a single character given its
Unicode code point. addStream consumes a
Stream<List<int>> asynchronously and must be awaited
before further writes to maintain correct ordering.
$ dart main.dart Hello Dart Bye
Controlling Line Behavior
This example illustrates the difference between write,
writeln, writeAll, and flush.
import 'dart:io';
void main() async {
// writeln appends a newline automatically
stdout.writeln('First line');
// write does not append a newline
stdout.write('Hello, ');
stdout.write('Dart!');
stdout.writeln(); // explicit newline
// writeAll joins items with a separator
stdout.writeAll(['apple', 'banana', 'cherry'], ', ');
stdout.writeln();
// flush returns a Future; await it to ensure all buffered output is sent
await stdout.flush();
}
writeln adds a trailing newline; write does not.
writeAll writes each item from an iterable separated by the given
string. flush returns a Future<void> that
completes when all buffered data has been written to the underlying sink, so
it should be awaited in an async function.
$ dart main.dart First line Hello, Dart! apple, banana, cherry
Checking Terminal Capabilities
This example demonstrates checking terminal properties before output.
import 'dart:io';
void main() {
if (stdout.hasTerminal) {
print('Terminal width: ${stdout.terminalColumns}');
print('Terminal height: ${stdout.terminalLines}');
if (stdout.supportsAnsiEscapes) {
stdout.writeln('\x1B[31mRed Text\x1B[0m');
}
} else {
stdout.writeln('No terminal detected');
}
}
hasTerminal returns true when stdout is connected to
an interactive terminal. terminalColumns and
terminalLines give the terminal dimensions in columns and rows.
Checking supportsAnsiEscapes before emitting escape sequences
ensures portability across different terminal emulators and piped output.
$ dart main.dart Terminal width: 80 Terminal height: 24 Red Text
Synchronous vs Asynchronous Writing
This example compares synchronous writes via write with
asynchronous stream consumption via addStream.
import 'dart:io';
void main() async {
// Synchronous writes execute immediately
stdout.write('Sync 1 ');
stdout.write('Sync 2 ');
stdout.writeln();
// codeUnits returns List<int>; addStream expects Stream<List<int>>
await stdout.addStream(Stream.fromIterable([
'Async 1\n'.codeUnits,
'Async 2\n'.codeUnits,
]));
stdout.writeln('Done');
}
write and writeln are synchronous and execute
immediately. addStream is asynchronous and accepts a
Stream<List<int>>. The codeUnits getter
on a string returns a List<int> of UTF-16 code units,
making it directly compatible. Using await ensures the stream is
fully consumed before continuing.
$ dart main.dart Sync 1 Sync 2 Async 1 Async 2 Done
Stdout and Stderr
This example shows how to separate normal program output and diagnostic
messages by writing to stdout and stderr respectively.
import 'dart:io';
void main() {
stdout.writeln('Starting data processing...');
for (var i = 1; i <= 3; i++) {
stdout.writeln('Processing record $i');
}
stderr.writeln('Warning: record 2 had missing fields');
stdout.writeln('Finished. 3 records processed.');
stderr.writeln('Error: could not save summary file');
}
stdout and stderr are independent streams.
stdout carries normal program output while stderr
carries warnings and error messages. They can be redirected independently
from the shell (e.g., 2>errors.log), making it easy to
separate logs from diagnostics.
$ dart main.dart Starting data processing... Processing record 1 Processing record 2 Processing record 3 Finished. 3 records processed.
Formatted Table Output
This example prints a neatly aligned table to the console using string padding methods available on all Dart strings.
import 'dart:io';
void main() {
final headers = ['Name', 'Score', 'Grade'];
final rows = [
['Alice', '95', 'A'],
['Bob', '78', 'B+'],
['Carol', '62', 'C'],
];
stdout.writeln(headers.map((h) => h.padRight(10)).join(''));
stdout.writeln('-' * 30);
for (final row in rows) {
stdout.writeln(row.map((cell) => cell.padRight(10)).join(''));
}
}
padRight(width) pads a string to the given width with trailing
spaces. Mapping each cell and joining the results with an empty string
produces a fixed-width row. Combined with writeln, this creates
clearly aligned tabular console output without any external dependencies.
$ dart main.dart Name Score Grade ------------------------------ Alice 95 A Bob 78 B+ Carol 62 C
Best Practices
- Flushing: Await
flush()when immediate output is required before a blocking operation - Terminal detection: Check
hasTerminalbefore readingterminalColumnsorterminalLinesto avoid runtime exceptions - ANSI codes: Verify
supportsAnsiEscapesbefore emitting escape sequences to prevent garbled output in non-supporting environments - Errors: Write diagnostic messages to
stderr, notstdout, so they can be redirected independently - Byte streams:
addStreamexpects aStream<List<int>>; usecodeUnitson a string to obtain a compatibleList<int>
Source
This tutorial covered Dart's Stdout class with practical examples
demonstrating basic output, byte writing, line control, terminal detection,
asynchronous stream consumption, stderr separation, and formatted table output.
Author
List all Dart tutorials.