Improve hot reload performance (#28152)

diff --git a/packages/flutter_tools/bin/fuchsia_attach.dart b/packages/flutter_tools/bin/fuchsia_attach.dart
index eb91c2d..b752efc 100644
--- a/packages/flutter_tools/bin/fuchsia_attach.dart
+++ b/packages/flutter_tools/bin/fuchsia_attach.dart
@@ -15,7 +15,6 @@
 import 'package:flutter_tools/src/commands/attach.dart';
 import 'package:flutter_tools/src/commands/doctor.dart';
 import 'package:flutter_tools/src/fuchsia/fuchsia_sdk.dart';
-import 'package:flutter_tools/src/run_hot.dart';
 import 'package:flutter_tools/src/runner/flutter_command.dart';
 
 final ArgParser parser = ArgParser()
@@ -100,8 +99,7 @@
         platformKernelDill: platformKernelDill,
         flutterPatchedSdk: flutterPatchedSdk,
       ),
-      HotRunnerConfig: () => HotRunnerConfig()..computeDartDependencies = false,
-    },
+    }
   );
 }
 
diff --git a/packages/flutter_tools/lib/src/commands/attach.dart b/packages/flutter_tools/lib/src/commands/attach.dart
index eb3c4c0..a485772 100644
--- a/packages/flutter_tools/lib/src/commands/attach.dart
+++ b/packages/flutter_tools/lib/src/commands/attach.dart
@@ -18,6 +18,7 @@
 import '../globals.dart';
 import '../ios/devices.dart';
 import '../ios/simulators.dart';
+import '../project.dart';
 import '../protocol_discovery.dart';
 import '../resident_runner.dart';
 import '../run_cold.dart';
@@ -129,6 +130,7 @@
   Future<FlutterCommandResult> runCommand() async {
     final String ipv4Loopback = InternetAddress.loopbackIPv4.address;
     final String ipv6Loopback = InternetAddress.loopbackIPv6.address;
+    final FlutterProject flutterProject = await FlutterProject.current();
 
     Cache.releaseLockEarly();
 
@@ -228,6 +230,7 @@
             projectRootPath: argResults['project-root'],
             dillOutputPath: argResults['output-dill'],
             ipv6: usesIpv6,
+            flutterProject: flutterProject,
           )
         : ColdRunner(
             flutterDevices,
@@ -289,6 +292,7 @@
     String dillOutputPath,
     bool stayResident = true,
     bool ipv6 = false,
+    FlutterProject flutterProject,
   }) => HotRunner(
     devices,
     target: target,
@@ -302,6 +306,7 @@
     dillOutputPath: dillOutputPath,
     stayResident: stayResident,
     ipv6: ipv6,
+    flutterProject: flutterProject,
   );
 }
 
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index 074caae..2f93ae5 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -19,6 +19,7 @@
 import '../device.dart';
 import '../emulator.dart';
 import '../globals.dart';
+import '../project.dart';
 import '../resident_runner.dart';
 import '../run_cold.dart';
 import '../run_hot.dart';
@@ -341,6 +342,7 @@
     if (await device.isLocalEmulator && !options.buildInfo.supportsEmulator) {
       throw '${toTitleCase(options.buildInfo.friendlyModeName)} mode is not supported for emulators.';
     }
+    final FlutterProject flutterProject = await FlutterProject.current();
 
     // We change the current working directory for the duration of the `start` command.
     final Directory cwd = fs.currentDirectory;
@@ -368,6 +370,7 @@
         dillOutputPath: dillOutputPath,
         ipv6: ipv6,
         hostIsIde: true,
+        flutterProject: flutterProject,
       );
     } else {
       runner = ColdRunner(
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index 507ea45..4edaf21 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -13,6 +13,7 @@
 import '../device.dart';
 import '../globals.dart';
 import '../ios/mac.dart';
+import '../project.dart';
 import '../resident_runner.dart';
 import '../run_cold.dart';
 import '../run_hot.dart';
@@ -280,6 +281,7 @@
     // Enable hot mode by default if `--no-hot` was not passed and we are in
     // debug mode.
     final bool hotMode = shouldUseHotMode();
+    final FlutterProject flutterProject = await FlutterProject.current();
 
     writePidFile(argResults['pid-file']);
 
@@ -389,6 +391,7 @@
         saveCompilationTrace: argResults['train'],
         stayResident: stayResident,
         ipv6: ipv6,
+        flutterProject: flutterProject,
       );
     } else {
       runner = ColdRunner(
diff --git a/packages/flutter_tools/lib/src/commands/update_packages.dart b/packages/flutter_tools/lib/src/commands/update_packages.dart
index 254826a..a019bb8 100644
--- a/packages/flutter_tools/lib/src/commands/update_packages.dart
+++ b/packages/flutter_tools/lib/src/commands/update_packages.dart
@@ -1093,7 +1093,7 @@
 
 /// Generates the File object for the pubspec.yaml file of a given Directory.
 File _pubspecFor(Directory directory) {
-  return fs.file('${directory.path}/pubspec.yaml');
+  return fs.file(fs.path.join(directory.path, 'pubspec.yaml'));
 }
 
 /// Generates the source of a fake pubspec.yaml file given a list of
diff --git a/packages/flutter_tools/lib/src/dart/dependencies.dart b/packages/flutter_tools/lib/src/dart/dependencies.dart
deleted file mode 100644
index 378081a..0000000
--- a/packages/flutter_tools/lib/src/dart/dependencies.dart
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// TODO(dnfield): This will be removed when @jonahwilliams' work on build lands
-// ignore: deprecated_member_use
-import 'package:analyzer/analyzer.dart' as analyzer;
-
-import '../base/file_system.dart';
-import '../dart/package_map.dart';
-
-// List of flutter specific environment configurations.
-// See https://github.com/munificent/dep-interface-libraries/blob/master/Proposal.md
-// We will populate this list as required. Potentially, all of dart:* libraries
-// supported by flutter would end up here.
-final List<String> _configurationConstants = <String>['dart.library.io'];
-
-String _dottedNameToString(analyzer.DottedName dottedName) {
-  String result = '';
-  for (analyzer.SimpleIdentifier identifier in dottedName.components) {
-    if (result.isEmpty) {
-      result += identifier.token.lexeme;
-    } else {
-      result += '.' + identifier.token.lexeme;
-    }
-  }
-  return result;
-}
-
-class DartDependencySetBuilder {
-  DartDependencySetBuilder(String mainScriptPath, String packagesFilePath)
-    : _mainScriptPath = canonicalizePath(mainScriptPath),
-      _mainScriptUri = fs.path.toUri(mainScriptPath),
-      _packagesFilePath = canonicalizePath(packagesFilePath);
-
-  final String _mainScriptPath;
-  final String _packagesFilePath;
-
-  final Uri _mainScriptUri;
-
-  Set<String> build() {
-    final List<String> dependencies = <String>[_mainScriptPath, _packagesFilePath];
-    final List<Uri> toProcess = <Uri>[_mainScriptUri];
-    final PackageMap packageMap = PackageMap(_packagesFilePath);
-
-    while (toProcess.isNotEmpty) {
-      final Uri currentUri = toProcess.removeLast();
-      final analyzer.CompilationUnit unit = _parse(currentUri.toFilePath());
-      for (analyzer.Directive directive in unit.directives) {
-        if (!(directive is analyzer.UriBasedDirective))
-          continue;
-
-        String uriAsString;
-        if (directive is analyzer.NamespaceDirective) {
-          final analyzer.NamespaceDirective namespaceDirective = directive;
-          // If the directive is a conditional import directive, we should
-          // select the imported uri based on the condition.
-          for (analyzer.Configuration configuration in namespaceDirective.configurations) {
-            if (_configurationConstants.contains(_dottedNameToString(configuration.name))) {
-              uriAsString = configuration.uri.stringValue;
-              break;
-            }
-          }
-        }
-        if (uriAsString == null) {
-          final analyzer.UriBasedDirective uriBasedDirective = directive;
-          uriAsString = uriBasedDirective.uri.stringValue;
-        }
-
-        Uri uri;
-        try {
-          uri = Uri.parse(uriAsString);
-        } on FormatException {
-          throw DartDependencyException('Unable to parse URI: $uriAsString');
-        }
-        Uri resolvedUri = analyzer.resolveRelativeUri(currentUri, uri);
-        if (resolvedUri.scheme.startsWith('dart'))
-          continue;
-        if (resolvedUri.scheme == 'package') {
-          final Uri newResolvedUri = packageMap.uriForPackage(resolvedUri);
-          if (newResolvedUri == null) {
-            throw DartDependencyException(
-              'The following Dart file:\n'
-              '  ${currentUri.toFilePath()}\n'
-              '...refers, in an import, to the following library:\n'
-              '  $resolvedUri\n'
-              'That library is in a package that is not known. Maybe you forgot to '
-              'mention it in your pubspec.yaml file?'
-            );
-          }
-          resolvedUri = newResolvedUri;
-        }
-        final String path = canonicalizePath(resolvedUri.toFilePath());
-        if (!dependencies.contains(path)) {
-          if (!fs.isFileSync(path)) {
-            throw DartDependencyException(
-              'The following Dart file:\n'
-              '  ${currentUri.toFilePath()}\n'
-              '...refers, in an import, to the following library:\n'
-              '  $path\n'
-              'Unfortunately, that library does not appear to exist on your file system.'
-            );
-          }
-          dependencies.add(path);
-          toProcess.add(resolvedUri);
-        }
-      }
-    }
-    return dependencies.toSet();
-  }
-
-  analyzer.CompilationUnit _parse(String path) {
-    String body;
-    try {
-      body = fs.file(path).readAsStringSync();
-    } on FileSystemException catch (error) {
-      throw DartDependencyException(
-        'Could not read "$path" when determining Dart dependencies.',
-        error,
-      );
-    }
-    try {
-      return analyzer.parseDirectives(body, name: path);
-    } on analyzer.AnalyzerError catch (error) {
-      throw DartDependencyException(
-        'When trying to parse this Dart file to find its dependencies:\n'
-        '  $path\n'
-        '...the analyzer failed with the following error:\n'
-        '  ${error.toString().trimRight()}',
-        error,
-      );
-    } on analyzer.AnalyzerErrorGroup catch (error) {
-      throw DartDependencyException(
-        'When trying to parse this Dart file to find its dependencies:\n'
-        '  $path\n'
-        '...the analyzer failed with the following error:\n'
-        '  ${error.toString().trimRight()}',
-        error,
-      );
-    }
-  }
-}
-
-class DartDependencyException implements Exception {
-  DartDependencyException(this.message, [this.parent]);
-  final String message;
-  final Exception parent;
-  @override
-  String toString() => message;
-}
diff --git a/packages/flutter_tools/lib/src/dependency_checker.dart b/packages/flutter_tools/lib/src/dependency_checker.dart
deleted file mode 100644
index 8f0aea7..0000000
--- a/packages/flutter_tools/lib/src/dependency_checker.dart
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'asset.dart';
-import 'base/file_system.dart';
-import 'dart/dependencies.dart';
-import 'globals.dart';
-
-class DependencyChecker {
-  DependencyChecker(this.builder, this.assets);
-
-  final DartDependencySetBuilder builder;
-  final Set<String> _dependencies = <String>{};
-  final AssetBundle assets;
-
-  /// Returns [true] if any components have been modified after [threshold] or
-  /// if it cannot be determined.
-  bool check(DateTime threshold) {
-    _dependencies.clear();
-    // Build the set of Dart dependencies.
-    try {
-      _dependencies.addAll(builder.build());
-    } catch (e, st) {
-      printTrace('DependencyChecker: error determining .dart dependencies:\n$e\n$st');
-      return true;
-    }
-    // TODO(johnmccutchan): Extract dependencies from the AssetBundle too.
-
-    // Check all dependency modification times.
-    for (String path in _dependencies) {
-      final File file = fs.file(path);
-      final FileStat stat = file.statSync();
-      if (stat.type == FileSystemEntityType.notFound) {
-        printTrace('DependencyChecker: Error stating $path.');
-        return true;
-      }
-      if (stat.modified.isAfter(threshold)) {
-        printTrace('DependencyChecker: $path is newer than $threshold');
-        return true;
-      }
-    }
-    printTrace('DependencyChecker: nothing is modified after $threshold.');
-    return false;
-  }
-}
diff --git a/packages/flutter_tools/lib/src/devfs.dart b/packages/flutter_tools/lib/src/devfs.dart
index 7940e8b..407a51b 100644
--- a/packages/flutter_tools/lib/src/devfs.dart
+++ b/packages/flutter_tools/lib/src/devfs.dart
@@ -30,8 +30,6 @@
 
 /// Common superclass for content copied to the device.
 abstract class DevFSContent {
-  bool _exists = true;
-
   /// Return true if this is the first time this method is called
   /// or if the entry has been modified since this method was last called.
   bool get isModified;
@@ -59,13 +57,6 @@
 class DevFSFileContent extends DevFSContent {
   DevFSFileContent(this.file);
 
-  static DevFSFileContent clone(DevFSFileContent fsFileContent) {
-    final DevFSFileContent newFsFileContent = DevFSFileContent(fsFileContent.file);
-    newFsFileContent._linkTarget = fsFileContent._linkTarget;
-    newFsFileContent._fileStat = fsFileContent._fileStat;
-    return newFsFileContent;
-  }
-
   final FileSystemEntity file;
   FileSystemEntity _linkTarget;
   FileStat _fileStat;
@@ -218,7 +209,6 @@
   Future<Uri> create(String fsName);
   Future<dynamic> destroy(String fsName);
   Future<dynamic> writeFile(String fsName, Uri deviceUri, DevFSContent content);
-  Future<dynamic> deleteFile(String fsName, Uri deviceUri);
 }
 
 /// An implementation of [DevFSOperations] that speaks to the
@@ -261,11 +251,6 @@
       printTrace('DevFS: Failed to write $deviceUri: $error');
     }
   }
-
-  @override
-  Future<dynamic> deleteFile(String fsName, Uri deviceUri) async {
-    // TODO(johnmccutchan): Add file deletion to the devFS protocol.
-  }
 }
 
 class DevFSException implements Exception {
@@ -288,16 +273,14 @@
   int _inFlight = 0;
   Map<Uri, DevFSContent> _outstanding;
   Completer<void> _completer;
-  HttpClient _client;
+  final HttpClient _client = HttpClient();
 
   Future<void> write(Map<Uri, DevFSContent> entries) async {
-    _client = HttpClient();
     _client.maxConnectionsPerHost = kMaxInFlight;
     _completer = Completer<void>();
     _outstanding = Map<Uri, DevFSContent>.from(entries);
     _scheduleWrites();
     await _completer.future;
-    _client.close();
   }
 
   void _scheduleWrites() {
@@ -405,9 +388,6 @@
   final Map<Uri, DevFSContent> _entries = <Uri, DevFSContent>{};
   final Set<String> assetPathsToEvict = <String>{};
 
-  final List<Future<Map<String, dynamic>>> _pendingOperations =
-      <Future<Map<String, dynamic>>>[];
-
   Uri _baseUri;
   Uri get baseUri => _baseUri;
 
@@ -453,66 +433,33 @@
     DateTime firstBuildTime,
     bool bundleFirstUpload = false,
     bool bundleDirty = false,
-    Set<String> fileFilter,
     @required ResidentCompiler generator,
     String dillOutputPath,
     @required bool trackWidgetCreation,
     bool fullRestart = false,
     String projectRootPath,
     @required String pathToReload,
+    @required List<String> invalidatedFiles,
   }) async {
     assert(trackWidgetCreation != null);
     assert(generator != null);
-    // Mark all entries as possibly deleted.
-    for (DevFSContent content in _entries.values) {
-      content._exists = false;
-    }
 
-    // Scan workspace, packages, and assets
-    printTrace('DevFS: Starting sync from $rootDirectory');
-    logger.printTrace('Scanning project files');
-    await _scanDirectory(rootDirectory,
-                         recursive: true,
-                         fileFilter: fileFilter);
-    if (fs.isFileSync(_packagesFilePath)) {
-      printTrace('Scanning package files');
-      await _scanPackages(fileFilter);
-    }
     if (bundle != null) {
       printTrace('Scanning asset files');
+      // We write the assets into the AssetBundle working dir so that they
+      // are in the same location in DevFS and the iOS simulator.
+      final String assetDirectory = getAssetBuildDirectory();
       bundle.entries.forEach((String archivePath, DevFSContent content) {
-        _scanBundleEntry(archivePath, content);
+        final Uri deviceUri = fs.path.toUri(fs.path.join(assetDirectory, archivePath));
+        _entries[deviceUri] = content;
       });
     }
 
-    // Handle deletions.
-    printTrace('Scanning for deleted files');
-    final String assetBuildDirPrefix = _asUriPath(getAssetBuildDirectory());
-    final List<Uri> toRemove = <Uri>[];
-    _entries.forEach((Uri deviceUri, DevFSContent content) {
-      if (!content._exists) {
-        final Future<Map<String, dynamic>> operation =
-            _operations.deleteFile(fsName, deviceUri)
-            .then<Map<String, dynamic>>((dynamic v) => v?.cast<String,dynamic>());
-        if (operation != null)
-          _pendingOperations.add(operation);
-        toRemove.add(deviceUri);
-        if (deviceUri.path.startsWith(assetBuildDirPrefix)) {
-          final String archivePath = deviceUri.path.substring(assetBuildDirPrefix.length);
-          assetPathsToEvict.add(archivePath);
-        }
-      }
-    });
-    if (toRemove.isNotEmpty) {
-      printTrace('Removing deleted files');
-      toRemove.forEach(_entries.remove);
-      await Future.wait<Map<String, dynamic>>(_pendingOperations);
-      _pendingOperations.clear();
-    }
-
     // Update modified files
-    int syncedBytes = 0;
+    final String assetBuildDirPrefix = _asUriPath(getAssetBuildDirectory());
     final Map<Uri, DevFSContent> dirtyEntries = <Uri, DevFSContent>{};
+
+    int syncedBytes = 0;
     _entries.forEach((Uri deviceUri, DevFSContent content) {
       String archivePath;
       if (deviceUri.path.startsWith(assetBuildDirPrefix))
@@ -527,30 +474,10 @@
           assetPathsToEvict.add(archivePath);
       }
     });
