watch method
Starts watching path with the provided config.
Returns a broadcast Stream of FileEvents. Only one watcher per path is active at a time; calling watch again for the same path replaces the previous watcher.
Implementation
Stream<FileEvent> watch(
String path, {
WatcherConfig config = const WatcherConfig(),
}) {
// Cancel existing watcher for this path.
_subscriptions[path]?.cancel();
final controller = StreamController<FileEvent>.broadcast();
final compiledIgnore = config.ignorePatterns
.map(GlobMatcher.compile)
.toList();
final entity = FileSystemEntity.isDirectorySync(path)
? Directory(path)
: File(path);
// Debounce tracking.
final lastEmit = <String, DateTime>{};
final sub = entity.watch(recursive: config.recursive).listen((fse) {
final eventPath = normalizePath(fse.path);
// Filter by extension.
if (config.filters.isNotEmpty) {
final ext = getExtension(eventPath);
if (!config.filters.contains(ext)) return;
}
// Ignore hidden.
if (config.ignoreHidden && isHidden(eventPath)) return;
// Ignore patterns.
for (final glob in compiledIgnore) {
if (glob.match(eventPath)) return;
}
// Debounce.
final now = DateTime.now();
final last = lastEmit[eventPath];
if (last != null && now.difference(last) < config.debounce) return;
lastEmit[eventPath] = now;
final type = switch (fse.type) {
FileSystemEvent.create => FileEventType.create,
FileSystemEvent.modify => FileEventType.modify,
FileSystemEvent.delete => FileEventType.delete,
FileSystemEvent.move => FileEventType.rename,
_ => FileEventType.modify,
};
controller.add(FileEvent(type: type, path: eventPath, timestamp: now));
});
_subscriptions[path] = sub;
// Clean up when the controller has no listeners.
controller.onCancel = () {
sub.cancel();
_subscriptions.remove(path);
};
return controller.stream;
}