Dart BytesBuilder
last modified October 18, 2026
The BytesBuilder class in Dart provides an efficient way to build
lists of bytes dynamically. It's useful for network protocols, file operations,
and binary data manipulation.
BytesBuilder collects the added bytes and joins them when the
result is requested, which avoids copying the data over and over. It is part of
the dart:typed_data library.
Basic Definition
BytesBuilder is a mutable collection for building byte lists. It
grows automatically as needed and provides methods to add bytes. The
toBytes method returns a Uint8List with a copy of the
content, and takeBytes returns the content and empties the builder.
The constructor has one named parameter, copy, which is true by
default. With the default, the builder copies every list that is added; with
BytesBuilder(copy: false) it keeps references to the added
Uint8List objects, which is faster but means that those lists must
not be modified afterwards.
Basic BytesBuilder Usage
This example shows basic byte collection using BytesBuilder.
import 'dart:typed_data';
void main() {
final builder = BytesBuilder();
builder.addByte(65); // 'A'
builder.addByte(66); // 'B'
builder.addByte(67); // 'C'
final bytes = builder.toBytes();
print(bytes);
print(String.fromCharCodes(bytes));
}
We create a BytesBuilder, add individual bytes, then extract the final byte list. The builder handles memory management internally as bytes are added. The bytes are interpreted as ASCII codes in the last line. Each added value must be in the range 0-255; larger values are truncated to the low eight bits.
$ dart main.dart [65, 66, 67] ABC
Adding Byte Lists
This example demonstrates adding entire lists of bytes at once.
import 'dart:typed_data';
void main() {
final builder = BytesBuilder();
builder.add([1, 2, 3]);
builder.add([4, 5, 6]);
print(builder.toBytes());
print('Length: ${builder.length}');
print('Empty: ${builder.isEmpty}');
}
We add multiple byte lists which are concatenated. The length
property tracks the total number of bytes added. Adding a list at once is more
efficient than adding the bytes individually.
$ dart main.dart [1, 2, 3, 4, 5, 6] Length: 6 Empty: false
Building Binary Data
This example shows building structured binary data with BytesBuilder.
import 'dart:typed_data';
void main() {
final builder = BytesBuilder();
final payload = 'Hello'.codeUnits;
// header
builder.add([0xAA, 0xBB]);
// payload length (2 bytes, big endian)
builder.addByte(payload.length >> 8);
builder.addByte(payload.length & 0xFF);
// payload
builder.add(payload);
// checksum: sum of all bytes so far, modulo 256
final checksum = builder.toBytes().fold(0, (sum, byte) => sum + byte) & 0xFF;
builder.addByte(checksum);
print(builder.toBytes());
}
We construct a binary message with header, length, payload and checksum. The checksum is computed from the bytes collected so far and appended as the last byte. The sum of the bytes 170, 187, 0, 5, 72, 101, 108, 108 and 111 is 862, and 862 & 255 is 94.
$ dart main.dart [170, 187, 0, 5, 72, 101, 108, 108, 111, 94]
Writing numbers with ByteData
BytesBuilder only accepts bytes. To write multi-byte numbers in a
well-defined byte order, we can combine it with ByteData, which has
methods such as setUint32 and setFloat64 with an
Endian argument.
import 'dart:typed_data';
Uint8List uint32(int value, Endian endian) {
final data = ByteData(4)..setUint32(0, value, endian);
return data.buffer.asUint8List();
}
void main() {
final builder = BytesBuilder();
builder.add(uint32(1, Endian.big));
builder.add(uint32(1, Endian.little));
builder.add(uint32(0xCAFEBABE, Endian.big));
print(builder.toBytes());
}
The number 1 is stored as 0 0 0 1 in big-endian (network) order and
as 1 0 0 0 in little-endian order. The helper function converts a
32-bit number to four bytes.
$ dart main.dart [0, 0, 0, 1, 1, 0, 0, 0, 202, 254, 186, 190]
Clearing and Reusing
This example demonstrates clearing the builder for reuse. The
takeBytes method returns the content and clears the builder in one
step.
import 'dart:typed_data';
void main() {
final builder = BytesBuilder();
builder.add([1, 2, 3]);
print('First build: ${builder.toBytes()}');
builder.clear();
builder.add([4, 5, 6]);
print('Second build: ${builder.takeBytes()}');
print('Length: ${builder.length}');
}
The clear method discards the collected bytes. After
takeBytes, the builder is empty, so its length is zero, and it can
be used again. The takeBytes call is cheaper than
toBytes followed by clear, because the content does not
have to be copied.
$ dart main.dart First build: [1, 2, 3] Second build: [4, 5, 6] Length: 0
Performance Comparison
This example compares BytesBuilder with manual list concatenation.
import 'dart:math';
import 'dart:typed_data';
void main() {
final random = Random(1);
final data = List.generate(1000, (_) => random.nextInt(256));
final stopwatch = Stopwatch()..start();
final builder = BytesBuilder();
for (var i = 0; i < 400; i++) {
builder.add(data);
}
final fast = builder.toBytes();
final builderTime = stopwatch.elapsedMicroseconds;
stopwatch.reset();
var manual = <int>[];
for (var i = 0; i < 400; i++) {
manual = [...manual, ...data];
}
final manualTime = stopwatch.elapsedMicroseconds;
print('BytesBuilder: $builderTime μs');
print('Manual: $manualTime μs');
print('Same result: ${fast.length == manual.length}');
}
BytesBuilder is significantly faster for large concatenations as it minimizes copies; the manual concatenation copies the whole list again and again, so the work grows quadratically. The exact timings depend on the machine and the Dart VM; run the example to see the values for your system. The relative difference is what matters.
$ dart main.dart BytesBuilder: 8322 μs Manual: 1519735 μs Same result: true
Best Practices
- Bulk adds: Prefer
addover repeatedaddBytecalls when you have several bytes. - Take, don't copy: Use
takeByteswhen the builder is no longer needed. - No copy mode: Use
BytesBuilder(copy: false)only when the added lists will not be modified. - Text: Encode strings with
utf8.encodebefore adding them;codeUnitsgives UTF-16 units, which are bytes only for ASCII text.
Source
Dart BytesBuilder Documentation
This tutorial covered Dart's BytesBuilder class with practical examples showing basic usage, performance benefits, and common patterns for byte manipulation.
Author
List all Dart tutorials.