-    // We run generator even if [dirtyEntries] was empty because we want to
-    // keep logic of accepting/rejecting generator's output simple: we must
-    // accept/reject generator's output after every [update] call.  Incremental
-    // run with no changes is supposed to be fast (considering that it is
-    // initiated by user key press).
-    final List<String> invalidatedFiles = <String>[];
-    final Set<Uri> filesUris = <Uri>{};
-    for (Uri uri in dirtyEntries.keys.toList()) {
-      if (!uri.path.startsWith(assetBuildDirPrefix)) {
-        final DevFSContent content = dirtyEntries[uri];
-        if (content is DevFSFileContent) {
-          filesUris.add(uri);
-          invalidatedFiles.add(content.file.uri.toString());
-          syncedBytes -= content.size;
-        }
-      }
-    }
-    // No need to send source files because all compilation is done on the
-    // host and result of compilation is single kernel file.
-    filesUris.forEach(dirtyEntries.remove);
-    printTrace('Compiling dart to kernel with ${invalidatedFiles.length} updated files');
     if (fullRestart) {
       generator.reset();
     }
+    printTrace('Compiling dart to kernel with ${invalidatedFiles.length} updated files');
     final CompilerOutput compilerOutput = await generator.recompile(
       mainPath,
       invalidatedFiles,
@@ -566,228 +493,28 @@
           ? fs.path.relative(pathToReload, from: projectRootPath)
           : pathToReload,
         );
-        if (!dirtyEntries.containsKey(entryUri)) {
-          final DevFSFileContent content = DevFSFileContent(fs.file(compiledBinary));
-          dirtyEntries[entryUri] = content;
-          syncedBytes += content.size;
-        }
+        final DevFSFileContent content = DevFSFileContent(fs.file(compiledBinary));
+        syncedBytes += content.size;
+        dirtyEntries[entryUri] = content;
       }
     }
