Odds are that if you work with Flutter, eventually you'll have to handle files and directories.
Let's do a quick summary of all the basic filesystem operations that we can do with Flutter:
List common directories
By using the path_provider plugin we can get the path to common directories designed for different purposes:
import 'package:path_provider/path_provider.dart';
// Put cache files in this directory
final temporaryDirectory = await getTemporaryDirectory();
// For files that our app uses but are not exposed to the user
final appSupport = await getApplicationSupportDirectory();
// For user-generated files
final appDocuments = await getApplicationDocumentsDirectory();
Create a file
Let's create a random file in the temporary directory and write something inside:
final Directory tempDir = await getTemporaryDirectory();
final File file = File('${tempDir.path}/sample_file.txt');
await file.writeAsString('Sample content to write');
Delete a file
The delete method is just as crucial as the create method, as it helps clear out old files and thus saves valuable storage space.
final Directory tempDir = await getTemporaryDirectory();
final File file = File('${tempDir.path}/sample_file.txt');
await file.delete();
Create a directory
Now let's say that I want to create a new file, but within a certain directory:
final Directory tempDir = await getTemporaryDirectory();
final Directory newDirectory =
Directory('${tempDir.path}/sample_directory');
// Always check that the directory exists
if (await newDirectory.exists() == false) {
await newDirectory.create();
}
final File file = File('${newDirectory.path}/sample_file.txt');
await file.writeAsString('Sample content to write');
Remove a directory
Now let's do the reverse and delete the directory:
final Directory tempDir = await getTemporaryDirectory();
final Directory newDirectory =
Directory('${tempDir.path}/sample_directory');
await newDirectory.delete(recursive: true);
List files
Sometimes you need to list all files within a directory to get some of its stats:
final Directory directory = await getTemporaryDirectory();
final List<FileSystemEntity> files = directory.listSync();
for (final FileSystemEntity file in files) {
final FileStat fileStat = await file.stat();
print('Path: ${file.path}');
print('Type: ${fileStat.type}');
print('Changed: ${fileStat.changed}');
print('Modified: ${fileStat.modified}');
print('Accessed: ${fileStat.accessed}');
print('Mode: ${fileStat.mode}');
print('Size: ${fileStat.size}');
}
Read file
Let's open a file to see its content:
final Directory tempDir = await getTemporaryDirectory();
final File file = File('${tempDir.path}/sample_file.txt');
final String fileContent = await file.readAsString();
Copy file
Now, let's generate a duplicate of the previously created sample file:
final Directory tempDir = await getTemporaryDirectory();
final File file = File('${tempDir.path}/sample_file.txt');
final File copy = await file.copy('${tempDir.path}/copy_file.txt');
Rename file
Next, let's change the name of the file we just copied:
final Directory tempDir = await getTemporaryDirectory();
final File file = File('${tempDir.path}/copy_file.txt');
await file.rename('${tempDir.path}/new_name.txt');
Synchronously manage file operations
So far, I've demonstrated how to handle files asynchronously, which is the preferred method. However, if for some reason Futures aren't an option for you, synchronous file operations are also possible:
final Directory tempDir = await getTemporaryDirectory();
final File file = File('${tempDir.path}/sample_file.txt');
file.writeAsStringSync('New content to add');
Error handling
All file system operations should be performed safely since they are prone to throw exceptions. For simplicity, the previous examples did not include this, but it's critical to always encapsulate your I/O operations within a try-catch
block:
final Directory tempDir = await getTemporaryDirectory();
final File file = File('${tempDir.path}/sample_file.txt');
try {
await file.writeAsString('New content to add');
} catch (e) {
// Handle IO error
}
Conclusion
Dart and Flutter simplify working with files, as demonstrated above. I hope this summary was useful and clear.
Happy coding!