Dart Stdin
last modified May 29, 2026
The Stdin class in Dart represents the standard input stream. It is
part of Dart's dart:io library and is accessed through the top-level
stdin variable. It is essential for interactive command-line
applications.
Stdin extends Stream<List<int>>, so it can
be consumed as a byte stream using the full Dart stream API. It also provides
synchronous convenience methods — readLineSync and
readByteSync — for simpler sequential input.
Basic Definition
Stdin is a class that implements
Stream<List<int>>. It is accessed through the
stdin top-level variable exported by dart:io.
The corresponding stdout and stderr variables handle
output and error messages.
The two primary synchronous methods are readLineSync, which reads a
complete line, and readByteSync, which reads a single byte. For
asynchronous consumption, stdin can be transformed with
utf8.decoder and LineSplitter from
dart:convert.
Reading a Single Line
This example reads a single line of text from the console using
readLineSync.
import 'dart:io';
void main() {
stdout.write('Enter your name: ');
String name = stdin.readLineSync() ?? 'Unknown';
print('Hello, $name!');
}
readLineSync blocks until the user presses Enter and returns the
line as a String, or null if EOF is reached. The
?? operator supplies a fallback value. stdout.write is
used for the prompt so the cursor stays on the same line as the user's input.
$ dart main.dart Enter your name: John Hello, John!
Reading Numbers
This example reads two integers from the console and prints their sum.
import 'dart:io';
void main() {
stdout.write('Enter first number: ');
var num1 = int.tryParse(stdin.readLineSync() ?? '0') ?? 0;
stdout.write('Enter second number: ');
var num2 = int.tryParse(stdin.readLineSync() ?? '0') ?? 0;
print('Sum: ${num1 + num2}');
}
readLineSync always returns a String?, so numeric
input must be parsed explicitly. int.tryParse returns
null for non-numeric strings instead of throwing an exception,
allowing the ?? operator to substitute a safe default.
$ dart main.dart Enter first number: 12 Enter second number: 7 Sum: 19
Asynchronous Line Reading
This example reads lines from stdin asynchronously by treating it as a byte
stream and applying decoders from dart:convert.
import 'dart:convert';
import 'dart:io';
void main() async {
stdout.writeln('Type lines to echo in upper case (Ctrl+D to finish):');
final lineStream = stdin
.transform(utf8.decoder)
.transform(const LineSplitter());
await for (final line in lineStream) {
stdout.writeln(line.toUpperCase());
}
}
stdin implements Stream<List<int>>.
Chaining utf8.decoder converts raw byte chunks into strings, and
LineSplitter splits the decoded text into individual lines. The
await for loop processes each line as it arrives and exits when
stdin closes (Ctrl+D on Unix/macOS, Ctrl+Z then Enter on Windows).
$ dart main.dart Type lines to echo in upper case (Ctrl+D to finish): hello HELLO dart DART
Reading Raw Bytes
This example demonstrates reading raw bytes from standard input.
import 'dart:io';
void main() {
stdout.writeln('Press any key to continue...');
var byte = stdin.readByteSync();
print('You pressed: $byte (${String.fromCharCode(byte)})');
}
readByteSync reads a single byte synchronously and returns its
value as an int, or -1 at EOF.
String.fromCharCode converts the integer back to its printable
character. This is useful for single-keypress menus or byte-by-byte input
processing.
$ dart main.dart Press any key to continue... a You pressed: 97 (a)
Reading Until EOF
This example reads all lines from stdin until EOF and prints a summary.
import 'dart:io';
void main() {
print('Enter multiple lines (Ctrl+D to end):');
var lines = <String>[];
while (true) {
var line = stdin.readLineSync();
if (line == null) break;
lines.add(line);
}
print('You entered ${lines.length} lines:');
lines.forEach(print);
}
readLineSync returns null when EOF is reached
(Ctrl+D on Unix/macOS, Ctrl+Z then Enter on Windows). Collecting each non-null
line into a list and breaking on null is the standard synchronous
pattern for consuming all of stdin, whether from an interactive terminal or a
piped file.
$ dart main.dart Enter multiple lines (Ctrl+D to end): hello world dart You entered 3 lines: hello world dart
Reading with Encoding
This example reads a line with an explicit UTF-8 encoding to correctly handle non-ASCII characters such as accented letters or CJK characters.
import 'dart:convert';
import 'dart:io';
void main() {
stdout.write('Enter your name: ');
final name = stdin.readLineSync(encoding: utf8) ?? 'Unknown';
print('Hello, $name!');
print('Character count: ${name.length}');
}
readLineSync accepts an optional encoding parameter.
Passing utf8 from dart:convert correctly decodes
multi-byte characters. Without an explicit encoding, the system default is
used, which may not handle all Unicode on every platform.
$ dart main.dart Enter your name: Ján Hello, Ján! Character count: 3
Input Validation Loop
This example repeatedly prompts the user until a valid positive integer is entered.
import 'dart:io';
int readPositiveInt(String prompt) {
while (true) {
stdout.write(prompt);
final input = stdin.readLineSync()?.trim() ?? '';
final value = int.tryParse(input);
if (value != null && value > 0 && value < 130) return value;
stderr.writeln('Please enter a valid positive integer (1-129).');
}
}
void main() {
final age = readPositiveInt('Enter your age: ');
print('In ten years you will be ${age + 10}.');
}
The helper function loops until int.tryParse succeeds and the value
is within the valid range (1-129). Invalid attempts print an error to
stderr and repeat the prompt. The return type is non-nullable
int because the loop only exits through the return
statement.
$ dart main.dart Enter your age: hello Please enter a valid positive integer (1-129). Enter your age: -3 Please enter a valid positive integer (1-129). Enter your age: 25 In ten years you will be 35.
Best Practices
- Null safety:
readLineSyncreturnsnullat EOF; always handle it with??or an explicit null check - Parsing: Prefer
int.tryParseanddouble.tryParseoverparseto avoid exceptions on bad input - Encoding: Pass
encoding: utf8toreadLineSyncwhen expecting non-ASCII characters - Async streams: Use
stdin.transform(utf8.decoder).transform(const LineSplitter())withawait forfor non-blocking stream processing - EOF signals: Ctrl+D ends input on Unix/macOS; Ctrl+Z then Enter on Windows
Source
This tutorial covered Dart's Stdin class with practical examples
demonstrating single-line reading, numeric parsing, asynchronous stream
processing, raw byte reading, EOF handling, UTF-8 encoding, and input
validation loops.
Author
List all Dart tutorials.