diff --git a/packages/flutter_tools/doc/daemon.md b/packages/flutter_tools/doc/daemon.md index b1e2f5f2ce..73a85c29a9 100644 --- a/packages/flutter_tools/doc/daemon.md +++ b/packages/flutter_tools/doc/daemon.md @@ -136,7 +136,7 @@ The `stop()` command takes one parameter, `appId`. It returns a `bool` to indica #### app.start -This is sent when an app is starting. The `params` field will be a map with the fields `appId`, `directory`, `deviceId`, and `launchMode`. +This is sent when an app is starting. The `params` field will be a map with the fields `appId`, `directory`, `deviceId`, `launchMode` (`run`/`attach`) and `mode` (`debug`, `profile`, `release`, `jit_release`). #### app.debugPort diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart index 4caaa82844..4c82d7faf4 100644 --- a/packages/flutter_tools/lib/src/commands/daemon.dart +++ b/packages/flutter_tools/lib/src/commands/daemon.dart @@ -613,6 +613,7 @@ class AppDomain extends Domain { 'directory': projectDirectory, 'supportsRestart': isRestartSupported(enableHotReload, device), 'launchMode': launchMode.toString(), + 'mode': runner.debuggingOptions.buildInfo.modeName, }); Completer? connectionInfoCompleter; diff --git a/packages/flutter_tools/lib/src/debug_adapters/flutter_adapter.dart b/packages/flutter_tools/lib/src/debug_adapters/flutter_adapter.dart index f02b2f511c..231c3f563b 100644 --- a/packages/flutter_tools/lib/src/debug_adapters/flutter_adapter.dart +++ b/packages/flutter_tools/lib/src/debug_adapters/flutter_adapter.dart @@ -393,6 +393,16 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter { // session (which is much slower, but required for profile/release mode). final bool supportsRestart = (params['supportsRestart'] as bool?) ?? false; sendEvent(CapabilitiesEventBody(capabilities: Capabilities(supportsRestartRequest: supportsRestart))); + + // Send a custom event so the editor has info about the app starting. + // + // This message contains things like the `deviceId` and `mode` that the + // client might not know about if they were inferred or set by users custom + // args. + sendEvent( + RawEventBody(params), + eventType: 'flutter.appStart', + ); } /// Handles the app.started event from Flutter. diff --git a/packages/flutter_tools/test/general.shard/dap/mocks.dart b/packages/flutter_tools/test/general.shard/dap/mocks.dart index 498949235f..515d151389 100644 --- a/packages/flutter_tools/test/general.shard/dap/mocks.dart +++ b/packages/flutter_tools/test/general.shard/dap/mocks.dart @@ -107,16 +107,18 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter { // Simulate the app starting by triggering handling of events that Flutter // would usually write to stdout. if (simulateAppStarted) { - simulateStdoutMessage({ - 'event': 'app.started', - }); simulateStdoutMessage({ 'event': 'app.start', 'params': { 'appId': 'TEST', 'supportsRestart': supportsRestart, + 'deviceId': 'flutter-tester', + 'mode': 'debug', } }); + simulateStdoutMessage({ + 'event': 'app.started', + }); } } diff --git a/packages/flutter_tools/test/integration.shard/debug_adapter/flutter_adapter_test.dart b/packages/flutter_tools/test/integration.shard/debug_adapter/flutter_adapter_test.dart index a09af55315..c27b7c295a 100644 --- a/packages/flutter_tools/test/integration.shard/debug_adapter/flutter_adapter_test.dart +++ b/packages/flutter_tools/test/integration.shard/debug_adapter/flutter_adapter_test.dart @@ -452,6 +452,30 @@ void main() { await dap.client.terminate(); }); + testWithoutContext('provides appStarted events to the client', () async { + final BasicProject project = BasicProject(); + await project.setUpIn(tempDir); + + // Launch the app and wait for it to send a 'flutter.appStart' event. + final Future appStartFuture = dap.client.event('flutter.appStart'); + await Future.wait(>[ + appStartFuture, + dap.client.start( + launch: () => dap.client.launch( + cwd: project.dir.path, + toolArgs: ['-d', 'flutter-tester'], + ), + ), + ], eagerError: true); + + await dap.client.terminate(); + + final Event appStart = await appStartFuture; + final Map params = appStart.body! as Map; + expect(params['deviceId'], 'flutter-tester'); + expect(params['mode'], 'debug'); + }); + testWithoutContext('provides appStarted events to the client', () async { final BasicProject project = BasicProject(); await project.setUpIn(tempDir); diff --git a/packages/flutter_tools/test/integration.shard/flutter_run_test.dart b/packages/flutter_tools/test/integration.shard/flutter_run_test.dart index 8a58f03849..e3b94ed4c4 100644 --- a/packages/flutter_tools/test/integration.shard/flutter_run_test.dart +++ b/packages/flutter_tools/test/integration.shard/flutter_run_test.dart @@ -68,4 +68,10 @@ void main() { matches: isNotEmpty, ); }); + + testWithoutContext('reports deviceId and mode in app.start event', () async { + await flutter.run(); + expect(flutter.currentRunningDeviceId, 'flutter-tester'); + expect(flutter.currentRunningMode, 'debug'); + }); } diff --git a/packages/flutter_tools/test/integration.shard/test_driver.dart b/packages/flutter_tools/test/integration.shard/test_driver.dart index db034d4a29..31bf7f53c3 100644 --- a/packages/flutter_tools/test/integration.shard/test_driver.dart +++ b/packages/flutter_tools/test/integration.shard/test_driver.dart @@ -496,6 +496,11 @@ class FlutterRunTestDriver extends FlutterTestDriver { }); String? _currentRunningAppId; + String? _currentRunningDeviceId; + String? _currentRunningMode; + + String? get currentRunningDeviceId => _currentRunningDeviceId; + String? get currentRunningMode => _currentRunningMode; Future run({ bool withDebugger = false, @@ -611,6 +616,7 @@ class FlutterRunTestDriver extends FlutterTestDriver { // Set this up now, but we don't wait it yet. We want to make sure we don't // miss it while waiting for debugPort below. + final Future> start = _waitFor(event: 'app.start', timeout: appStartTimeout); final Future> started = _waitFor(event: 'app.started', timeout: appStartTimeout); if (withDebugger) { @@ -630,9 +636,13 @@ class FlutterRunTestDriver extends FlutterTestDriver { _attachPort = attachPort; } - // Now await the started event; if it had already happened the future will + // Now await the start/started events; if it had already happened the future will // have already completed. - _currentRunningAppId = ((await started)['params'] as Map?)?['appId'] as String?; + final Map? startParams = (await start)['params'] as Map?; + final Map? startedParams = (await started)['params'] as Map?; + _currentRunningAppId = startedParams?['appId'] as String?; + _currentRunningDeviceId = startParams?['deviceId'] as String?; + _currentRunningMode = startParams?['mode'] as String?; prematureExitGuard.complete(); } on Exception catch (error, stackTrace) { prematureExitGuard.completeError(Exception(error.toString()), stackTrace);