ZetCode

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.

main.dart
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.

main.dart
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.

main.dart
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.

main.dart
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.

main.dart
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.

main.dart
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.

main.dart
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

Source

Dart Stdin Documentation

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

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Dart tutorials.