Dart Lesson 77 of 102 3 min read
Reading Files in Dart
Learn how to read files in Dart with dart:io: whole file as text, line by line, as bytes, as a stream, and sync versus async methods.
On this page
To work with a file, create a File object from its path. This does not open or create anything. It is only a reference to a location on disk.
For the examples, assume a file notes.txt with this content:
Buy milk
Call Hari
Finish the Dart tutorial
Read the whole file as a string #
import 'dart:io';
Future<void> main() async {
final file = File('notes.txt');
final contents = await file.readAsString();
print(contents);
print('${contents.length} characters');
}
Buy milk
Call Hari
Finish the Dart tutorial
44 characters
readAsString returns a Future<String>, so it must be awaited. Text is decoded as UTF-8 unless you pass another encoding.
Read as a list of lines #
import 'dart:io';
Future<void> main() async {
final lines = await File('notes.txt').readAsLines();
print('${lines.length} tasks');
for (final (i, line) in lines.indexed) {
print('${i + 1}. $line');
}
}
3 tasks
1. Buy milk
2. Call Hari
3. Finish the Dart tutorial
Read as bytes #
Images, PDFs and other binary files are read as a list of bytes.
import 'dart:io';
Future<void> main() async {
final bytes = await File('photo.jpg').readAsBytes();
print('${bytes.length} bytes');
print('First bytes: ${bytes.take(3).toList()}');
}
Read a large file as a stream #
The methods above load the entire file into memory. For a file of hundreds of megabytes, read it in pieces with openRead, which returns a stream.
import 'dart:convert';
import 'dart:io';
Future<void> main() async {
final lines = File('server.log')
.openRead()
.transform(utf8.decoder) // bytes -> text
.transform(const LineSplitter()); // text -> lines
var errors = 0;
await for (final line in lines) {
if (line.contains('ERROR')) errors++;
}
print('$errors error lines');
}
Only a small chunk is in memory at any moment, however big the file is.
Synchronous and asynchronous versions #
Almost every file method comes in two forms.
| Asynchronous | Synchronous | Returns |
|---|---|---|
readAsString() | readAsStringSync() | Text |
readAsLines() | readAsLinesSync() | List of lines |
readAsBytes() | readAsBytesSync() | Bytes |
exists() | existsSync() | bool |
writeAsString() | writeAsStringSync() | The file |
import 'dart:io';
void main() {
final contents = File('notes.txt').readAsStringSync();
print(contents.split('\n').first);
}
Buy milk
The synchronous form needs no await, but it blocks the program until the disk responds. Which to choose:
- Short scripts and command-line tools: synchronous is fine and simpler.
- Flutter apps and servers: use the asynchronous form. A blocked thread means a frozen screen or other requests left waiting.
The file must exist #
Reading a file that is not there throws a PathNotFoundException, a kind of FileSystemException.
import 'dart:io';
Future<void> main() async {
final file = File('missing.txt');
try {
print(await file.readAsString());
} on PathNotFoundException {
print('${file.path} does not exist');
} on FileSystemException catch (e) {
print('Could not read the file: ${e.message}');
}
}
missing.txt does not exist
See file error handling for more.
Relative and absolute paths #
File('notes.txt') is a relative path. It is resolved from the folder where you ran the command, not the folder containing your .dart file. If a file “cannot be found”, print Directory.current.path to see where the program is looking. Details are in file paths.
Try it yourself #
Create a text file with a few lines of numbers, one per line. Write a program that reads it, ignores blank or invalid lines, and prints the count, the sum and the average.