+    printTrace('Updating files');
     if (dirtyEntries.isNotEmpty) {
-      printTrace('Updating files');
-      if (_httpWriter != null) {
-        try {
-          await _httpWriter.write(dirtyEntries);
-        } on SocketException catch (socketException, stackTrace) {
-          printTrace('DevFS sync failed. Lost connection to device: $socketException');
-          throw DevFSException('Lost connection to device.', socketException, stackTrace);
-        } catch (exception, stackTrace) {
-          printError('Could not update files on device: $exception');
-          throw DevFSException('Sync failed', exception, stackTrace);
-        }
-      } else {
-        // Make service protocol requests for each.
-        dirtyEntries.forEach((Uri deviceUri, DevFSContent content) {
-          final Future<Map<String, dynamic>> operation =
-              _operations.writeFile(fsName, deviceUri, content)
-                  .then<Map<String, dynamic>>((dynamic v) => v?.cast<String, dynamic>());
-          if (operation != null)
-            _pendingOperations.add(operation);
-        });
-        await Future.wait<Map<String, dynamic>>(_pendingOperations, eagerError: true);
-        _pendingOperations.clear();
+      try {
+        await _httpWriter.write(dirtyEntries);
+      } on SocketException catch (socketException, stackTrace) {
+        printTrace('DevFS sync failed. Lost connection to device: $socketException');
+        throw DevFSException('Lost connection to device.', socketException, stackTrace);
+      } catch (exception, stackTrace) {
+        printError('Could not update files on device: $exception');
+        throw DevFSException('Sync failed', exception, stackTrace);
       }
     }
-
     printTrace('DevFS: Sync finished');
     return UpdateFSReport(success: true, syncedBytes: syncedBytes,
-        invalidatedSourcesCount: invalidatedFiles.length);
-  }
-
-  void _scanFile(Uri deviceUri, FileSystemEntity file) {
-    final DevFSContent content = _entries.putIfAbsent(deviceUri, () => DevFSFileContent(file));
-    content._exists = true;
-  }
-
-  void _scanBundleEntry(String archivePath, DevFSContent content) {
-    // We write the assets into the AssetBundle working dir so that they
-    // are in the same location in DevFS and the iOS simulator.
-    final Uri deviceUri = fs.path.toUri(fs.path.join(getAssetBuildDirectory(), archivePath));
-
-    _entries[deviceUri] = content;
-    content._exists = true;
-  }
-
-  bool _shouldIgnore(Uri deviceUri) {
-    final List<String> ignoredUriPrefixes = <String>['android/',
-                                               _asUriPath(getBuildDirectory()),
-                                               'ios/',
-                                               '.pub/'];
-    for (String ignoredUriPrefix in ignoredUriPrefixes) {
-      if (deviceUri.path.startsWith(ignoredUriPrefix))
-        return true;
-    }
-    return false;
-  }
-
-  bool _shouldSkip(
-    FileSystemEntity file,
-    String relativePath,
-    Uri directoryUriOnDevice, {
-    bool ignoreDotFiles = true,
-  }) {
-    if (file is Directory) {
-      // Skip non-files.
-      return true;
-    }
-    assert((file is Link) || (file is File));
-    final String basename = fs.path.basename(file.path);
-    if (ignoreDotFiles && basename.startsWith('.')) {
-      // Skip dot files, but not the '.packages' file (even though in dart1
-      // mode devfs['.packages'] will be overwritten with synthesized string content).
-      return basename != '.packages';
-    }
-    return false;
-  }
-
-  Uri _directoryUriOnDevice(Uri directoryUriOnDevice, Directory directory) {
-    if (directoryUriOnDevice == null) {
-      final String relativeRootPath = fs.path.relative(directory.path, from: rootDirectory.path);
-      if (relativeRootPath == '.') {
-        directoryUriOnDevice = Uri();
-      } else {
-        directoryUriOnDevice = fs.path.toUri(relativeRootPath);
-      }
-    }
-    return directoryUriOnDevice;
-  }
-
-  /// Scan all files from the [fileFilter] that are contained in [directory] and
-  /// pass various filters (e.g. ignoreDotFiles).
-  Future<bool> _scanFilteredDirectory(
-    Set<String> fileFilter,
-    Directory directory, {
-    Uri directoryUriOnDevice,
-    bool ignoreDotFiles = true,
-  }) async {
-    directoryUriOnDevice =
-        _directoryUriOnDevice(directoryUriOnDevice, directory);
-    try {
-      final String absoluteDirectoryPath = canonicalizePath(directory.path);
-      // For each file in the file filter.
-      for (String filePath in fileFilter) {
-        if (!filePath.startsWith(absoluteDirectoryPath)) {
-          // File is not in this directory. Skip.
-          continue;
-        }
-        final String relativePath =
-          fs.path.relative(filePath, from: directory.path);
-        final FileSystemEntity file = fs.file(filePath);
-        if (_shouldSkip(file, relativePath, directoryUriOnDevice, ignoreDotFiles: ignoreDotFiles)) {
-          continue;
-        }
-        final Uri deviceUri = directoryUriOnDevice.resolveUri(fs.path.toUri(relativePath));
-        if (!_shouldIgnore(deviceUri))
-          _scanFile(deviceUri, file);
-      }
-    } on FileSystemException catch (e) {
-      _printScanDirectoryError(directory.path, e);
-      return false;
-    }
-    return true;
-  }
-
-  /// Scan all files in [directory] that pass various filters (e.g. ignoreDotFiles).
-  Future<bool> _scanDirectory(
-    Directory directory, {
-    Uri directoryUriOnDevice,
-    bool recursive = false,
-    bool ignoreDotFiles = true,
-    Set<String> fileFilter,
-  }) async {
-    directoryUriOnDevice = _directoryUriOnDevice(directoryUriOnDevice, directory);
-    if ((fileFilter != null) && fileFilter.isNotEmpty) {
-      // When the fileFilter isn't empty, we can skip crawling the directory
-      // tree and instead use the fileFilter as the source of potential files.
-      return _scanFilteredDirectory(fileFilter,
-                                    directory,
-                                    directoryUriOnDevice: directoryUriOnDevice,
-                                    ignoreDotFiles: ignoreDotFiles);
-    }
-    try {
-      final Stream<FileSystemEntity> files =
-          directory.list(recursive: recursive, followLinks: false);
-      await for (FileSystemEntity file in files) {
-        if (!devFSConfig.noDirectorySymlinks && (file is Link)) {
-          // Check if this is a symlink to a directory and skip it.
-          try {
-            final FileSystemEntityType linkType =
-                fs.statSync(file.resolveSymbolicLinksSync()).type;
-            if (linkType == FileSystemEntityType.directory)
-              continue;
-          } on FileSystemException catch (e) {
-            _printScanDirectoryError(file.path, e);
-            continue;
-          }
-        }
-        final String relativePath =
-          fs.path.relative(file.path, from: directory.path);
-        if (_shouldSkip(file, relativePath, directoryUriOnDevice, ignoreDotFiles: ignoreDotFiles)) {
-          continue;
-        }
-        final Uri deviceUri = directoryUriOnDevice.resolveUri(fs.path.toUri(relativePath));
-        if (!_shouldIgnore(deviceUri))
-          _scanFile(deviceUri, file);
-      }
-    } on FileSystemException catch (e) {
-      _printScanDirectoryError(directory.path, e);
-      return false;
-    }
-    return true;
-  }
-
-  void _printScanDirectoryError(String path, Exception e) {
-    printError(
-        'Error while scanning $path.\n'
-        'Hot Reload might not work until the following error is resolved:\n'
-        '$e\n'
-    );
-  }
-
-  Future<void> _scanPackages(Set<String> fileFilter) async {
-    StringBuffer sb;
-    final PackageMap packageMap = PackageMap(_packagesFilePath);
-
-    for (String packageName in packageMap.map.keys) {
-      final Uri packageUri = packageMap.map[packageName];
-      final String packagePath = fs.path.fromUri(packageUri);
-      final Directory packageDirectory = fs.directory(packageUri);
-      Uri directoryUriOnDevice = fs.path.toUri(fs.path.join('packages', packageName) + fs.path.separator);
-      bool packageExists = packageDirectory.existsSync();
-
-      if (!packageExists) {
-        // If the package directory doesn't exist at all, we ignore it.
-        continue;
-      }
-
-      if (fs.path.isWithin(rootDirectory.path, packagePath)) {
-        // We already scanned everything under the root directory.
-        directoryUriOnDevice = fs.path.toUri(
-            fs.path.relative(packagePath, from: rootDirectory.path) + fs.path.separator
-        );
-      } else {
-        packageExists =
-            await _scanDirectory(packageDirectory,
-                                 directoryUriOnDevice: directoryUriOnDevice,
-                                 recursive: true,
-                                 fileFilter: fileFilter);
-      }
-      if (packageExists) {
-        sb ??= StringBuffer();
-        sb.writeln('$packageName:$directoryUriOnDevice');
-      }
-    }
+         invalidatedSourcesCount: invalidatedFiles.length);
   }
 }
+
 /// Converts a platform-specific file path to a platform-independent Uri path.
 String _asUriPath(String filePath) => fs.path.toUri(filePath).path + '/';
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index 703d6d7..88c11d4 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -18,9 +18,7 @@
 import 'build_info.dart';
 import 'codegen.dart';
 import 'compile.dart';
-import 'dart/dependencies.dart';
 import 'dart/package_map.dart';
-import 'dependency_checker.dart';
 import 'devfs.dart';
 import 'device.dart';
 import 'globals.dart';
@@ -444,10 +442,10 @@
     DateTime firstBuildTime,
     bool bundleFirstUpload = false,
     bool bundleDirty = false,
-    Set<String> fileFilter,
     bool fullRestart = false,
     String projectRootPath,
     String pathToReload,
+    @required List<String> invalidatedFiles,
   }) async {
     final Status devFSStatus = logger.startProgress(
       'Syncing files to device ${device.name}...',
@@ -462,13 +460,13 @@
         firstBuildTime: firstBuildTime,
         bundleFirstUpload: bundleFirstUpload,
         bundleDirty: bundleDirty,
-        fileFilter: fileFilter,
         generator: generator,
         fullRestart: fullRestart,
         dillOutputPath: dillOutputPath,
         trackWidgetCreation: trackWidgetCreation,
         projectRootPath: projectRootPath,
         pathToReload: pathToReload,
+        invalidatedFiles: invalidatedFiles,
       );
     } on DevFSException {
       devFSStatus.cancel();
@@ -945,21 +943,19 @@
   }
 
   bool hasDirtyDependencies(FlutterDevice device) {
-    /// When using the build system, dependency analysis is handled by build
-    /// runner instead.
-    if (experimentalBuildEnabled) {
-      return false;
-    }
-    final DartDependencySetBuilder dartDependencySetBuilder =
-        DartDependencySetBuilder(mainPath, packagesFilePath);
-    final DependencyChecker dependencyChecker =
-        DependencyChecker(dartDependencySetBuilder, assetBundle);
     if (device.package.packagesFile == null || !device.package.packagesFile.existsSync()) {
       return true;
     }
-    final DateTime lastBuildTime = device.package.packagesFile.statSync().modified;
-
-    return dependencyChecker.check(lastBuildTime);
+    // Leave pubspec null to check all dependencies.
+    final ProjectFileInvalidator projectFileInvalidator = ProjectFileInvalidator(device.package.packagesFile.path, null);
+    projectFileInvalidator.findInvalidated();
+    final int lastBuildTime = device.package.packagesFile.statSync().modified.millisecondsSinceEpoch;
+    for (int updateTime in projectFileInvalidator.updateTime.values) {
+      if (updateTime > lastBuildTime) {
+        return true;
+      }
+    }
+    return false;
   }
 
   Future<void> preStop() async { }
diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart
index 53a4f36..e6da33b 100644
--- a/packages/flutter_tools/lib/src/run_hot.dart
+++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -7,29 +7,28 @@
 import 'package:json_rpc_2/error_code.dart' as rpc_error_code;
 import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
 import 'package:meta/meta.dart';
+import 'package:yaml/yaml.dart';
 
 import 'base/common.dart';
 import 'base/context.dart';
 import 'base/file_system.dart';
 import 'base/logger.dart';
+import 'base/platform.dart';
 import 'base/terminal.dart';
 import 'base/utils.dart';
 import 'build_info.dart';
-import 'codegen.dart';
 import 'compile.dart';
 import 'convert.dart';
-import 'dart/dependencies.dart';
-import 'dart/pub.dart';
+import 'dart/package_map.dart';
 import 'devfs.dart';
 import 'device.dart';
 import 'globals.dart';
+import 'project.dart';
 import 'resident_runner.dart';
 import 'usage.dart';
 import 'vmservice.dart';
 
 class HotRunnerConfig {
-  /// Should the hot runner compute the minimal Dart dependencies?
-  bool computeDartDependencies = true;
   /// Should the hot runner assume that the minimal Dart dependencies do not change?
   bool stableDartDependencies = false;
   /// A hook for implementations to perform any necessary initialization prior
@@ -71,6 +70,7 @@
     bool saveCompilationTrace = false,
     bool stayResident = true,
     bool ipv6 = false,
+    FlutterProject flutterProject,
   }) : super(devices,
              target: target,
              debuggingOptions: debuggingOptions,
@@ -79,14 +79,19 @@
              packagesFilePath: packagesFilePath,
              saveCompilationTrace: saveCompilationTrace,
              stayResident: stayResident,
-             ipv6: ipv6);
+             ipv6: ipv6)  {
+    fileInvalidator = ProjectFileInvalidator(
+      packagesFilePath ?? fs.path.absolute(PackageMap.globalPackagesPath),
+      flutterProject,
+    );
+  }
 
   final bool benchmarkMode;
   final File applicationBinary;
   final bool hostIsIde;
   bool _didAttach = false;
