Dart Tutorial

Dart Lesson 79 of 102 3 min read

Checking If a File Exists and Deleting Files in Dart

Learn to check whether a file or directory exists in Dart, and to delete, rename, move and copy files safely with dart:io.

On this page

Before reading a file you may want to know whether it is there. When you are done with one, you may want to remove it, rename it or copy it.

Does it exist? #

import 'dart:io';

Future<void> main() async {
  final file = File('notes.txt');

  if (await file.exists()) {
    print('Found ${file.path}');
  } else {
    print('${file.path} is missing');
  }

  // Synchronous version
  print(File('pubspec.yaml').existsSync());
}

File.exists() is true only if the path exists and is a file. For a folder, use Directory(path).exists().

What kind of thing is at this path? #

When you do not know whether a path is a file, a folder or a link, ask FileSystemEntity.

import 'dart:io';

Future<void> describe(String path) async {
  final type = await FileSystemEntity.type(path);

  final label = switch (type) {
    FileSystemEntityType.file => 'a file',
    FileSystemEntityType.directory => 'a directory',
    FileSystemEntityType.link => 'a link',
    _ => 'not there',
  };
  print('$path is $label');
}

Future<void> main() async {
  await describe('pubspec.yaml');
  await describe('lib');
  await describe('nothing-here');
}
pubspec.yaml is a file
lib is a directory
nothing-here is not there

There are also the shortcuts FileSystemEntity.isFile(path) and FileSystemEntity.isDirectory(path).

Check, or just try? #

Checking and then acting leaves a small gap in which another program could delete the file. For that reason, the most robust code simply attempts the operation and handles the failure.

import 'dart:io';

Future<String?> readIfPresent(String path) async {
  try {
    return await File(path).readAsString();
  } on PathNotFoundException {
    return null;
  }
}

Future<void> main() async {
  final text = await readIfPresent('maybe.txt');
  print(text ?? 'No such file, using defaults');
}
No such file, using defaults

Use exists() when you need to decide something, such as whether to create a default config file. Use try/catch when you are about to use the file.

Delete a file #

import 'dart:io';

Future<void> main() async {
  final file = File('temp.txt');
  await file.writeAsString('temporary');

  await file.delete();
  print('Still there? ${await file.exists()}');
}
Still there? false

Deleting is permanent. The file does not go to the recycle bin. Deleting a file that does not exist throws a PathNotFoundException.

Delete only if it exists #

import 'dart:io';

Future<bool> deleteIfExists(String path) async {
  try {
    await File(path).delete();
    return true;
  } on PathNotFoundException {
    return false;
  }
}

Future<void> main() async {
  print(await deleteIfExists('old-cache.txt'));
}

Rename or move #

rename returns a File for the new location. Renaming to a path in a different folder moves the file.

import 'dart:io';

Future<void> main() async {
  final draft = File('draft.txt');
  await draft.writeAsString('My essay');

  final finalFile = await draft.rename('essay.txt');

  print(finalFile.path);
  print(await draft.exists());
}
essay.txt
false

If the target already exists it is replaced. A rename across different drives may fail; in that case copy the file and delete the original.

Copy #

import 'dart:io';

Future<void> main() async {
  final original = File('essay.txt');
  final backup = await original.copy('essay_backup.txt');

  print('${await original.length()} bytes copied to ${backup.path}');
}

Delete safely #

Before deleting anything based on user input or a calculated path:

  • Print or log the full path first while you are developing.
  • Check that the path is inside the folder you expect.
  • Never build a delete path from unchecked user input.
  • For folders, delete(recursive: true) removes everything inside. Double-check the path. See directories.

Quick reference #

TaskCode
Does the file exist?await file.exists()
File, folder or link?await FileSystemEntity.type(path)
Deleteawait file.delete()
Rename or moveawait file.rename(newPath)
Copyawait file.copy(newPath)
Size in bytesawait file.length()

Try it yourself #

Write a “safe save” function: if data.txt already exists, rename it to data.bak (replacing any old backup), then write the new contents to data.txt. Test it by saving three times and checking both files.

Practise in the playground Updated by Santosh Adhikari