Dart ReadPipe
last modified October 18, 2026
The ReadPipe interface in Dart represents the readable end of an
anonymous operating system pipe. It is part of the dart:io
library and is useful for consuming byte streams from processes, sockets, and
other I/O resources.
A ReadPipe is not created directly. You obtain one from
Pipe.create().read or by extracting it from a
ResourceHandle with toReadPipe.
Basic Definition
ReadPipe is an abstract interface class that implements
Stream<List<int>>. It adds no reading methods of its
own; use the standard stream API such as listen,
transform, and join.
The stream yields raw byte chunks. Chunk boundaries are determined by the operating system and do not necessarily match logical messages.
Basic ReadPipe Usage
This example reads all text written to the other end of a pipe.
import 'dart:convert';
import 'dart:io';
Future<void> main() async {
var pipe = await Pipe.create();
var readPipe = pipe.read;
pipe.write.write('Hello World');
await pipe.write.close();
var data = await readPipe.transform(utf8.decoder).join();
print(data);
}
We get the ReadPipe from pipe.read, write to the other
end, and close it. Decoding the stream with utf8.decoder and
joining the result produces the complete text.
$ dart main.dart Hello World
Reading in Chunks
This example demonstrates regrouping raw pipe data into fixed-size chunks.
import 'dart:io';
Future<void> main() async {
var pipe = await Pipe.create();
var readPipe = pipe.read;
pipe.write.write('Dart is awesome!');
await pipe.write.close();
var bytes = await readPipe.expand((chunk) => chunk).toList();
for (var i = 0; i < bytes.length; i += 3) {
var end = i + 3 < bytes.length ? i + 3 : bytes.length;
var chunk = bytes.sublist(i, end);
print('Chunk: ${String.fromCharCodes(chunk)}');
}
}
The stream delivers byte lists whose sizes depend on the operating system. By
flattening them with expand and regrouping the bytes, we can read
data in fixed-size chunks, which is useful for simple binary protocols.
$ dart main.dart Chunk: Dar Chunk: t i Chunk: s a Chunk: wes Chunk: ome Chunk: !
Handling Binary Data
This example shows reading a binary header followed by a larger body.
import 'dart:io';
Future<void> main() async {
var pipe = await Pipe.create();
var readPipe = pipe.read;
pipe.write.add([137, 80, 78, 71]);
pipe.write.add(List.filled(1024, 0));
await pipe.write.close();
var bytes = await readPipe.expand((chunk) => chunk).toList();
print('Header: ${bytes.sublist(0, 4)}');
print('Body length: ${bytes.length - 4} bytes');
}
No text decoder is used here. The first four bytes are read as a binary
header, and the remaining bytes form the body. Working with
List<int> avoids corrupting binary data.
$ dart main.dart Header: [137, 80, 78, 71] Body length: 1024 bytes
Error Handling
This example demonstrates error handling when decoding data from a
ReadPipe.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
Future<void> main() async {
var pipe = await Pipe.create();
var done = Completer<void>();
pipe.read.transform(utf8.decoder).listen(
(data) => print('Data: $data'),
onError: (e) => print('Error: $e'),
onDone: done.complete,
);
pipe.write.add([0xff]);
await pipe.write.close();
await done.future;
}
The byte 0xff is not valid UTF-8. The decoder reports a
FormatException through onError, and the stream then
finishes normally after the write end closes.
$ dart main.dart Error: FormatException: Invalid UTF-8 byte (at offset 0)
Combining with Transformers
This example shows using a ReadPipe with UTF-8 and JSON
transformers.
import 'dart:convert';
import 'dart:io';
Future<void> main() async {
var pipe = await Pipe.create();
var readPipe = pipe.read;
pipe.write.write('{"name": "John", "age": 30}');
await pipe.write.close();
var data = await readPipe
.transform(utf8.decoder)
.transform(json.decoder)
.first as Map<String, dynamic>;
print('Name: ${data['name']}');
print('Age: ${data['age']}');
}
Transformers can be chained directly on the read stream. Here the bytes are decoded as UTF-8 and parsed as JSON, and the first decoded value is cast to a map for property access.
$ dart main.dart Name: John Age: 30
Best Practices
- Use stream operations: Treat a
ReadPipeasStream<List<int>> - Decode when needed: Apply
utf8.decoderonly to textual data - Handle errors: Always provide an
onErrorhandler for decoding failures - Respect chunk boundaries: Do not assume one read equals one message
- Close the writer: The read stream ends only when the write end is closed
Source
This tutorial covered Dart's ReadPipe interface with practical
examples showing basic reading, fixed-size chunks, binary data, errors, and
transformers.
Author
List all Dart tutorials.