saf¶
One class for the Android Storage Access Framework โ pickers, persisted permissions, file management, streaming read/write, recursive walk, and progress.
final saf = Saf();
final dir = await saf.pickDirectory(); // one prompt, persists across restarts
if (dir == null) return;
for (final f in await saf.list(dir.uri)) {
print('${f.name} ยท ${f.length} B ยท ${f.mimeType}');
}
final doc = await saf.writeFileBytes(
dir.uri, 'hello.txt', 'text/plain', utf8.encode('hi') as Uint8List);
final bytes = await saf.readFileBytes(doc.uri);
๐ One package¶
Everything SAF in a single Saf class โ no pairing two libraries.
๐ Persisted permissions¶
Grant once, reuse across app restarts. List grants with persistedPermissions().
๐ Streaming I/O¶
readFileStream and one-call writeFileStream for large files โ no session bookkeeping.
๐ถ Recursive walk¶
walk() streams a whole directory tree; copyTo/moveTo recurse with progress.
๐ Descriptors & thumbnails¶
openFileDescriptor / withFileDescriptor hand native APIs a live /proc/self/fd path; thumbnail() returns provider JPEG previews.
๐ฏ Typed errors¶
SafPermissionException, SafNotFoundException, โฆ instead of raw PlatformException.
๐ฑ Broad support¶
Dart โฅ 3.0, Flutter โฅ 3.10, Android minSdk 21 โ wider than the alternatives.
Where to next¶
- Install & first run โ add the dependency and grant your first directory.
- Recipes โ copy-paste snippets for every operation.
- Migrate โ from saf 1.x.
- Architecture โ how the Dart facade talks to the Kotlin SAF layer.
- API reference โ the full generated dartdoc.