-  Set<String> _dartDependencies;
   final String dillOutputPath;
+  ProjectFileInvalidator fileInvalidator;
 
   final Map<String, List<int>> benchmarkData = <String, List<int>>{};
   // The initial launch is from a snapshot.
@@ -98,54 +103,8 @@
     benchmarkData[name].add(value);
   }
 
-  Future<bool> _refreshDartDependencies() async {
-    if (!hotRunnerConfig.computeDartDependencies) {
-      // Disabled.
-      return true;
-    }
-    if (_dartDependencies != null) {
-      // Already computed.
-      return true;
-    }
-
-    try {
-      // Will return immediately if pubspec.yaml is up-to-date.
-      await pubGet(
-        context: PubContext.pubGet,
-        directory: projectRootPath,
-      );
-    } on ToolExit catch (error) {
-      printError(
-        'Unable to reload your application because "flutter packages get" failed to update '
-        'package dependencies.\n'
-        '$error'
-      );
-      return false;
-    }
-
-    /// When using the build system, dependency analysis is handled by build
-    /// runner instead.
-    if (experimentalBuildEnabled) {
-      return true;
-    }
-    final DartDependencySetBuilder dartDependencySetBuilder = DartDependencySetBuilder(mainPath, packagesFilePath);
-    try {
-      _dartDependencies = Set<String>.from(dartDependencySetBuilder.build());
-    } on DartDependencyException catch (error) {
-      printError(
-        'Your application could not be compiled, because its dependencies could not be established.\n'
-        '$error'
-      );
-      return false;
-    }
-    return true;
-  }
-
-  Future<void> _reloadSourcesService(
-    String isolateId, {
-    bool force = false,
-    bool pause = false,
-  }) async {
+  Future<void> _reloadSourcesService(String isolateId,
+      { bool force = false, bool pause = false }) async {
     // TODO(cbernaschina): check that isolateId is the id of the UI isolate.
     final OperationResult result = await restart(pauseAfterRestart: pause);
     if (!result.isOk) {
@@ -257,7 +216,6 @@
       // Measure time to perform a hot restart.
       printStatus('Benchmarking hot restart');
       await restart(fullRestart: true);
-      // TODO(johnmccutchan): Modify script entry point.
       printStatus('Benchmarking hot reload');
       // Measure time to perform a hot reload.
       await restart(fullRestart: false);
@@ -296,12 +254,6 @@
       return 1;
     }
 
-    // Determine the Dart dependencies eagerly.
-    if (!await _refreshDartDependencies()) {
-      // Some kind of source level error or missing file in the Dart code.
-      return 1;
-    }
-
     firstBuildTime = DateTime.now();
 
     for (FlutterDevice device in flutterDevices) {
@@ -336,8 +288,6 @@
         result = await restart(fullRestart: false);
       }
       if (!result.isOk) {
-        // TODO(johnmccutchan): Attempt to determine the number of errors that
-        // occurred and tighten this message.
         printStatus('Try again after fixing the above error(s).', emphasis: true);
       }
     } else if (lower == 'l') {
@@ -364,10 +314,6 @@
   }
 
   Future<UpdateFSReport> _updateDevFS({ bool fullRestart = false }) async {
-    if (!await _refreshDartDependencies()) {
-      // Did not update DevFS because of a Dart source error.
-      return UpdateFSReport(success: false);
-    }
     final bool isFirstUpload = assetBundle.wasBuiltOnce() == false;
     final bool rebuildBundle = assetBundle.needsBuild();
     if (rebuildBundle) {
@@ -376,7 +322,7 @@
       if (result != 0)
         return UpdateFSReport(success: false);
     }
-
+    final List<String> invalidatedFiles = fileInvalidator.findInvalidated();
     final UpdateFSReport results = UpdateFSReport(success: true);
     for (FlutterDevice device in flutterDevices) {
       results.incorporateResults(await device.updateDevFS(
@@ -386,39 +332,15 @@
         firstBuildTime: firstBuildTime,
         bundleFirstUpload: isFirstUpload,
         bundleDirty: isFirstUpload == false && rebuildBundle,
-        fileFilter: _dartDependencies,
         fullRestart: fullRestart,
         projectRootPath: projectRootPath,
         pathToReload: getReloadPath(fullRestart: fullRestart),
+        invalidatedFiles: invalidatedFiles,
       ));
     }
-    if (!results.success) {
-      return results;
-    }
-
-    if (!hotRunnerConfig.stableDartDependencies) {
-      // Clear the set after the sync so they are recomputed next time.
-      _dartDependencies = null;
-    }
     return results;
   }
 
-  Future<void> _evictDirtyAssets() {
-    final List<Future<Map<String, dynamic>>> futures = <Future<Map<String, dynamic>>>[];
-    for (FlutterDevice device in flutterDevices) {
-      if (device.devFS.assetPathsToEvict.isEmpty)
-        continue;
-      if (device.views.first.uiIsolate == null) {
-        printError('Application isolate not found for $device');
-        continue;
-      }
-      for (String assetPath in device.devFS.assetPathsToEvict)
-        futures.add(device.views.first.uiIsolate.flutterEvictAsset(assetPath));
-      device.devFS.assetPathsToEvict.clear();
-    }
-    return Future.wait<Map<String, dynamic>>(futures);
-  }
-
   void _resetDirtyAssets() {
     for (FlutterDevice device in flutterDevices)
       device.devFS.assetPathsToEvict.clear();
@@ -811,9 +733,9 @@
         return OperationResult(OperationResult.ok.code, reloadMessage);
       }
     }
-    assert(reassembleViews.isNotEmpty);
     printTrace('Evicting dirty assets');
     await _evictDirtyAssets();
+    assert(reassembleViews.isNotEmpty);
     printTrace('Reassembling application');
     bool failedReassemble = false;
     final List<Future<void>> futures = <Future<void>>[];
@@ -886,7 +808,6 @@
     // Only report timings if we reloaded a single view without any errors.
     if ((reassembleViews.length == 1) && !failedReassemble && shouldReportReloadTime)
       flutterUsage.sendTiming('hot', 'reload', reloadDuration);
-
     return OperationResult(
       failedReassemble ? 1 : OperationResult.ok.code,
       reloadMessage,
@@ -965,6 +886,23 @@
     }
   }
 
+  Future<void> _evictDirtyAssets() {
+    final List<Future<Map<String, dynamic>>> futures = <Future<Map<String, dynamic>>>[];
+    for (FlutterDevice device in flutterDevices) {
+      if (device.devFS.assetPathsToEvict.isEmpty)
+        continue;
+      if (device.views.first.uiIsolate == null) {
+        printError('Application isolate not found for $device');
+        continue;
+      }
+      for (String assetPath in device.devFS.assetPathsToEvict) {
+        futures.add(device.views.first.uiIsolate.flutterEvictAsset(assetPath));
+      }
+      device.devFS.assetPathsToEvict.clear();
+    }
+    return Future.wait<Map<String, dynamic>>(futures);
+  }
+
   @override
   Future<void> cleanupAfterSignal() async {
     await stopEchoingDeviceLog();
@@ -988,3 +926,106 @@
     await stopEchoingDeviceLog();
   }
 }
+
+class ProjectFileInvalidator {
+  ProjectFileInvalidator(this._packagesPath, this._flutterProject) {
+    final File packagesFile = fs.file(_packagesPath);
+    if (packagesFile.existsSync()) {
+      _packagesUpdateTime = packagesFile.statSync().modified.millisecondsSinceEpoch;
+      _packageMap = PackageMap(_packagesPath).map;
+    } else {
+      _packagesUpdateTime = -1;
+      _packageMap = const <String, Uri>{};
+    }
+    _computePackageMap(_packageMap, _flutterProject);
+  }
+
+  // Used to avoid watching pubspec directories. This will not change even with pub upgrade,
+  // because that actually switches the directory and requires a corresponding
+  // update to .packages
+  static const String _pubCachePathLinuxAndWindows = '.pub-cache';
+  static const String _pubCachePathWindows = 'Pub/Cache';
+
+  Map<String, Uri> _packageMap;
+  final String _packagesPath;
+  final FlutterProject _flutterProject;
+  final Map<String, int> _updateTime = <String, int>{};
+  int _packagesUpdateTime;
+
+  Map<String, int> get updateTime => _updateTime;
+
+  @visibleForTesting
+  Map<String, Uri> get packageMap => _packageMap;
+
+  static void _computePackageMap(Map<String, Uri> packageMap, FlutterProject flutterProject) {
+    if (flutterProject != null && flutterProject.pubspecFile.existsSync()) {
+      try {
+        final YamlMap pubspec = loadYamlDocument(flutterProject.pubspecFile.readAsStringSync()).contents;
+        final YamlMap dependencies = pubspec['dependencies'];
+        final Set<String> relevantDependencies = Set<String>.from(dependencies.keys);
+        // Remove any packages which were tagged as dev dependenices,
+        // But don't remove the app itself!
+        for (String packageName in packageMap.keys.toList()) {
+          if (!relevantDependencies.contains(packageName) && packageName != flutterProject.manifest.appName) {
+            packageMap.remove(packageName);
+            continue;
+          }
+        }
+      } catch (err) {
+        // If we detect a pubspec formatting problem, fallback to the packages file.
+      }
+    }
+    // Remove any packages which are derived from the pub cache.
+    for (String packageName in packageMap.keys.toList()) {
+      final String path = packageMap[packageName].path;
+      if ((platform.isWindows && path.contains(_pubCachePathWindows))
+          || path.contains(_pubCachePathLinuxAndWindows)) {
+        packageMap.remove(packageName);
+      }
+    }
+  }
+
+  List<String> findInvalidated() {
+    final File packagesFile = fs.file(_packagesPath);
+    if (packagesFile.existsSync()) {
+      final int newPackagesUpdateTime = packagesFile.statSync().modified.millisecondsSinceEpoch;
+      // Hot reloading with an updated package will often times kill a non-trivial
+      // appliction. This _might_ work, given certain application size and package
+      // constraints, so instead of exiting we print a warning so that the user has
+      // some hint on what went wrong.
+      if (newPackagesUpdateTime > _packagesUpdateTime) {
+        printError('Warning: updated dependencies detected. The Flutter application will require a restart to safely use new packages.');
+      }
+      _packagesUpdateTime = newPackagesUpdateTime;
+    }
+    final List<String> invalidatedFiles = <String>[];
+    for (String packageName in _packageMap.keys) {
+      final Uri packageUri =_packageMap[packageName];
+      _scanDirectory(packageUri, invalidatedFiles);
+    }
+    return invalidatedFiles;
+  }
+
+  void _scanDirectory(Uri path, List<String> invalidatedFiles) {
+    final Directory directory = fs.directory(path);
+    if (!directory.existsSync()) {
+      return;
+    }
+    for (FileSystemEntity entity in directory.listSync(recursive: true)) {
+      if (entity.path.endsWith('.dart')) {
+        final int oldUpdatedAt = _updateTime[entity.path];
+        final int updatedAt = fs.statSync(entity.path).modified.millisecondsSinceEpoch;
+        if (oldUpdatedAt == null || updatedAt > oldUpdatedAt) {
+          // On windows convert to file uri in expected format.
+          if (platform.isWindows) {
+            final Uri uri = Uri.file(entity.path, windows: platform.isWindows);
+            invalidatedFiles.add(uri.toString());
+          } else {
+            invalidatedFiles.add(entity.path);
+          }
+        }
+        _updateTime[entity.path] = updatedAt;
+      }
+    }
+  }
+}
diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml
index 9eae2de..a1d023d 100644
--- a/packages/flutter_tools/pubspec.yaml
+++ b/packages/flutter_tools/pubspec.yaml
@@ -9,7 +9,6 @@
 
 dependencies:
   # To update these, use "flutter update-packages --force-upgrade".
