ZetCode

Dart FileSystemMoveEvent

last modified October 18, 2026

The FileSystemMoveEvent class in Dart represents file system move or rename operations. It's part of Dart's file system event monitoring API.

This event is triggered when files or directories are moved or renamed within a watched directory. It provides details about both old and new paths.

Basic Definition

FileSystemMoveEvent is a subclass of FileSystemEvent. It contains information about file/directory move operations in the file system.

Key properties include the old path (source) and the new path (destination) of the moved item. The destination is nullable because some platforms cannot always determine it. The event is emitted by Directory.watch() when moves occur.

Basic FileSystemMoveEvent Usage

This example shows how to watch for file move events in a directory.

main.dart
import 'dart:io';

void main() async {
  var dir = Directory('test_dir');
  await dir.create();
  
  var subscription = dir.watch().listen((event) {
    if (event is FileSystemMoveEvent) {
      print('Move detected:');
      print('  From: ${event.path}');
      print('  To: ${event.destination ?? '(unknown)'}');
      print('  Type: ${event.runtimeType}');
    }
  });
  
  // Simulate a file move
  var file = File('${dir.path}/test.txt');
  await file.create();
  await file.rename('${dir.path}/renamed.txt');
  await Future.delayed(Duration(seconds: 1));
  await subscription.cancel();
}

We create a directory watcher and listen for move events. When a file is renamed, the event provides the original path and a nullable destination. The concrete event type is FileSystemMoveEvent.

$ dart main.dart
Move detected:
  From: test_dir/test.txt
  To: test_dir/renamed.txt
  Type: FileSystemMoveEvent

Handling Directory Moves

This example demonstrates detecting directory move operations.

main.dart
import 'dart:io';

void main() async {
  var dir = Directory('watch_dir');
  await dir.create();
  
  var subscription = dir.watch().listen((event) {
    if (event is FileSystemMoveEvent) {
      print('Directory moved:');
      print('  Source: ${event.path}');
      print('  Target: ${event.destination ?? '(unknown)'}');
    }
  });
  
  // Create and move a subdirectory
  var subDir = Directory('${dir.path}/subdir');
  await subDir.create();
  await subDir.rename('${dir.path}/moved_subdir');
  await Future.delayed(Duration(seconds: 1));
  await subscription.cancel();
}

Directory moves trigger similar events to file moves. The event contains the original and new paths of the moved directory.

$ dart main.dart
Directory moved:
  Source: watch_dir/subdir
  Target: watch_dir/moved_subdir

Renames and Moves Out of a Directory

This example shows how renames and moves are reported by a non-recursive directory watcher.

main.dart
import 'dart:io';

void main() async {
  var dir = Directory('monitor_dir');
  await dir.create();
  
  var subscription = dir.watch().listen((event) {
    if (event is FileSystemMoveEvent) {
      var destination = event.destination;
      print('Renamed/moved within watched directory:');
      print('  Old: ${event.path}');
      print('  New: ${destination ?? '(unknown)'}');
    } else if (event is FileSystemDeleteEvent) {
      print('Removed or moved out of watched directory: ${event.path}');
    }
  });
  
  // Rename within the watched directory
  var file = File('${dir.path}/original.txt');
  await file.create();
  await file.rename('${dir.path}/renamed.txt');
  
  // Move into a subdirectory. On Linux this is reported as a delete of the
  // source path because the non-recursive watcher does not see the subdirectory.
  var newDir = Directory('${dir.path}/new_location');
  await newDir.create();
  await File('${dir.path}/renamed.txt')
      .rename('${newDir.path}/moved.txt');
  
  await Future.delayed(Duration(seconds: 1));
  await subscription.cancel();
}

A rename whose destination stays in the watched directory produces a FileSystemMoveEvent with both paths. Moving a file into a subdirectory is reported differently: on Linux the non-recursive watcher sees the source path disappear and emits a FileSystemDeleteEvent. Recursive watching would capture the destination, but Dart's Linux backend does not support recursive watches.

$ dart main.dart
Renamed/moved within watched directory:
  Old: monitor_dir/original.txt
  New: monitor_dir/renamed.txt
Removed or moved out of watched directory: monitor_dir/renamed.txt

Error Handling in Move Events

This example demonstrates error handling during file system move operations.

main.dart
import 'dart:io';

void main() async {
  var dir = Directory('error_dir');
  await dir.create();
  
  var subscription = dir.watch().listen((event) {
    if (event is FileSystemMoveEvent) {
      print('Move event detected');
      var destination = event.destination;
      if (destination == null) {
        print('Destination unknown');
        return;
      }
      print('Destination exists: ${File(destination).existsSync()}');
    }
  });
  
  var file = File('${dir.path}/testfile.txt');
  await file.create();
  await file.rename('${dir.path}/renamed.txt');
  
  // Move to invalid location: no move event is emitted for a failed rename
  try {
    await File('${dir.path}/renamed.txt')
        .rename('${dir.path}/nonexistent/subdir/file.txt');
  } on FileSystemException catch (e) {
    print('Move failed: ${e.message}');
  }
  
  await Future.delayed(Duration(seconds: 1));
  await subscription.cancel();
}

A move event is emitted only after a successful rename, so the destination can be validated safely. A failed rename throws a FileSystemException instead of producing an event.

$ dart main.dart
Move event detected
Destination exists: true
Move failed: Cannot rename file to 'error_dir/nonexistent/subdir/file.txt'

Monitoring Multiple Events

This example shows handling multiple event types including move events.

main.dart
import 'dart:io';

void main() async {
  var dir = Directory('multi_event_dir');
  await dir.create();
  
  var seen = <String>{};
  var subscription = dir.watch().listen((event) {
    if (!seen.add('${event.type}:${event.path}')) return;
    
    switch (event.type) {
      case FileSystemEvent.move:
        var destination = (event as FileSystemMoveEvent).destination;
        print('Move event: ${event.path} → ${destination ?? '(unknown)'}');
        break;
      case FileSystemEvent.create:
        print('Create event: ${event.path}');
        break;
      case FileSystemEvent.delete:
        print('Delete event: ${event.path}');
        break;
      case FileSystemEvent.modify:
        print('Modify event: ${event.path}');
        break;
    }
  });
  
  // Trigger various events
  var file = File('${dir.path}/demo.txt');
  await file.create();
  await file.writeAsString('content');
  var renamed = await file.rename('${dir.path}/renamed_demo.txt');
  await renamed.delete();
  await Future.delayed(Duration(seconds: 1));
  await subscription.cancel();
}

We handle all file system event types with a switch statement. Move events are specifically cast to access the nullable destination property. Each event type has its own case.

$ dart main.dart
Create event: multi_event_dir/demo.txt
Modify event: multi_event_dir/demo.txt
Move event: multi_event_dir/demo.txt → multi_event_dir/renamed_demo.txt
Delete event: multi_event_dir/renamed_demo.txt

Best Practices

Source

Dart FileSystemMoveEvent Documentation

This tutorial covered Dart's FileSystemMoveEvent class with practical examples showing file and directory move detection, error handling, and event processing.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Dart tutorials.