Separate hot reload and hot restart capabilities. (#24122)
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart index 94b165e..601b04a 100644 --- a/packages/flutter_tools/lib/src/android/android_device.dart +++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -469,7 +469,10 @@ } @override - bool get supportsHotMode => true; + bool get supportsHotReload => true; + + @override + bool get supportsHotRestart => true; @override Future<bool> stopApp(ApplicationPackage app) {
diff --git a/packages/flutter_tools/lib/src/commands/attach.dart b/packages/flutter_tools/lib/src/commands/attach.dart index b2282c1..a4d9d6c 100644 --- a/packages/flutter_tools/lib/src/commands/attach.dart +++ b/packages/flutter_tools/lib/src/commands/attach.dart
@@ -209,7 +209,9 @@ } } finally { final List<ForwardedPort> ports = device.portForwarder.forwardedPorts.toList(); - ports.forEach(device.portForwarder.unforward); + for (ForwardedPort port in ports) { + await device.portForwarder.unforward(port); + } } return null; }
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart index f8799ae..5bf05f4 100644 --- a/packages/flutter_tools/lib/src/commands/daemon.dart +++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -458,7 +458,7 @@ } bool isRestartSupported(bool enableHotReload, Device device) => - enableHotReload && device.supportsHotMode; + enableHotReload && device.supportsHotRestart; Future<OperationResult> _inProgressHotReload;
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 461c4fc..cb0e66e 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -336,8 +336,8 @@ if (hotMode) { for (Device device in devices) { - if (!device.supportsHotMode) - throwToolExit('Hot mode is not supported by ${device.name}. Run with --no-hot.'); + if (!device.supportsHotReload) + throwToolExit('Hot reload is not supported by ${device.name}. Run with --no-hot.'); } }
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 8225f4f..3c6d378 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart
@@ -270,8 +270,11 @@ bool ipv6 = false, }); - /// Does this device implement support for hot reloading / restarting? - bool get supportsHotMode => true; + /// Whether this device implements support for hot reload. + bool get supportsHotReload => true; + + /// Whether this device implements support for hot restart. + bool get supportsHotRestart => true; /// Stop an app package on the current device. Future<bool> stopApp(ApplicationPackage app);
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart index 42ea084..dba2a3a 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
@@ -97,7 +97,10 @@ FuchsiaDevice(String id, { this.name }) : super(id); @override - bool get supportsHotMode => true; + bool get supportsHotReload => true; + + @override + bool get supportsHotRestart => false; @override final String name;
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart index d65689f..c55cb85 100644 --- a/packages/flutter_tools/lib/src/ios/devices.dart +++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -124,7 +124,10 @@ final String _sdkVersion; @override - bool get supportsHotMode => true; + bool get supportsHotReload => true; + + @override + bool get supportsHotRestart => true; @override final String name;
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart index 14b8dcb..69c8a1c 100644 --- a/packages/flutter_tools/lib/src/ios/simulators.dart +++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -223,7 +223,10 @@ Future<bool> get isLocalEmulator async => true; @override - bool get supportsHotMode => true; + bool get supportsHotReload => true; + + @override + bool get supportsHotRestart => true; Map<ApplicationPackage, _IOSSimulatorLogReader> _logReaders; _IOSSimulatorDevicePortForwarder _portForwarder;
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index 05c9562..5be4191 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -460,6 +460,17 @@ bool get isRunningRelease => debuggingOptions.buildInfo.isRelease; bool get supportsServiceProtocol => isRunningDebug || isRunningProfile; + /// Whether this runner can hot restart. + /// + /// To prevent scenarios where only a subset of devices are hot restarted, + /// the runner requires that all attached devices can support hot restart + /// before enabling it. + bool get canHotRestart { + return flutterDevices.every((FlutterDevice device) { + return device.device.supportsHotRestart; + }); + } + /// Start the app and keep the process running during its lifetime. Future<int> run({ Completer<DebugConnectionInfo> connectionInfoCompleter,
diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart index b0de03a..ded7a25 100644 --- a/packages/flutter_tools/lib/src/run_hot.dart +++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -289,7 +289,16 @@ Future<void> handleTerminalCommand(String code) async { final String lower = code.toLowerCase(); if (lower == 'r') { - final OperationResult result = await restart(fullRestart: code == 'R'); + OperationResult result; + if (code == 'R') { + // If hot restart is not supported for all devices, ignore the command. + if (!canHotRestart) { + return; + } + result = await restart(fullRestart: true); + } else { + result = await restart(fullRestart: false); + } if (!result.isOk) { // TODO(johnmccutchan): Attempt to determine the number of errors that // occurred and tighten this message. @@ -541,6 +550,9 @@ Future<OperationResult> restart({ bool fullRestart = false, bool pauseAfterRestart = false, String reason }) async { final Stopwatch timer = Stopwatch()..start(); if (fullRestart) { + if (!canHotRestart) { + return OperationResult(1, 'hotRestart not supported'); + } final Status status = logger.startProgress( 'Performing hot restart...', progressId: 'hot.restart', @@ -780,9 +792,12 @@ @override void printHelp({ @required bool details }) { const String fire = '🔥'; + String rawMessage = ' To hot reload changes while running, press "r". '; + if (canHotRestart) { + rawMessage += 'To hot restart (and rebuild state), press "R".'; + } final String message = terminal.color( - fire + terminal.bolden(' To hot reload changes while running, press "r". ' - 'To hot restart (and rebuild state), press "R".'), + fire + terminal.bolden(rawMessage), TerminalColor.red, ); printStatus(message);