-  analyzer: 0.35.3
   archive: 2.0.8
   args: 1.5.1
   bsdiff: 0.1.0
@@ -53,6 +52,7 @@
   build_modules: 1.0.9
   build_daemon: 0.4.2
 
+  analyzer: 0.35.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   bazel_worker: 0.1.20 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -100,8 +100,8 @@
   collection: 1.14.11
   mockito: 4.0.0
   file_testing: 2.1.0
-  test: 1.5.3
   vm_service_lib: 3.14.2
+  test: 1.5.3
   build_runner: 1.2.8
   build_vm_compilers: 0.1.1+5
   build_test: 0.10.6
diff --git a/packages/flutter_tools/test/commands/attach_test.dart b/packages/flutter_tools/test/commands/attach_test.dart
index 693d71e..87dcb92 100644
--- a/packages/flutter_tools/test/commands/attach_test.dart
+++ b/packages/flutter_tools/test/commands/attach_test.dart
@@ -120,6 +120,7 @@
             debuggingOptions: anyNamed('debuggingOptions'),
             packagesFilePath: anyNamed('packagesFilePath'),
             usesTerminalUI: anyNamed('usesTerminalUI'),
+            flutterProject: anyNamed('flutterProject'),
             ipv6: false,
           ),
         ).thenReturn(mockHotRunner);
@@ -151,6 +152,7 @@
             debuggingOptions: anyNamed('debuggingOptions'),
             packagesFilePath: anyNamed('packagesFilePath'),
             usesTerminalUI: anyNamed('usesTerminalUI'),
+            flutterProject: anyNamed('flutterProject'),
             ipv6: false,
           ),
         )..called(1);
@@ -225,6 +227,7 @@
         debuggingOptions: anyNamed('debuggingOptions'),
         packagesFilePath: anyNamed('packagesFilePath'),
         usesTerminalUI: anyNamed('usesTerminalUI'),
+        flutterProject: anyNamed('flutterProject'),
         ipv6: false,
       )).thenReturn(mockHotRunner);
 
@@ -254,6 +257,7 @@
         debuggingOptions: anyNamed('debuggingOptions'),
         packagesFilePath: anyNamed('packagesFilePath'),
         usesTerminalUI: anyNamed('usesTerminalUI'),
+        flutterProject: anyNamed('flutterProject'),
         ipv6: false,
       )).called(1);
     }, overrides: <Type, Generator>{
diff --git a/packages/flutter_tools/test/dart_dependencies_test.dart b/packages/flutter_tools/test/dart_dependencies_test.dart
deleted file mode 100644
index dbfdc26..0000000
--- a/packages/flutter_tools/test/dart_dependencies_test.dart
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:flutter_tools/src/dart/dependencies.dart';
-import 'package:flutter_tools/src/base/file_system.dart';
-
-import 'src/common.dart';
-import 'src/context.dart';
-
-void main() {
-  group('DartDependencySetBuilder', () {
-    final String dataPath = fs.path.join(
-      getFlutterRoot(),
-      'packages',
-      'flutter_tools',
-      'test',
-      'data',
-      'dart_dependencies_test',
-    );
-
-    testUsingContext('good', () {
-      final String testPath = fs.path.join(dataPath, 'good');
-      final String mainPath = fs.path.join(testPath, 'main.dart');
-      final String packagesPath = fs.path.join(testPath, '.packages');
-      final DartDependencySetBuilder builder =
-          DartDependencySetBuilder(mainPath, packagesPath);
-      final Set<String> dependencies = builder.build();
-      expect(dependencies.contains(canonicalizePath(mainPath)), isTrue);
-      expect(dependencies.contains(canonicalizePath(fs.path.join(testPath, 'foo.dart'))), isTrue);
-    });
-
-    testUsingContext('syntax_error', () {
-      final String testPath = fs.path.join(dataPath, 'syntax_error');
-      final String mainPath = fs.path.join(testPath, 'main.dart');
-      final String packagesPath = fs.path.join(testPath, '.packages');
-      final DartDependencySetBuilder builder =
-          DartDependencySetBuilder(mainPath, packagesPath);
-      try {
-        builder.build();
-        fail('expect an exception to be thrown.');
-      } on DartDependencyException catch (error) {
-        expect(error.toString(), contains('foo.dart: Expected a string literal'));
-      }
-    });
-
-    testUsingContext('bad_path', () {
-      final String testPath = fs.path.join(dataPath, 'bad_path');
-      final String mainPath = fs.path.join(testPath, 'main.dart');
-      final String packagesPath = fs.path.join(testPath, '.packages');
-      final DartDependencySetBuilder builder =
-          DartDependencySetBuilder(mainPath, packagesPath);
-      try {
-        builder.build();
-        fail('expect an exception to be thrown.');
-      } on DartDependencyException catch (error) {
-        expect(error.toString(), contains('amaze${fs.path.separator}and${fs.path.separator}astonish.dart'));
-      }
-    });
-
-    testUsingContext('bad_package', () {
-      final String testPath = fs.path.join(dataPath, 'bad_package');
-      final String mainPath = fs.path.join(testPath, 'main.dart');
-      final String packagesPath = fs.path.join(testPath, '.packages');
-      final DartDependencySetBuilder builder =
-          DartDependencySetBuilder(mainPath, packagesPath);
-      try {
-        builder.build();
-        fail('expect an exception to be thrown.');
-      } on DartDependencyException catch (error) {
-        expect(error.toString(), contains('rochambeau'));
-        expect(error.toString(), contains('pubspec.yaml'));
-      }
-    });
-
-    testUsingContext('does not change ASCII casing of path', () {
-      final String testPath = fs.path.join(dataPath, 'asci_casing');
-      final String mainPath = fs.path.join(testPath, 'main.dart');
-      final String packagesPath = fs.path.join(testPath, '.packages');
-      final DartDependencySetBuilder builder = DartDependencySetBuilder(mainPath, packagesPath);
-      final Set<String> deps = builder.build();
-      expect(deps, contains(endsWith('This_Import_Has_fuNNy_casING.dart')));
-    });
-
-    testUsingContext('bad_import', () {
-      final String testPath = fs.path.join(dataPath, 'bad_import');
-      final String mainPath = fs.path.join(testPath, 'main.dart');
-      final String packagesPath = fs.path.join(testPath, '.packages');
-      final DartDependencySetBuilder builder =
-          DartDependencySetBuilder(mainPath, packagesPath);
-      try {
-        builder.build();
-        fail('expect an exception to be thrown.');
-      } on DartDependencyException catch (error) {
-        expect(error.toString(), contains('Unable to parse URI'));
-      }
-    });
-  });
-}
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/.dartignore b/packages/flutter_tools/test/data/dart_dependencies_test/.dartignore
deleted file mode 100644
index e69de29..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/.dartignore
+++ /dev/null
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/.packages b/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/.packages
deleted file mode 100644
index 453dc36..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/.packages
+++ /dev/null
@@ -1 +0,0 @@
-self:lib/
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/This_Import_Has_fuNNy_casING.dart b/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/This_Import_Has_fuNNy_casING.dart
deleted file mode 100644
index 6081630..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/This_Import_Has_fuNNy_casING.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-String dummy = 'Hello';
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/analysis_options.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/analysis_options.yaml
deleted file mode 100644
index 4c1615a..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/analysis_options.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-analyzer:
-  exclude:
-    - '**'
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/main.dart b/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/main.dart
deleted file mode 100644
index 176c82d..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/main.dart
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'This_Import_Has_fuNNy_casING.dart';
-
-void main() {
-  print(dummy);
-}
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/pubspec.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/pubspec.yaml
deleted file mode 100644
index 99a0109..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/pubspec.yaml
+++ /dev/null
@@ -1 +0,0 @@
-name: self
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/analysis_options.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/analysis_options.yaml
deleted file mode 100644
index 4c1615a..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/analysis_options.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-analyzer:
-  exclude:
-    - '**'
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/main.dart b/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/main.dart
deleted file mode 100644
index 7f22c90..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/main.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'data://object.dart';
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/pubspec.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/pubspec.yaml
deleted file mode 100644
index 99a0109..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/pubspec.yaml
+++ /dev/null
@@ -1 +0,0 @@
-name: self
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/.packages b/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/.packages
deleted file mode 100644
index 453dc36..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/.packages
+++ /dev/null
@@ -1 +0,0 @@
-self:lib/
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/analysis_options.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/analysis_options.yaml
deleted file mode 100644
index 4c1615a..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/analysis_options.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-analyzer:
-  exclude:
-    - '**'
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/main.dart b/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/main.dart
deleted file mode 100644
index c3b231b..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/main.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:rochambeau/you_have_your_orders_now_go_man_go.dart';
-
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/pubspec.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/pubspec.yaml
deleted file mode 100644
index 99a0109..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/pubspec.yaml
+++ /dev/null
@@ -1 +0,0 @@
-name: self
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/.packages b/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/.packages
deleted file mode 100644
index 453dc36..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/.packages
+++ /dev/null
@@ -1 +0,0 @@
-self:lib/
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/analysis_options.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/analysis_options.yaml
deleted file mode 100644
index 4c1615a..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/analysis_options.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-analyzer:
-  exclude:
-    - '**'
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/main.dart b/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/main.dart
deleted file mode 100644
index 9c9d216..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/main.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'amaze/and/astonish.dart';
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/pubspec.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/pubspec.yaml
deleted file mode 100644
index 99a0109..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/pubspec.yaml
+++ /dev/null
@@ -1 +0,0 @@
-name: self
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/.packages b/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/.packages
deleted file mode 100644
index 0b55f79..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/.packages
+++ /dev/null
@@ -1,2 +0,0 @@
-flutter:file:///a/wild/non-existent/directory/has/appeared
-sdk-move-test:lib/
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/lib/main.dart b/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/lib/main.dart
deleted file mode 100644
index bcd11b5..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/lib/main.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// No content
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/pubspec.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/pubspec.yaml
deleted file mode 100644
index 9de2fbe..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/pubspec.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-name: sdk-move-test
-
-environment:
-  # The pub client defaults to an <2.0.0 sdk constraint which we need to explicitly overwrite.
-  sdk: ">=2.0.0-dev.68.0 <3.0.0"
-
-dependencies:
-  flutter:
-    sdk: flutter
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/good/.packages b/packages/flutter_tools/test/data/dart_dependencies_test/good/.packages
deleted file mode 100644
index 453dc36..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/good/.packages
+++ /dev/null
@@ -1 +0,0 @@
-self:lib/
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/good/analysis_options.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/good/analysis_options.yaml
deleted file mode 100644
index 4c1615a..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/good/analysis_options.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-analyzer:
-  exclude:
-    - '**'
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/good/foo.dart b/packages/flutter_tools/test/data/dart_dependencies_test/good/foo.dart
deleted file mode 100644
index 6853db7..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/good/foo.dart
+++ /dev/null
@@ -1,3 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/good/lib/bar.dart b/packages/flutter_tools/test/data/dart_dependencies_test/good/lib/bar.dart
deleted file mode 100644
index 6853db7..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/good/lib/bar.dart
+++ /dev/null
@@ -1,3 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/good/main.dart b/packages/flutter_tools/test/data/dart_dependencies_test/good/main.dart
deleted file mode 100644
index 659a48d..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/good/main.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'foo.dart';
-import 'package:self/bar.dart';
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/good/pubspec.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/good/pubspec.yaml
deleted file mode 100644
index 99a0109..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/good/pubspec.yaml
+++ /dev/null
@@ -1 +0,0 @@
-name: self
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/.packages b/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/.packages
deleted file mode 100644
index 453dc36..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/.packages
+++ /dev/null
@@ -1 +0,0 @@
-self:lib/
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/analysis_options.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/analysis_options.yaml
deleted file mode 100644
index 4c1615a..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/analysis_options.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-analyzer:
-  exclude:
-    - '**'
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/foo.dart b/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/foo.dart
deleted file mode 100644
index 6fbabe4..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/foo.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import bad programmer!
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/main.dart b/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/main.dart
deleted file mode 100644
index 43d48c0..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/main.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'foo.dart';
diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/pubspec.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/pubspec.yaml
deleted file mode 100644
index 99a0109..0000000
--- a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/pubspec.yaml
+++ /dev/null
@@ -1 +0,0 @@
-name: self
diff --git a/packages/flutter_tools/test/dependency_checker_test.dart b/packages/flutter_tools/test/dependency_checker_test.dart
deleted file mode 100644
index a815ff2..0000000
--- a/packages/flutter_tools/test/dependency_checker_test.dart
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:file/memory.dart';
-import 'package:flutter_tools/src/base/file_system.dart';
-import 'package:flutter_tools/src/cache.dart';
-import 'package:flutter_tools/src/commands/devices.dart';
-import 'package:flutter_tools/src/dart/dependencies.dart';
-import 'package:flutter_tools/src/dependency_checker.dart';
-
-import 'src/common.dart';
-import 'src/context.dart';
-
-void main() {
-  group('DependencyChecker', () {
-    final String dataPath = fs.path.join(
-        getFlutterRoot(),
-        'packages',
-        'flutter_tools',
-        'test',
-        'data',
-        'dart_dependencies_test',
-    );
-
-    FileSystem testFileSystem;
-
-    setUpAll(() {
-      Cache.disableLocking();
-    });
-
-    setUp(() {
-      testFileSystem = MemoryFileSystem();
-    });
-
-    testUsingContext('good', () {
-      final String testPath = fs.path.join(dataPath, 'good');
-      final String mainPath = fs.path.join(testPath, 'main.dart');
-      final String fooPath = fs.path.join(testPath, 'foo.dart');
-      final String barPath = fs.path.join(testPath, 'lib', 'bar.dart');
-      final String packagesPath = fs.path.join(testPath, '.packages');
-      final DartDependencySetBuilder builder =
-          DartDependencySetBuilder(mainPath, packagesPath);
-      final DependencyChecker dependencyChecker =
-          DependencyChecker(builder, null);
-
-      // Set file modification time on all dependencies to be in the past.
-      final DateTime baseTime = DateTime.now();
-      updateFileModificationTime(packagesPath, baseTime, -10);
-      updateFileModificationTime(mainPath, baseTime, -10);
-      updateFileModificationTime(fooPath, baseTime, -10);
-      updateFileModificationTime(barPath, baseTime, -10);
-      expect(dependencyChecker.check(baseTime), isFalse);
-
-      // Set .packages file modification time to be in the future.
-      updateFileModificationTime(packagesPath, baseTime, 20);
-      expect(dependencyChecker.check(baseTime), isTrue);
-
-      // Reset .packages file modification time.
-      updateFileModificationTime(packagesPath, baseTime, 0);
-      expect(dependencyChecker.check(baseTime), isFalse);
-
-      // Set 'package:self/bar.dart' file modification time to be in the future.
-      updateFileModificationTime(barPath, baseTime, 10);
-      expect(dependencyChecker.check(baseTime), isTrue);
-    });
-
-    testUsingContext('syntax error', () {
-      final String testPath = fs.path.join(dataPath, 'syntax_error');
-      final String mainPath = fs.path.join(testPath, 'main.dart');
-      final String fooPath = fs.path.join(testPath, 'foo.dart');
-      final String packagesPath = fs.path.join(testPath, '.packages');
-
-      final DartDependencySetBuilder builder =
-          DartDependencySetBuilder(mainPath, packagesPath);
-      final DependencyChecker dependencyChecker =
-          DependencyChecker(builder, null);
-
-      final DateTime baseTime = DateTime.now();
-
-      // Set file modification time on all dependencies to be in the past.
-      updateFileModificationTime(packagesPath, baseTime, -10);
-      updateFileModificationTime(mainPath, baseTime, -10);
-      updateFileModificationTime(fooPath, baseTime, -10);
-
-      // Dependencies are considered dirty because there is a syntax error in
-      // the .dart file.
-      expect(dependencyChecker.check(baseTime), isTrue);
-    });
-
-    /// Test a flutter tool move.
-    ///
-    /// Tests that the flutter tool doesn't crash and displays a warning when its own location
-    /// changed since it was last referenced to in a package's .packages file.
-    testUsingContext('moved flutter sdk', () async {
-      final Directory tempDir = fs.systemTempDirectory.createTempSync('flutter_dependency_checker_test.');
-
-      // Copy the golden input and let the test run in an isolated temporary in-memory file system.
-      const LocalFileSystem localFileSystem = LocalFileSystem();
-      final Directory sourcePath = localFileSystem.directory(localFileSystem.path.join(dataPath, 'changed_sdk_location'));
-      copyDirectorySync(sourcePath, tempDir);
-      fs.currentDirectory = tempDir;
-
-      // Doesn't matter what commands we run. Arbitrarily list devices here.
-      await createTestCommandRunner(DevicesCommand()).run(<String>['devices']);
-      expect(testLogger.errorText, contains('.packages'));
-      tryToDelete(tempDir);
-    }, overrides: <Type, Generator>{
-      FileSystem: () => testFileSystem,
-    });
-  });
-}
diff --git a/packages/flutter_tools/test/devfs_test.dart b/packages/flutter_tools/test/devfs_test.dart
index 318ed1b..653442b 100644
--- a/packages/flutter_tools/test/devfs_test.dart
+++ b/packages/flutter_tools/test/devfs_test.dart
@@ -7,10 +7,8 @@
 
 import 'package:file/file.dart';
 import 'package:file/memory.dart';
-import 'package:flutter_tools/src/asset.dart';
 import 'package:flutter_tools/src/base/io.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
-import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/devfs.dart';
 import 'package:flutter_tools/src/vmservice.dart';
 import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
@@ -22,16 +20,13 @@
 void main() {
   FileSystem fs;
   String filePath;
-  String filePath2;
   Directory tempDir;
   String basePath;
   DevFS devFS;
-  final AssetBundle assetBundle = AssetBundleFactory.defaultInstance.createBundle();
 
   setUpAll(() {
     fs = MemoryFileSystem();
     filePath = fs.path.join('lib', 'foo.txt');
-    filePath2 = fs.path.join('foo', 'bar.txt');
   });
 
   group('DevFSContent', () {
@@ -94,362 +89,6 @@
     });
   });
 
