Migrate to null safety.
diff --git a/pkgs/watcher/lib/src/async_queue.dart b/pkgs/watcher/lib/src/async_queue.dart
index 37f13d6..93899e7 100644
--- a/pkgs/watcher/lib/src/async_queue.dart
+++ b/pkgs/watcher/lib/src/async_queue.dart
@@ -33,7 +33,8 @@
   /// Used to avoid top-leveling asynchronous errors.
   final Function _errorHandler;
 
-  AsyncQueue(this._processor, {Function onError}) : _errorHandler = onError;
+  AsyncQueue(this._processor, {required Function onError})
+      : _errorHandler = onError;
 
   /// Enqueues [item] to be processed and starts asynchronously processing it
   /// if a process isn't already running.
diff --git a/pkgs/watcher/lib/src/custom_watcher_factory.dart b/pkgs/watcher/lib/src/custom_watcher_factory.dart
index 3dc1bea..8f4132b 100644
--- a/pkgs/watcher/lib/src/custom_watcher_factory.dart
+++ b/pkgs/watcher/lib/src/custom_watcher_factory.dart
@@ -7,9 +7,9 @@
 /// A factory to produce custom watchers for specific file paths.
 class _CustomWatcherFactory {
   final String id;
-  final DirectoryWatcher Function(String path, {Duration pollingDelay})
+  final DirectoryWatcher? Function(String path, {Duration? pollingDelay})
       createDirectoryWatcher;
-  final FileWatcher Function(String path, {Duration pollingDelay})
+  final FileWatcher? Function(String path, {Duration? pollingDelay})
       createFileWatcher;
 
   _CustomWatcherFactory(
@@ -22,7 +22,7 @@
 /// registered more than once.
 /// [createDirectoryWatcher] and [createFileWatcher] should return watchers for
 /// the file paths they are able to handle. If the custom watcher is not able to
-/// handle the path it should reuturn null.
+/// handle the path it should return null.
 /// The paths handled by each custom watch may not overlap, at most one custom
 /// matcher may return a non-null watcher for a given path.
 ///
@@ -31,9 +31,10 @@
 /// will be used instead of the default.
 void registerCustomWatcher(
   String id,
-  DirectoryWatcher Function(String path, {Duration pollingDelay})
+  DirectoryWatcher Function(String path, {Duration? pollingDelay})?
       createDirectoryWatcher,
-  FileWatcher Function(String path, {Duration pollingDelay}) createFileWatcher,
+  FileWatcher Function(String path, {Duration? pollingDelay})?
+      createFileWatcher,
 ) {
   if (_customWatcherFactories.containsKey(id)) {
     throw ArgumentError('A custom watcher with id `$id` '
@@ -49,10 +50,10 @@
 ///
 /// Returns `null` if no custom watcher was applicable and throws a [StateError]
 /// if more than one was.
-DirectoryWatcher createCustomDirectoryWatcher(String path,
-    {Duration pollingDelay}) {
-  DirectoryWatcher customWatcher;
-  String customFactoryId;
+DirectoryWatcher? createCustomDirectoryWatcher(String path,
+    {Duration? pollingDelay}) {
+  DirectoryWatcher? customWatcher;
+  String? customFactoryId;
   for (var watcherFactory in _customWatcherFactories.values) {
     if (customWatcher != null) {
       throw StateError('Two `CustomWatcherFactory`s applicable: '
@@ -69,9 +70,9 @@
 ///
 /// Returns `null` if no custom watcher was applicable and throws a [StateError]
 /// if more than one was.
-FileWatcher createCustomFileWatcher(String path, {Duration pollingDelay}) {
-  FileWatcher customWatcher;
-  String customFactoryId;
+FileWatcher? createCustomFileWatcher(String path, {Duration? pollingDelay}) {
+  FileWatcher? customWatcher;
+  String? customFactoryId;
   for (var watcherFactory in _customWatcherFactories.values) {
     if (customWatcher != null) {
       throw StateError('Two `CustomWatcherFactory`s applicable: '
diff --git a/pkgs/watcher/lib/src/directory_watcher.dart b/pkgs/watcher/lib/src/directory_watcher.dart
index 3fe5004..043ebab 100644
--- a/pkgs/watcher/lib/src/directory_watcher.dart
+++ b/pkgs/watcher/lib/src/directory_watcher.dart
@@ -28,7 +28,7 @@
   /// shorter will give more immediate feedback at the expense of doing more IO
   /// and higher CPU usage. Defaults to one second. Ignored for non-polling
   /// watchers.
-  factory DirectoryWatcher(String directory, {Duration pollingDelay}) {
+  factory DirectoryWatcher(String directory, {Duration? pollingDelay}) {
     if (FileSystemEntity.isWatchSupported) {
       var customWatcher =
           createCustomDirectoryWatcher(directory, pollingDelay: pollingDelay);
diff --git a/pkgs/watcher/lib/src/directory_watcher/linux.dart b/pkgs/watcher/lib/src/directory_watcher/linux.dart
index 7348cb4..06d3508 100644
--- a/pkgs/watcher/lib/src/directory_watcher/linux.dart
+++ b/pkgs/watcher/lib/src/directory_watcher/linux.dart
@@ -91,7 +91,7 @@
       } else {
         _files.add(entity.path);
       }
-    }, onError: (error, StackTrace stackTrace) {
+    }, onError: (Object error, StackTrace stackTrace) {
       _eventsController.addError(error, stackTrace);
       close();
     }, onDone: () {
@@ -155,13 +155,16 @@
         files.remove(event.path);
         dirs.remove(event.path);
 
-        changed.add(event.destination);
+        var destination = event.destination;
+        if (destination == null) continue;
+
+        changed.add(destination);
         if (event.isDirectory) {
-          files.remove(event.destination);
-          dirs.add(event.destination);
+          files.remove(destination);
+          dirs.add(destination);
         } else {
-          files.add(event.destination);
-          dirs.remove(event.destination);
+          files.add(destination);
+          dirs.remove(destination);
         }
       } else if (event is FileSystemDeleteEvent) {
         files.remove(event.path);
@@ -221,7 +224,7 @@
         _files.add(entity.path);
         _emit(ChangeType.ADD, entity.path);
       }
-    }, onError: (error, StackTrace stackTrace) {
+    }, onError: (Object error, StackTrace stackTrace) {
       // Ignore an exception caused by the dir not existing. It's fine if it
       // was added and then quickly removed.
       if (error is FileSystemException) return;
@@ -258,8 +261,10 @@
   /// Like [Stream.listen], but automatically adds the subscription to
   /// [_subscriptions] so that it can be canceled when [close] is called.
   void _listen<T>(Stream<T> stream, void Function(T) onData,
-      {Function onError, void Function() onDone, bool cancelOnError}) {
-    StreamSubscription subscription;
+      {Function? onError,
+      void Function()? onDone,
+      bool cancelOnError = false}) {
+    late StreamSubscription subscription;
     subscription = stream.listen(onData, onError: onError, onDone: () {
       _subscriptions.remove(subscription);
       onDone?.call();
diff --git a/pkgs/watcher/lib/src/directory_watcher/mac_os.dart b/pkgs/watcher/lib/src/directory_watcher/mac_os.dart
index 4408ff1..e44b7f5 100644
--- a/pkgs/watcher/lib/src/directory_watcher/mac_os.dart
+++ b/pkgs/watcher/lib/src/directory_watcher/mac_os.dart
@@ -64,11 +64,11 @@
   ///
   /// This is separate from [_listSubscriptions] because this stream
   /// occasionally needs to be resubscribed in order to work around issue 14849.
-  StreamSubscription<List<FileSystemEvent>> _watchSubscription;
+  StreamSubscription<List<FileSystemEvent>>? _watchSubscription;
 
   /// The subscription to the [Directory.list] call for the initial listing of
   /// the directory to determine its initial state.
-  StreamSubscription<FileSystemEntity> _initialListSubscription;
+  StreamSubscription<FileSystemEntity>? _initialListSubscription;
 
   /// The subscriptions to [Directory.list] calls for listing the contents of a
   /// subdirectory that was moved into the watched directory.
@@ -76,7 +76,7 @@
 
   /// The timer for tracking how long we wait for an initial batch of bogus
   /// events (see issue 14373).
-  Timer _bogusEventTimer;
+  late Timer _bogusEventTimer;
 
   _MacOSDirectoryWatcher(String path)
       : path = path,
@@ -144,14 +144,14 @@
 
           if (_files.containsDir(path)) continue;
 
-          StreamSubscription<FileSystemEntity> subscription;
+          late StreamSubscription<FileSystemEntity> subscription;
           subscription = Directory(path).list(recursive: true).listen((entity) {
             if (entity is Directory) return;
             if (_files.contains(path)) return;
 
             _emitEvent(ChangeType.ADD, entity.path);
             _files.add(entity.path);
-          }, onError: (e, StackTrace stackTrace) {
+          }, onError: (Object e, StackTrace stackTrace) {
             _emitError(e, stackTrace);
           }, onDone: () {
             _listSubscriptions.remove(subscription);
@@ -192,7 +192,10 @@
     var directories = unionAll(batch.map((event) {
       if (!event.isDirectory) return <String>{};
       if (event is FileSystemMoveEvent) {
-        return {event.path, event.destination};
+        var destination = event.destination;
+        if (destination != null) {
+          return {event.path, destination};
+        }
       }
       return {event.path};
     }));
@@ -224,7 +227,7 @@
   /// If [batch] does contain contradictory events, this returns `null` to
   /// indicate that the state of the path on the filesystem should be checked to
   /// determine what occurred.
-  FileSystemEvent _canonicalEvent(Set<FileSystemEvent> batch) {
+  FileSystemEvent? _canonicalEvent(Set<FileSystemEvent> batch) {
     // An empty batch indicates that we've learned earlier that the batch is
     // contradictory (e.g. because of a move).
     if (batch.isEmpty) return null;
@@ -394,7 +397,7 @@
   }
 
   /// Emit an error, then close the watcher.
-  void _emitError(error, StackTrace stackTrace) {
+  void _emitError(Object error, StackTrace stackTrace) {
     _eventsController.addError(error, stackTrace);
     close();
   }
diff --git a/pkgs/watcher/lib/src/directory_watcher/polling.dart b/pkgs/watcher/lib/src/directory_watcher/polling.dart
index 902e8b8..968c9c6 100644
--- a/pkgs/watcher/lib/src/directory_watcher/polling.dart
+++ b/pkgs/watcher/lib/src/directory_watcher/polling.dart
@@ -24,7 +24,7 @@
   /// will pause between successive polls of the directory contents. Making this
   /// shorter will give more immediate feedback at the expense of doing more IO
   /// and higher CPU usage. Defaults to one second.
-  PollingDirectoryWatcher(String directory, {Duration pollingDelay})
+  PollingDirectoryWatcher(String directory, {Duration? pollingDelay})
       : super(directory, () {
           return _PollingDirectoryWatcher(
               directory, pollingDelay ?? Duration(seconds: 1));
@@ -56,12 +56,12 @@
   /// The previous modification times of the files in the directory.
   ///
   /// Used to tell which files have been modified.
-  final _lastModifieds = <String, DateTime>{};
+  final _lastModifieds = <String, DateTime?>{};
 
   /// The subscription used while [directory] is being listed.
   ///
   /// Will be `null` if a list is not currently happening.
-  StreamSubscription<FileSystemEntity> _listSubscription;
+  StreamSubscription<FileSystemEntity>? _listSubscription;
 
   /// The queue of files waiting to be processed to see if they have been
   /// modified.
@@ -70,7 +70,7 @@
   /// queue exists to let each of those proceed at their own rate. The lister
   /// will enqueue files as quickly as it can. Meanwhile, files are dequeued
   /// and processed sequentially.
-  AsyncQueue<String> _filesToProcess;
+  late AsyncQueue<String?> _filesToProcess;
 
   /// The set of files that have been seen in the current directory listing.
   ///
@@ -79,8 +79,8 @@
   final _polledFiles = <String>{};
 
   _PollingDirectoryWatcher(this.path, this._pollingDelay) {
-    _filesToProcess =
-        AsyncQueue<String>(_processFile, onError: (e, StackTrace stackTrace) {
+    _filesToProcess = AsyncQueue<String?>(_processFile,
+        onError: (Object e, StackTrace stackTrace) {
       if (!_events.isClosed) _events.addError(e, stackTrace);
     });
 
@@ -120,7 +120,7 @@
 
       if (entity is! File) return;
       _filesToProcess.add(entity.path);
-    }, onError: (error, StackTrace stackTrace) {
+    }, onError: (Object error, StackTrace stackTrace) {
       if (!isDirectoryNotFoundException(error)) {
         // It's some unknown error. Pipe it over to the event stream so the
         // user can see it.
@@ -136,7 +136,7 @@
 
   /// Processes [file] to determine if it has been modified since the last
   /// time it was scanned.
-  Future<void> _processFile(String file) async {
+  Future<void> _processFile(String? file) async {
     // `null` is the sentinel which means the directory listing is complete.
     if (file == null) {
       await _completePoll();
diff --git a/pkgs/watcher/lib/src/directory_watcher/windows.dart b/pkgs/watcher/lib/src/directory_watcher/windows.dart
index 98e9639..7640373 100644
--- a/pkgs/watcher/lib/src/directory_watcher/windows.dart
+++ b/pkgs/watcher/lib/src/directory_watcher/windows.dart
@@ -28,7 +28,7 @@
 class _EventBatcher {
   static const Duration _BATCH_DELAY = Duration(milliseconds: 100);
   final List<FileSystemEvent> events = [];
-  Timer timer;
+  Timer? timer;
 
   void addEvent(FileSystemEvent event, void Function() callback) {
     events.add(event);
@@ -37,7 +37,7 @@
   }
 
   void cancelTimer() {
-    timer.cancel();
+    timer?.cancel();
   }
 }
 
@@ -71,16 +71,16 @@
   final PathSet _files;
 
   /// The subscription to the stream returned by [Directory.watch].
-  StreamSubscription<FileSystemEvent> _watchSubscription;
+  StreamSubscription<FileSystemEvent>? _watchSubscription;
 
   /// The subscription to the stream returned by [Directory.watch] of the
   /// parent directory to [directory]. This is needed to detect changes to
   /// [directory], as they are not included on Windows.
-  StreamSubscription<FileSystemEvent> _parentWatchSubscription;
+  StreamSubscription<FileSystemEvent>? _parentWatchSubscription;
 
   /// The subscription to the [Directory.list] call for the initial listing of
   /// the directory to determine its initial state.
-  StreamSubscription<FileSystemEntity> _initialListSubscription;
+  StreamSubscription<FileSystemEntity>? _initialListSubscription;
 
   /// The subscriptions to the [Directory.list] calls for listing the contents
   /// of subdirectories that were moved into the watched directory.
@@ -148,7 +148,7 @@
       // Ignore errors, simply close the stream. The user listens on
       // [directory], and while it can fail to listen on the parent, we may
       // still be able to listen on the path requested.
-      _parentWatchSubscription.cancel();
+      _parentWatchSubscription?.cancel();
       _parentWatchSubscription = null;
     });
   }
@@ -184,7 +184,7 @@
           if (_files.containsDir(path)) continue;
 
           var stream = Directory(path).list(recursive: true);
-          StreamSubscription<FileSystemEntity> subscription;
+          late StreamSubscription<FileSystemEntity> subscription;
           subscription = stream.listen((entity) {
             if (entity is Directory) return;
             if (_files.contains(path)) return;
@@ -193,7 +193,7 @@
             _files.add(entity.path);
           }, onDone: () {
             _listSubscriptions.remove(subscription);
-          }, onError: (e, StackTrace stackTrace) {
+          }, onError: (Object e, StackTrace stackTrace) {
             _listSubscriptions.remove(subscription);
             _emitError(e, stackTrace);
           }, cancelOnError: true);
@@ -229,7 +229,10 @@
     var directories = unionAll(batch.map((event) {
       if (!event.isDirectory) return <String>{};
       if (event is FileSystemMoveEvent) {
-        return {event.path, event.destination};
+        var destination = event.destination;
+        if (destination != null) {
+          return {event.path, destination};
+        }
       }
       return {event.path};
     }));
@@ -244,7 +247,10 @@
 
     for (var event in batch) {
       if (event is FileSystemMoveEvent) {
-        addEvent(event.destination, event);
+        var destination = event.destination;
+        if (destination != null) {
+          addEvent(destination, event);
+        }
       }
       addEvent(event.path, event);
     }
@@ -262,7 +268,7 @@
   /// If [batch] does contain contradictory events, this returns `null` to
   /// indicate that the state of the path on the filesystem should be checked to
   /// determine what occurred.
-  FileSystemEvent _canonicalEvent(Set<FileSystemEvent> batch) {
+  FileSystemEvent? _canonicalEvent(Set<FileSystemEvent> batch) {
     // An empty batch indicates that we've learned earlier that the batch is
     // contradictory (e.g. because of a move).
     if (batch.isEmpty) return null;
@@ -386,7 +392,7 @@
     }, (error, stackTrace) {
       if (error is FileSystemException &&
           error.message.startsWith('Directory watcher closed unexpectedly')) {
-        _watchSubscription.cancel();
+        _watchSubscription?.cancel();
         _eventsController.addError(error, stackTrace);
         _startWatch();
       } else {
@@ -421,7 +427,7 @@
   }
 
   /// Emit an error, then close the watcher.
-  void _emitError(error, StackTrace stackTrace) {
+  void _emitError(Object error, StackTrace stackTrace) {
     _eventsController.addError(error, stackTrace);
     close();
   }
diff --git a/pkgs/watcher/lib/src/file_watcher.dart b/pkgs/watcher/lib/src/file_watcher.dart
index c41e5e6..9b8ecc4 100644
--- a/pkgs/watcher/lib/src/file_watcher.dart
+++ b/pkgs/watcher/lib/src/file_watcher.dart
@@ -29,7 +29,7 @@
   /// shorter will give more immediate feedback at the expense of doing more IO
   /// and higher CPU usage. Defaults to one second. Ignored for non-polling
   /// watchers.
-  factory FileWatcher(String file, {Duration pollingDelay}) {
+  factory FileWatcher(String file, {Duration? pollingDelay}) {
     var customWatcher =
         createCustomFileWatcher(file, pollingDelay: pollingDelay);
     if (customWatcher != null) return customWatcher;
diff --git a/pkgs/watcher/lib/src/file_watcher/native.dart b/pkgs/watcher/lib/src/file_watcher/native.dart
index 0b42cb9..f7d92d4 100644
--- a/pkgs/watcher/lib/src/file_watcher/native.dart
+++ b/pkgs/watcher/lib/src/file_watcher/native.dart
@@ -33,7 +33,7 @@
   Future get ready => _readyCompleter.future;
   final _readyCompleter = Completer();
 
-  StreamSubscription _subscription;
+  StreamSubscription? _subscription;
 
   _NativeFileWatcher(this.path) {
     _listen();
diff --git a/pkgs/watcher/lib/src/file_watcher/polling.dart b/pkgs/watcher/lib/src/file_watcher/polling.dart
index 11c0c6d..b208a4b 100644
--- a/pkgs/watcher/lib/src/file_watcher/polling.dart
+++ b/pkgs/watcher/lib/src/file_watcher/polling.dart
@@ -14,7 +14,7 @@
 
 /// Periodically polls a file for changes.
 class PollingFileWatcher extends ResubscribableWatcher implements FileWatcher {
-  PollingFileWatcher(String path, {Duration pollingDelay})
+  PollingFileWatcher(String path, {Duration? pollingDelay})
       : super(path, () {
           return _PollingFileWatcher(
               path, pollingDelay ?? Duration(seconds: 1));
@@ -37,13 +37,13 @@
   final _readyCompleter = Completer();
 
   /// The timer that controls polling.
-  Timer _timer;
+  late Timer _timer;
 
   /// The previous modification time of the file.
   ///
   /// Used to tell when the file was modified. This is `null` before the file's
   /// mtime has first been checked.
-  DateTime _lastModified;
+  DateTime? _lastModified;
 
   _PollingFileWatcher(this.path, Duration pollingDelay) {
     _timer = Timer.periodic(pollingDelay, (_) => _poll());
@@ -64,7 +64,7 @@
       return;
     }
 
-    DateTime modified;
+    DateTime? modified;
     try {
       modified = await modificationTime(path);
     } on FileSystemException catch (error, stackTrace) {
diff --git a/pkgs/watcher/lib/src/path_set.dart b/pkgs/watcher/lib/src/path_set.dart
index 41a0a39..090090e 100644
--- a/pkgs/watcher/lib/src/path_set.dart
+++ b/pkgs/watcher/lib/src/path_set.dart
@@ -119,8 +119,9 @@
     var entry = _entries;
 
     for (var part in p.split(path)) {
-      entry = entry.contents[part];
-      if (entry == null) return false;
+      var child = entry.contents[part];
+      if (child == null) return false;
+      entry = child;
     }
 
     return entry.isExplicit;
@@ -132,8 +133,9 @@
     var entry = _entries;
 
     for (var part in p.split(path)) {
-      entry = entry.contents[part];
-      if (entry == null) return false;
+      var child = entry.contents[part];
+      if (child == null) return false;
+      entry = child;
     }
 
     return entry.contents.isNotEmpty;
@@ -144,9 +146,9 @@
     var result = <String>[];
 
     void recurse(_Entry dir, String path) {
-      for (var name in dir.contents.keys) {
-        var entry = dir.contents[name];
-        var entryPath = p.join(path, name);
+      for (var mapEntry in dir.contents.entries) {
+        var entry = mapEntry.value;
+        var entryPath = p.join(path, mapEntry.key);
         if (entry.isExplicit) result.add(entryPath);
         recurse(entry, entryPath);
       }
diff --git a/pkgs/watcher/lib/src/resubscribable.dart b/pkgs/watcher/lib/src/resubscribable.dart
index 0719096..1c4bb25 100644
--- a/pkgs/watcher/lib/src/resubscribable.dart
+++ b/pkgs/watcher/lib/src/resubscribable.dart
@@ -29,7 +29,7 @@
 
   @override
   Stream<WatchEvent> get events => _eventsController.stream;
-  StreamController<WatchEvent> _eventsController;
+  late StreamController<WatchEvent> _eventsController;
 
   @override
   bool get isReady => _readyCompleter.isCompleted;
@@ -41,8 +41,8 @@
   /// Creates a new [ResubscribableWatcher] wrapping the watchers
   /// emitted by [_factory].
   ResubscribableWatcher(this.path, this._factory) {
-    ManuallyClosedWatcher watcher;
-    StreamSubscription subscription;
+    late ManuallyClosedWatcher watcher;
+    late StreamSubscription subscription;
 
     _eventsController = StreamController<WatchEvent>.broadcast(
         onListen: () async {
diff --git a/pkgs/watcher/lib/src/stat.dart b/pkgs/watcher/lib/src/stat.dart
index 08cf935..06e3feb 100644
--- a/pkgs/watcher/lib/src/stat.dart
+++ b/pkgs/watcher/lib/src/stat.dart
@@ -8,7 +8,7 @@
 /// the file at that path.
 typedef MockTimeCallback = DateTime Function(String path);
 
-MockTimeCallback _mockTimeCallback;
+MockTimeCallback? _mockTimeCallback;
 
 /// Overrides the default behavior for accessing a file's modification time
 /// with [callback].
@@ -21,9 +21,11 @@
 }
 
 /// Gets the modification time for the file at [path].
-Future<DateTime> modificationTime(String path) async {
-  if (_mockTimeCallback != null) {
-    return _mockTimeCallback(path);
+/// Completes with `null` if the file does not exist.
+Future<DateTime?> modificationTime(String path) async {
+  var mockTimeCallback = _mockTimeCallback;
+  if (mockTimeCallback != null) {
+    return mockTimeCallback(path);
   }
 
   final stat = await FileStat.stat(path);
diff --git a/pkgs/watcher/lib/src/utils.dart b/pkgs/watcher/lib/src/utils.dart
index 24b8184..66c59d3 100644
--- a/pkgs/watcher/lib/src/utils.dart
+++ b/pkgs/watcher/lib/src/utils.dart
@@ -8,12 +8,12 @@
 
 /// Returns `true` if [error] is a [FileSystemException] for a missing
 /// directory.
-bool isDirectoryNotFoundException(error) {
+bool isDirectoryNotFoundException(Object error) {
   if (error is! FileSystemException) return false;
 
   // See dartbug.com/12461 and tests/standalone/io/directory_error_test.dart.
   var notFoundCode = Platform.operatingSystem == 'windows' ? 3 : 2;
-  return error.osError.errorCode == notFoundCode;
+  return error.osError?.errorCode == notFoundCode;
 }
 
 /// Returns the union of all elements in each set in [sets].
diff --git a/pkgs/watcher/lib/watcher.dart b/pkgs/watcher/lib/watcher.dart
index 01336ab..22e0d6e 100644
--- a/pkgs/watcher/lib/watcher.dart
+++ b/pkgs/watcher/lib/watcher.dart
@@ -57,7 +57,7 @@
   /// shorter will give more immediate feedback at the expense of doing more IO
   /// and higher CPU usage. Defaults to one second. Ignored for non-polling
   /// watchers.
-  factory Watcher(String path, {Duration pollingDelay}) {
+  factory Watcher(String path, {Duration? pollingDelay}) {
     if (File(path).existsSync()) {
       return FileWatcher(path, pollingDelay: pollingDelay);
     } else {
diff --git a/pkgs/watcher/pubspec.yaml b/pkgs/watcher/pubspec.yaml
index c4cafad..8567e9b 100644
--- a/pkgs/watcher/pubspec.yaml
+++ b/pkgs/watcher/pubspec.yaml
@@ -1,5 +1,5 @@
 name: watcher
-version: 0.9.8-dev
+version: 0.10.0-nullsafety.0
 
 description: >-
   A file system watcher. It monitors changes to contents of directories and
@@ -7,14 +7,14 @@
 repository: https://github.com/dart-lang/watcher
 
 environment:
-  sdk: '>=2.8.4 <3.0.0'
+  sdk: '>=2.12.0-0 <3.0.0'
 
 dependencies:
-  async: ^2.0.0
-  path: ^1.0.0
-  pedantic: ^1.1.0
+  async: ^2.5.0-nullsafety.3
+  path: ^1.8.0-nullsafety.3
+  pedantic: ^1.10.0-nullsafety.3
 
 dev_dependencies:
-  benchmark_harness: ^1.0.4
-  test: ^1.0.0
-  test_descriptor: ^1.0.0
+  benchmark_harness: ^2.0.0-nullsafety.0
+  test: ^1.16.0-nullsafety.13
+  test_descriptor: ^2.0.0-nullsafety
diff --git a/pkgs/watcher/test/custom_watcher_factory_test.dart b/pkgs/watcher/test/custom_watcher_factory_test.dart
index 8210c06..89f8e3c 100644
--- a/pkgs/watcher/test/custom_watcher_factory_test.dart
+++ b/pkgs/watcher/test/custom_watcher_factory_test.dart
@@ -4,7 +4,7 @@
 import 'package:watcher/watcher.dart';
 
 void main() {
-  _MemFs memFs;
+  late _MemFs memFs;
   final defaultFactoryId = 'MemFs';
 
   setUpAll(() {
@@ -16,7 +16,7 @@
         watcherFactory.createFileWatcher);
   });
 
-  test('notifes for files', () async {
+  test('notifies for files', () async {
     var watcher = FileWatcher('file.txt');
 
     var completer = Completer<WatchEvent>();
@@ -29,7 +29,7 @@
     expect(event.path, 'file.txt');
   });
 
-  test('notifes for directories', () async {
+  test('notifies for directories', () async {
     var watcher = DirectoryWatcher('dir');
 
     var completer = Completer<WatchEvent>();
@@ -45,7 +45,7 @@
   test('registering twice throws', () async {
     expect(
         () => registerCustomWatcher(defaultFactoryId,
-            (_, {pollingDelay}) => null, (_, {pollingDelay}) => null),
+            (_, {pollingDelay}) => throw 0, (_, {pollingDelay}) => throw 0),
         throwsA(isA<ArgumentError>()));
   });
 
@@ -117,9 +117,9 @@
   _MemFsWatcherFactory(this._memFs);
 
   DirectoryWatcher createDirectoryWatcher(String path,
-          {Duration pollingDelay}) =>
+          {Duration? pollingDelay}) =>
       _MemFsWatcher(path, _memFs.watchStream(path));
 
-  FileWatcher createFileWatcher(String path, {Duration pollingDelay}) =>
+  FileWatcher createFileWatcher(String path, {Duration? pollingDelay}) =>
       _MemFsWatcher(path, _memFs.watchStream(path));
 }
diff --git a/pkgs/watcher/test/path_set_test.dart b/pkgs/watcher/test/path_set_test.dart
index 25cf969..61ab2cd 100644
--- a/pkgs/watcher/test/path_set_test.dart
+++ b/pkgs/watcher/test/path_set_test.dart
@@ -15,7 +15,7 @@
     'set contains directory "$path"');
 
 void main() {
-  PathSet paths;
+  late PathSet paths;
   setUp(() => paths = PathSet('root'));
 
   group('adding a path', () {
diff --git a/pkgs/watcher/test/utils.dart b/pkgs/watcher/test/utils.dart
index 6e7686c..9b4fd28 100644
--- a/pkgs/watcher/test/utils.dart
+++ b/pkgs/watcher/test/utils.dart
@@ -30,12 +30,12 @@
 /// increment the mod time for that file instantly.
 final _mockFileModificationTimes = <String, int>{};
 
-WatcherFactory _watcherFactory;
+late WatcherFactory _watcherFactory;
 
 /// Creates a new [Watcher] that watches a temporary file or directory.
 ///
 /// If [path] is provided, watches a subdirectory in the sandbox with that name.
-Watcher createWatcher({String path}) {
+Watcher createWatcher({String? path}) {
   if (path == null) {
     path = d.sandbox;
   } else {
@@ -46,13 +46,13 @@
 }
 
 /// The stream of events from the watcher started with [startWatcher].
-StreamQueue<WatchEvent> _watcherEvents;
+late StreamQueue<WatchEvent> _watcherEvents;
 
 /// Creates a new [Watcher] that watches a temporary file or directory and
 /// starts monitoring it for events.
 ///
 /// If [path] is provided, watches a path in the sandbox with that name.
-Future<Null> startWatcher({String path}) async {
+Future<void> startWatcher({String? path}) async {
   mockGetModificationTime((path) {
     final normalized = p.normalize(p.relative(path, from: d.sandbox));
 
@@ -86,7 +86,7 @@
 
 /// A list of [StreamMatcher]s that have been collected using
 /// [_collectStreamMatcher].
-List<StreamMatcher> _collectedStreamMatchers;
+List<StreamMatcher>? _collectedStreamMatchers;
 
 /// Collects all stream matchers that are registered within [block] into a
 /// single stream matcher.
@@ -94,10 +94,10 @@
 /// The returned matcher will match each of the collected matchers in order.
 StreamMatcher _collectStreamMatcher(void Function() block) {
   var oldStreamMatchers = _collectedStreamMatchers;
-  _collectedStreamMatchers = <StreamMatcher>[];
+  var collectedStreamMatchers = _collectedStreamMatchers = <StreamMatcher>[];
   try {
     block();
-    return emitsInOrder(_collectedStreamMatchers);
+    return emitsInOrder(collectedStreamMatchers);
   } finally {
     _collectedStreamMatchers = oldStreamMatchers;
   }
@@ -108,9 +108,10 @@
 ///
 /// [streamMatcher] can be a [StreamMatcher], a [Matcher], or a value.
 Future _expectOrCollect(streamMatcher) {
-  if (_collectedStreamMatchers != null) {
-    _collectedStreamMatchers.add(emits(streamMatcher));
-    return null;
+  var collectedStreamMatchers = _collectedStreamMatchers;
+  if (collectedStreamMatchers != null) {
+    collectedStreamMatchers.add(emits(streamMatcher));
+    return Future.sync(() {});
   } else {
     return expectLater(_watcherEvents, emits(streamMatcher));
   }
@@ -202,7 +203,7 @@
 ///
 /// If [contents] is omitted, creates an empty file. If [updateModified] is
 /// `false`, the mock file modification time is not changed.
-void writeFile(String path, {String contents, bool updateModified}) {
+void writeFile(String path, {String? contents, bool? updateModified}) {
   contents ??= '';
   updateModified ??= true;
 
@@ -219,8 +220,8 @@
   if (updateModified) {
     path = p.normalize(path);
 
-    _mockFileModificationTimes.putIfAbsent(path, () => 0);
-    _mockFileModificationTimes[path]++;
+    _mockFileModificationTimes.update(path, (value) => value + 1,
+        ifAbsent: () => 1);
   }
 }
 
@@ -236,8 +237,8 @@
   // Make sure we always use the same separator on Windows.
   to = p.normalize(to);
 
-  _mockFileModificationTimes.putIfAbsent(to, () => 0);
-  _mockFileModificationTimes[to]++;
+  _mockFileModificationTimes.update(to, (value) => value + 1,
+      ifAbsent: () => 1);
 }
 
 /// Schedules creating a directory in the sandbox at [path].
@@ -261,7 +262,7 @@
 /// Returns a set of all values returns by [callback].
 ///
 /// [limit] defaults to 3.
-Set<S> withPermutations<S>(S Function(int, int, int) callback, {int limit}) {
+Set<S> withPermutations<S>(S Function(int, int, int) callback, {int? limit}) {
   limit ??= 3;
   var results = <S>{};
   for (var i = 0; i < limit; i++) {