-  group('devfs local', () {
-    final MockDevFSOperations devFSOperations = MockDevFSOperations();
-    final MockResidentCompiler residentCompiler = MockResidentCompiler();
-
-    setUpAll(() {
-      tempDir = _newTempDir(fs);
-      basePath = tempDir.path;
-    });
-    tearDownAll(_cleanupTempDirs);
-
-    testUsingContext('create dev file system', () async {
-      // simulate workspace
-      final File file = fs.file(fs.path.join(basePath, filePath));
-      await file.parent.create(recursive: true);
-      file.writeAsBytesSync(<int>[1, 2, 3]);
-      _packages['my_project'] = fs.path.toUri('lib');
-
-      // simulate package
-      await _createPackage(fs, 'somepkg', 'somefile.txt');
-
-      devFS = DevFS.operations(devFSOperations, 'test', tempDir);
-      await devFS.create();
-      devFSOperations.expectMessages(<String>['create test']);
-      expect(devFS.assetPathsToEvict, isEmpty);
-
-      UpdateFSReport report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: false,
-      );
-      devFSOperations.expectMessages(<String>[
-        'writeFile test lib/foo.txt.dill build/app.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, isEmpty);
-
-      report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: true,
-      );
-      devFSOperations.expectMessages(<String>[
-        'writeFile test lib/foo.txt.dill build/app.dill.track.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, isEmpty);
-
-      expect(report.syncedBytes, 22);
-      expect(report.success, true);
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fs,
-    });
-
-    testUsingContext('add new file to local file system', () async {
-      final File file = fs.file(fs.path.join(basePath, filePath2));
-      await file.parent.create(recursive: true);
-      file.writeAsBytesSync(<int>[1, 2, 3, 4, 5, 6, 7]);
-      final UpdateFSReport report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: false,
-      );
-      devFSOperations.expectMessages(<String>[
-        'writeFile test lib/foo.txt.dill build/app.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, isEmpty);
-      expect(report.syncedBytes, 22);
-      expect(report.success, true);
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fs,
-    });
-
-    testUsingContext('modify existing file on local file system', () async {
-      UpdateFSReport report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: false,
-      );
-      devFSOperations.expectMessages(<String>[
-        'writeFile test lib/foo.txt.dill build/app.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, isEmpty);
-      expect(report.syncedBytes, 22);
-      expect(report.success, true);
-
-      final File file = fs.file(fs.path.join(basePath, filePath));
-      // Set the last modified time to 5 seconds in the past.
-      updateFileModificationTime(file.path, DateTime.now(), -5);
-      report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: false,
-      );
-      devFSOperations.expectMessages(<String>[
-        'writeFile test lib/foo.txt.dill build/app.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, isEmpty);
-      expect(report.syncedBytes, 22);
-      expect(report.success, true);
-
-      await file.writeAsBytes(<int>[1, 2, 3, 4, 5, 6]);
-      report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: false,
-      );
-      devFSOperations.expectMessages(<String>[
-        'writeFile test lib/foo.txt.dill build/app.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, isEmpty);
-      expect(report.syncedBytes, 22);
-      expect(report.success, true);
-
-      // Set the last modified time to 5 seconds in the past.
-      updateFileModificationTime(file.path, DateTime.now(), -5);
-      report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: true,
-      );
-      devFSOperations.expectMessages(<String>[
-        'writeFile test lib/foo.txt.dill build/app.dill.track.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, isEmpty);
-      expect(report.syncedBytes, 22);
-      expect(report.success, true);
-
-      await file.writeAsBytes(<int>[1, 2, 3, 4, 5, 6]);
-      report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: true,
-      );
-      devFSOperations.expectMessages(<String>[
-        'writeFile test lib/foo.txt.dill build/app.dill.track.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, isEmpty);
-      expect(report.syncedBytes, 22);
-      expect(report.success, true);
-
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fs,
-    });
-
-    testUsingContext('delete a file from the local file system', () async {
-      final File file = fs.file(fs.path.join(basePath, filePath));
-      await file.delete();
-      final UpdateFSReport report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: false,
-      );
-      devFSOperations.expectMessages(<String>[
-        'deleteFile test lib/foo.txt',
-        'writeFile test lib/foo.txt.dill build/app.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, isEmpty);
-      expect(report.syncedBytes, 22);
-      expect(report.success, true);
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fs,
-    });
-
-    testUsingContext('add new package', () async {
-      await _createPackage(fs, 'newpkg', 'anotherfile.txt');
-      UpdateFSReport report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: false,
-      );
-      devFSOperations.expectMessages(<String>[
-        'writeFile test lib/foo.txt.dill build/app.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, isEmpty);
-      expect(report.syncedBytes, 22);
-      expect(report.success, true);
-
-      report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: true,
-      );
-      devFSOperations.expectMessages(<String>[
-        'writeFile test lib/foo.txt.dill build/app.dill.track.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, isEmpty);
-      expect(report.syncedBytes, 22);
-      expect(report.success, true);
-
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fs,
-    });
-
-    testUsingContext('add new package with double slashes in URI', () async {
-      const String packageName = 'doubleslashpkg';
-      await _createPackage(fs, packageName, 'somefile.txt', doubleSlash: true);
-
-      final Set<String> fileFilter = <String>{};
-      final List<Uri> pkgUris = <Uri>[fs.path.toUri(basePath)]..addAll(_packages.values);
-      for (Uri pkgUri in pkgUris) {
-        if (!pkgUri.isAbsolute) {
-          pkgUri = fs.path.toUri(fs.path.join(basePath, pkgUri.path));
-        }
-        fileFilter.addAll(fs.directory(pkgUri)
-            .listSync(recursive: true)
-            .whereType<File>()
-            .map<String>((File file) => canonicalizePath(file.path))
-            .toList());
-      }
-      final UpdateFSReport report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        fileFilter: fileFilter,
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: false,
-      );
-      devFSOperations.expectMessages(<String>[
-        'writeFile test lib/foo.txt.dill build/app.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, isEmpty);
-      expect(report.syncedBytes, 22);
-      expect(report.success, true);
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fs,
-    });
-
-    testUsingContext('add an asset bundle', () async {
-      assetBundle.entries['a.txt'] = DevFSStringContent('abc');
-      final UpdateFSReport report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        bundle: assetBundle,
-        bundleDirty: true,
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: false,
-      );
-      devFSOperations.expectMessages(<String>[
-        'writeFile test ${_inAssetBuildDirectory(fs, 'a.txt')}',
-        'writeFile test lib/foo.txt.dill build/app.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, unorderedMatches(<String>['a.txt']));
-      devFS.assetPathsToEvict.clear();
-      expect(report.syncedBytes, 25);
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fs,
-    });
-
-    testUsingContext('add a file to the asset bundle - bundleDirty', () async {
-      assetBundle.entries['b.txt'] = DevFSStringContent('abcd');
-      final UpdateFSReport report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        bundle: assetBundle,
-        bundleDirty: true,
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: false,
-      );
-      // Expect entire asset bundle written because bundleDirty is true
-      devFSOperations.expectMessages(<String>[
-        'writeFile test ${_inAssetBuildDirectory(fs, 'a.txt')}',
-        'writeFile test ${_inAssetBuildDirectory(fs, 'b.txt')}',
-        'writeFile test lib/foo.txt.dill build/app.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, unorderedMatches(<String>[
-        'a.txt', 'b.txt']));
-      devFS.assetPathsToEvict.clear();
-      expect(report.syncedBytes, 29);
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fs,
-    });
-
-    testUsingContext('add a file to the asset bundle', () async {
-      assetBundle.entries['c.txt'] = DevFSStringContent('12');
-      final UpdateFSReport report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        bundle: assetBundle,
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: false,
-      );
-      devFSOperations.expectMessages(<String>[
-        'writeFile test ${_inAssetBuildDirectory(fs, 'c.txt')}',
-        'writeFile test lib/foo.txt.dill build/app.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, unorderedMatches(<String>[
-        'c.txt']));
-      devFS.assetPathsToEvict.clear();
-      expect(report.syncedBytes, 24);
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fs,
-    });
-
-    testUsingContext('delete a file from the asset bundle', () async {
-      assetBundle.entries.remove('c.txt');
-      final UpdateFSReport report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        bundle: assetBundle,
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: false,
-      );
-      devFSOperations.expectMessages(<String>[
-        'deleteFile test ${_inAssetBuildDirectory(fs, 'c.txt')}',
-        'writeFile test lib/foo.txt.dill build/app.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, unorderedMatches(<String>['c.txt']));
-      devFS.assetPathsToEvict.clear();
-      expect(report.syncedBytes, 22);
-      expect(report.success, true);
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fs,
-    });
-
-    testUsingContext('delete all files from the asset bundle', () async {
-      assetBundle.entries.clear();
-      final UpdateFSReport report = await devFS.update(
-        mainPath: 'lib/foo.txt',
-        bundle: assetBundle,
-        bundleDirty: true,
-        generator: residentCompiler,
-        pathToReload: 'lib/foo.txt.dill',
-        trackWidgetCreation: false,
-      );
-      devFSOperations.expectMessages(<String>[
-        'deleteFile test ${_inAssetBuildDirectory(fs, 'a.txt')}',
-        'deleteFile test ${_inAssetBuildDirectory(fs, 'b.txt')}',
-        'writeFile test lib/foo.txt.dill build/app.dill',
-      ]);
-      expect(devFS.assetPathsToEvict, unorderedMatches(<String>[
-        'a.txt', 'b.txt',
-      ]));
-      devFS.assetPathsToEvict.clear();
-      expect(report.syncedBytes, 22);
-      expect(report.success, true);
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fs,
-    });
-
-    testUsingContext('delete dev file system', () async {
-      await devFS.destroy();
-      devFSOperations.expectMessages(<String>['destroy test']);
-      expect(devFS.assetPathsToEvict, isEmpty);
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fs,
-    });
-  });
-
   group('devfs remote', () {
     MockVMService vmService;
     final MockResidentCompiler residentCompiler = MockResidentCompiler();
@@ -484,6 +123,7 @@
         generator: residentCompiler,
         pathToReload: 'lib/foo.txt.dill',
         trackWidgetCreation: false,
+        invalidatedFiles: <String>[],
       );
       vmService.expectMessages(<String>[
         'writeFile test lib/foo.txt.dill',
@@ -652,6 +292,3 @@
   fs.file(fs.path.join(_tempDirs[0].path, '.packages')).writeAsStringSync(sb.toString());
 }
 
-String _inAssetBuildDirectory(FileSystem fs, String filename) {
-  return '${fs.path.toUri(getAssetBuildDirectory()).path}/$filename';
-}
diff --git a/packages/flutter_tools/test/hot_test.dart b/packages/flutter_tools/test/hot_test.dart
index a9efb24..be36a3d 100644
--- a/packages/flutter_tools/test/hot_test.dart
+++ b/packages/flutter_tools/test/hot_test.dart
@@ -105,13 +105,13 @@
       firstBuildTime: anyNamed('firstBuildTime'),
       bundleFirstUpload: anyNamed('bundleFirstUpload'),
       bundleDirty: anyNamed('bundleDirty'),
-      fileFilter: anyNamed('fileFilter'),
       generator: anyNamed('generator'),
       fullRestart: anyNamed('fullRestart'),
       dillOutputPath: anyNamed('dillOutputPath'),
       trackWidgetCreation: anyNamed('trackWidgetCreation'),
       projectRootPath: anyNamed('projectRootPath'),
       pathToReload: anyNamed('pathToReload'),
+      invalidatedFiles: anyNamed('invalidatedFiles'),
     )).thenAnswer((Invocation _) => Future<UpdateFSReport>.value(
         UpdateFSReport(success: true, syncedBytes: 1000, invalidatedSourcesCount: 1)));
     when(mockDevFs.assetPathsToEvict).thenReturn(<String>{});
@@ -122,18 +122,6 @@
       when(mockArtifacts.getArtifactPath(Artifact.flutterPatchedSdkPath)).thenReturn('some/path');
     });
 
-    testUsingContext('no setup', () async {
-      final MockDevice mockDevice = MockDevice();
-      when(mockDevice.supportsHotReload).thenReturn(true);
-      when(mockDevice.supportsHotRestart).thenReturn(true);
-      final List<FlutterDevice> devices = <FlutterDevice>[
-        FlutterDevice(mockDevice, generator: residentCompiler, trackWidgetCreation: false),
-      ];
-      expect((await HotRunner(devices).restart(fullRestart: true)).isOk, false);
-    }, overrides: <Type, Generator>{
-      Artifacts: () => mockArtifacts,
-    });
-
     testUsingContext('Does not hot restart when device does not support it', () async {
       // Setup mocks
       final MockDevice mockDevice = MockDevice();
@@ -149,7 +137,7 @@
       expect(result.message, 'hotRestart not supported');
     }, overrides: <Type, Generator>{
       Artifacts: () => mockArtifacts,
-      HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true, computeDartDependencies: false),
+      HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
     });
 
     testUsingContext('Does not hot restart when one of many devices does not support it', () async {
@@ -171,7 +159,7 @@
       expect(result.message, 'hotRestart not supported');
     }, overrides: <Type, Generator>{
       Artifacts: () => mockArtifacts,
-      HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true, computeDartDependencies: false),
+      HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
     });
 
     testUsingContext('Does hot restarts when all devices support it', () async {
@@ -193,7 +181,7 @@
       expect(result.message, isNot('hotRestart not supported'));
     }, overrides: <Type, Generator>{
       Artifacts: () => mockArtifacts,
-      HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true, computeDartDependencies: false),
+      HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
     });
 
     testUsingContext('setup function fails', () async {
@@ -226,7 +214,7 @@
       expect(result.message, isNot('setupHotRestart failed'));
     }, overrides: <Type, Generator>{
       Artifacts: () => mockArtifacts,
-      HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true, computeDartDependencies: false),
+      HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
     });
 
     group('shutdown hook tests', () {
@@ -235,7 +223,6 @@
       setUp(() {
         shutdownTestingConfig = TestHotRunnerConfig(
           successfulSetup: true,
-          computeDartDependencies: false,
         );
       });
 
@@ -283,10 +270,7 @@
 }
 
 class TestHotRunnerConfig extends HotRunnerConfig {
-  TestHotRunnerConfig({@required this.successfulSetup, bool computeDartDependencies = true}) {
-    this.computeDartDependencies = computeDartDependencies;
-  }
-
+  TestHotRunnerConfig({@required this.successfulSetup});
   bool successfulSetup;
   bool shutdownHookCalled = false;
 
diff --git a/packages/flutter_tools/test/project_file_invalidator_test.dart b/packages/flutter_tools/test/project_file_invalidator_test.dart
new file mode 100644
index 0000000..c38984e
--- /dev/null
+++ b/packages/flutter_tools/test/project_file_invalidator_test.dart
@@ -0,0 +1,234 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:file/memory.dart';
+import 'package:flutter_tools/src/base/file_system.dart';
+import 'package:flutter_tools/src/base/logger.dart';
+import 'package:flutter_tools/src/base/platform.dart';
+import 'package:flutter_tools/src/project.dart';
+import 'package:flutter_tools/src/run_hot.dart';
+import 'package:mockito/mockito.dart';
+
+import 'src/common.dart';
+import 'src/context.dart';
+
+void main() {
+  final Platform windowsPlatform = MockPlatform();
+  final Platform notWindowsPlatform = MockPlatform();
+  final BufferLogger bufferLogger = BufferLogger();
+  when(windowsPlatform.isWindows).thenReturn(true);
+  when(notWindowsPlatform.isWindows).thenReturn(false);
+
+  group('ProjectFileInvalidator linux/mac', () {
+    final MemoryFileSystem memoryFileSystem = MemoryFileSystem();
+    final File packagesFile = memoryFileSystem.file('.packages')
+      ..createSync()
+      ..writeAsStringSync(r'''
+foo:file:///foo/lib/
+bar:file:///.pub-cache/bar/lib/
+baz:file:///baz/lib/
+test_package:file:///lib/
+''');
+    final File pubspecFile = memoryFileSystem.file('pubspec.yaml')
+      ..createSync()
+      ..writeAsStringSync(r'''
+name: test_package
+
+dependencies:
+  foo: any
+  bar:
+
+dev_dependencies:
+  baz: any
+''');
+    final File mainFile = memoryFileSystem.file('lib/main.dart')
+      ..createSync(recursive: true)
+      ..writeAsStringSync(r'''
+void main() {}
+''');
+    final File fooFile = memoryFileSystem.file('foo/lib/foo.dart')
+      ..createSync(recursive: true)
+      ..writeAsStringSync('');
+    memoryFileSystem.file('bar/lib/bar.dart')
+      ..createSync(recursive: true)
+      ..writeAsStringSync('');
+    final File bazFile = memoryFileSystem.file('baz/lib/baz.dart')
+      ..createSync(recursive: true)
+      ..writeAsStringSync('');
+
+    testUsingContext('No .packages, no pubspec', () async {
+      // Instead of setting up multiple filesystems, passing a .packages file which does not exist.
+      final ProjectFileInvalidator invalidator = ProjectFileInvalidator('.packages-wrong', null);
+      invalidator.findInvalidated();
+      expect(invalidator.packageMap, isEmpty);
+      expect(invalidator.updateTime, isEmpty);
+    }, overrides: <Type, Generator>{
+      FileSystem: () => memoryFileSystem,
+      Platform: () => notWindowsPlatform,
+    });
+
+    testUsingContext('.packages only', () async {
+      final ProjectFileInvalidator invalidator = ProjectFileInvalidator(packagesFile.path, null);
+      invalidator.findInvalidated();
+      expect(invalidator.packageMap, <String, Uri>{
+        'foo': Uri.parse('file:///foo/lib/'),
+        // Excluded because it is in pub cache.
+        // 'bar': Uri.parse('file:///.pub-cache/bar/lib/'),
+        'baz': Uri.parse('file:///baz/lib/'),
+        'test_package': Uri.parse('file:///lib/'),
+      });
+      expect(invalidator.updateTime, <String, int>{
+        '/baz/lib/baz.dart': bazFile.statSync().modified.millisecondsSinceEpoch,
+        '/lib/main.dart': mainFile.statSync().modified.millisecondsSinceEpoch,
+        '/foo/lib/foo.dart': fooFile.statSync().modified.millisecondsSinceEpoch,
+      });
+    }, overrides: <Type, Generator>{
+      FileSystem: () => memoryFileSystem,
+      Platform: () => notWindowsPlatform,
+    });
+
+    testUsingContext('.packages and pubspec', () async {
+      final FlutterProject flutterProject = await FlutterProject.fromDirectory(pubspecFile.parent);
+      final ProjectFileInvalidator invalidator = ProjectFileInvalidator(packagesFile.path, flutterProject);
+      invalidator.findInvalidated();
+      expect(invalidator.packageMap, <String, Uri>{
+        'foo': Uri.parse('file:///foo/lib/'),
+        // Excluded because it is in pub cache.
+        // 'bar': Uri.parse('file:///.pub-cache/bar/lib/'),
+        // Excluded because it is a dev dependency/
+        // 'baz': Uri.parse('file:///baz/lib/'),
+        'test_package': Uri.parse('file:///lib/'),
+      });
+      expect(invalidator.updateTime, <String, int>{
+        '/foo/lib/foo.dart': fooFile.statSync().modified.millisecondsSinceEpoch,
+        '/lib/main.dart':mainFile.statSync().modified.millisecondsSinceEpoch,
+      });
+      expect(invalidator.findInvalidated(), isEmpty);
+
+      // Invalidate main.dart.
+      mainFile.writeAsStringSync('void main() { }');
+      expect(invalidator.findInvalidated(), <String>['/lib/main.dart']);
+    }, overrides: <Type, Generator>{
+      FileSystem: () => memoryFileSystem,
+      Platform: () => notWindowsPlatform,
+    });
+
+    testUsingContext('update to .packages triggers warning', () async {
+      final FlutterProject flutterProject = await FlutterProject.fromDirectory(pubspecFile.parent);
+      final ProjectFileInvalidator invalidator = ProjectFileInvalidator(packagesFile.path, flutterProject);
+      invalidator.findInvalidated();
+      packagesFile.writeAsStringSync(r'''
+foo:file:///foo/lib/
+bar:file:///.pub-cache/bar/lib/
+baz:file:///baz/lib/
+new_dep:file:///new_dep/lib/
+test_package:file:///lib/
+''');
+      invalidator.findInvalidated();
+      expect(bufferLogger.errorText, 'Warning: updated dependencies detected. The Flutter application will require a restart to safely use new packages.\n');
+    }, overrides: <Type, Generator>{
+      FileSystem: () => memoryFileSystem,
+      Platform: () => notWindowsPlatform,
+      Logger: () => bufferLogger,
+    });
+  });
+
+  group('ProjectFileInvalidator windows', () {
+    final MemoryFileSystem memoryFileSystem = MemoryFileSystem(style: FileSystemStyle.windows);
+    // On windows .packages still contains file Uris, albeit ones with the Drive prefix.
+    final File packagesFile = memoryFileSystem.file(r'C:\.packages')
+      ..createSync()
+      ..writeAsStringSync(r'''
+foo:file:///C:/foo/lib/
+bar:file:///C:/Pub/Cache/bar/lib/
+baz:file:///C:/baz/lib/
+test_package:file:///C:/lib/
+''');
+    memoryFileSystem.file(r'C:\pubspec.yaml')
+      ..createSync()
+      ..writeAsStringSync(r'''
+name: test_package
+
+dependencies:
+  foo: any
+  bar:
+
+dev_dependencies:
+  baz: any
+''');
+    final File mainFile = memoryFileSystem.file(r'C:\lib\main.dart')
+      ..createSync(recursive: true)
+      ..writeAsStringSync(r'''
+void main() {}
+''');
+    final File fooFile = memoryFileSystem.file(r'C:\foo\lib\foo.dart')
+      ..createSync(recursive: true)
+      ..writeAsStringSync('');
+    memoryFileSystem.file(r'C:\bar\lib\bar.dart')
+      ..createSync(recursive: true)
+      ..writeAsStringSync('');
+    final File bazFile = memoryFileSystem.file(r'C:\baz\lib\baz.dart')
+      ..createSync(recursive: true)
+      ..writeAsStringSync('');
+
+    testUsingContext('No .packages, no pubspec', () async {
+      // Instead of setting up multiple filesystems, passing a .packages file which does not exist.
+      final ProjectFileInvalidator invalidator = ProjectFileInvalidator('.packages-wrong', null);
+      invalidator.findInvalidated();
+      expect(invalidator.packageMap, isEmpty);
+      expect(invalidator.updateTime, isEmpty);
+    }, overrides: <Type, Generator>{
+      FileSystem: () => memoryFileSystem,
+      Platform: () => windowsPlatform,
+    });
+
+    testUsingContext('.packages only', () async {
+      final ProjectFileInvalidator invalidator = ProjectFileInvalidator(packagesFile.path, null);
+      invalidator.findInvalidated();
+      expect(invalidator.packageMap, <String, Uri>{
+        'foo': Uri.file(r'C:\foo\lib\', windows: true),
+        // Excluded because it is in pub cache.
+        // 'bar': Uri.parse('file:///Pub/Cache/bar/lib/'),
+        'baz': Uri.file(r'C:\baz\lib\', windows: true),
+        'test_package': Uri.file(r'C:\lib\', windows: true),
+      });
+      expect(invalidator.updateTime, <String, int>{
+        r'C:\baz\lib\baz.dart': bazFile.statSync().modified.millisecondsSinceEpoch,
+        r'C:\lib\main.dart': mainFile.statSync().modified.millisecondsSinceEpoch,
+        r'C:\foo\lib\foo.dart': fooFile.statSync().modified.millisecondsSinceEpoch,
+      });
+    }, overrides: <Type, Generator>{
+      FileSystem: () => memoryFileSystem,
+      Platform: () => windowsPlatform,
+    });
+
+    testUsingContext('.packages and pubspec', () async {
+      final FlutterProject flutterProject = await FlutterProject.fromDirectory(fs.directory(r'C:\'));
+      final ProjectFileInvalidator invalidator = ProjectFileInvalidator(packagesFile.path, flutterProject);
+      invalidator.findInvalidated();
+      expect(invalidator.packageMap, <String, Uri>{
+        'foo': Uri.file(r'C:\foo\lib\', windows: true),
+        // Excluded because it is in pub cache.
+        // 'bar': Uri.parse('file:///C:/Pub/Cache/bar/lib/'),
+        // Excluded because it is a dev dependency/
+        // 'baz': Uri.parse('file:///baz/lib/'),
+        'test_package': Uri.file(r'C:\lib\', windows: true),
+      });
+      expect(invalidator.updateTime, <String, int>{
+        r'C:\lib\main.dart': mainFile.statSync().modified.millisecondsSinceEpoch,
+        r'C:\foo\lib\foo.dart': fooFile.statSync().modified.millisecondsSinceEpoch,
+      });
+      expect(invalidator.findInvalidated(), isEmpty);
+
+      // Invalidate main.dart.
+      mainFile.writeAsStringSync('void main() { }');
+      expect(invalidator.findInvalidated(), <String>['file:///C:/lib/main.dart']);
+    }, overrides: <Type, Generator>{
+      FileSystem: () => memoryFileSystem,
+      Platform: () => windowsPlatform,
+    });
+  });
+}
+
+class MockPlatform extends Mock implements Platform {}
diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart
index 52c5fc9..42c9d8f 100644
--- a/packages/flutter_tools/test/src/mocks.dart
+++ b/packages/flutter_tools/test/src/mocks.dart
@@ -460,12 +460,6 @@
     messages.add(message);
     devicePathToContent[deviceUri] = content;
   }
-
-  @override
-  Future<dynamic> deleteFile(String fsName, Uri deviceUri) async {
-    messages.add('deleteFile $fsName $deviceUri');
-    devicePathToContent.remove(deviceUri);
-  }
 }
 
 class MockResidentCompiler extends BasicMock implements ResidentCompiler {