[flutter_tools] Launch DevTools with 'v' (#53902)
diff --git a/packages/flutter_tools/lib/src/base/command_help.dart b/packages/flutter_tools/lib/src/base/command_help.dart
index 11c5fbd..b58d5a3 100644
--- a/packages/flutter_tools/lib/src/base/command_help.dart
+++ b/packages/flutter_tools/lib/src/base/command_help.dart
@@ -140,6 +140,12 @@
'debugDumpRenderTree',
);
+ CommandHelpOption _v;
+ CommandHelpOption get v => _v ??= _makeOption(
+ 'v',
+ 'Launch DevTools.',
+ );
+
CommandHelpOption _w;
CommandHelpOption get w => _w ??= _makeOption(
'w',
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index 7f013a4..47553f8 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -4,6 +4,7 @@
import 'dart:async';
+import 'package:devtools_server/devtools_server.dart' as devtools_server;
import 'package:meta/meta.dart';
import 'application_package.dart';
@@ -650,6 +651,8 @@
final CommandHelp commandHelp;
+ io.HttpServer _devtoolsServer;
+
bool _exited = false;
Completer<int> _finished = Completer<int>();
bool hotMode;
@@ -769,12 +772,14 @@
Future<void> exit() async {
_exited = true;
+ await shutdownDevtools();
await stopEchoingDeviceLog();
await preExit();
await exitApp();
}
Future<void> detach() async {
+ await shutdownDevtools();
await stopEchoingDeviceLog();
await preExit();
appFinished();
@@ -982,6 +987,31 @@
}
}
+ Future<void> launchDevTools() async {
+ try {
+ assert(supportsServiceProtocol);
+ _devtoolsServer ??= await devtools_server.serveDevTools(
+ enableStdinCommands: false,
+ );
+ await devtools_server.launchDevTools(
+ <String, dynamic>{
+ 'reuseWindows': true,
+ },
+ flutterDevices.first.vmService.httpAddress,
+ 'http://${_devtoolsServer.address.host}:${_devtoolsServer.port}',
+ false, // headless mode,
+ false, // machine mode
+ );
+ } on Exception catch (e, st) {
+ globals.printTrace('Failed to launch DevTools: $e\n$st');
+ }
+ }
+
+ Future<void> shutdownDevtools() async {
+ await _devtoolsServer?.close();
+ _devtoolsServer = null;
+ }
+
Future<void> _serviceProtocolDone(dynamic object) async {
globals.printTrace('Service protocol connection closed.');
}
@@ -1064,6 +1094,7 @@
if (supportsCanvasKit){
commandHelp.k.print();
}
+ commandHelp.v.print();
// `P` should precede `a`
commandHelp.P.print();
commandHelp.a.print();
@@ -1299,6 +1330,12 @@
return true;
}
return false;
+ case 'v':
+ if (residentRunner.supportsServiceProtocol) {
+ await residentRunner.launchDevTools();
+ return true;
+ }
+ return false;
case 'w':
case 'W':
if (residentRunner.supportsServiceProtocol) {
diff --git a/packages/flutter_tools/test/general.shard/base/command_help_test.dart b/packages/flutter_tools/test/general.shard/base/command_help_test.dart
index daa9b64..6b97735 100644
--- a/packages/flutter_tools/test/general.shard/base/command_help_test.dart
+++ b/packages/flutter_tools/test/general.shard/base/command_help_test.dart
@@ -66,6 +66,7 @@
expect(commandHelp.r.toString().length, lessThanOrEqualTo(expectedWidth));
expect(commandHelp.s.toString().length, lessThanOrEqualTo(expectedWidth));
expect(commandHelp.t.toString().length, lessThanOrEqualTo(expectedWidth));
+ expect(commandHelp.v.toString().length, lessThanOrEqualTo(expectedWidth));
expect(commandHelp.w.toString().length, lessThanOrEqualTo(expectedWidth));
expect(commandHelp.z.toString().length, lessThanOrEqualTo(expectedWidth));
}
@@ -95,6 +96,7 @@
expect(commandHelp.r.toString(), startsWith('\x1B[1mr\x1B[22m'));
expect(commandHelp.s.toString(), startsWith('\x1B[1ms\x1B[22m'));
expect(commandHelp.t.toString(), startsWith('\x1B[1mt\x1B[22m'));
+ expect(commandHelp.v.toString(), startsWith('\x1B[1mv\x1B[22m'));
expect(commandHelp.w.toString(), startsWith('\x1B[1mw\x1B[22m'));
expect(commandHelp.z.toString(), startsWith('\x1B[1mz\x1B[22m'));
});
@@ -170,6 +172,7 @@
expect(commandHelp.r.toString(), equals('\x1B[1mr\x1B[22m Hot reload. $fire$fire$fire'));
expect(commandHelp.s.toString(), equals('\x1B[1ms\x1B[22m Save a screenshot to flutter.png.'));
expect(commandHelp.t.toString(), equals('\x1B[1mt\x1B[22m Dump rendering tree to the console. \x1B[1;30m(debugDumpRenderTree)\x1B[39m'));
+ expect(commandHelp.v.toString(), equals('\x1B[1mv\x1B[22m Launch DevTools.'));
expect(commandHelp.w.toString(), equals('\x1B[1mw\x1B[22m Dump widget hierarchy to the console. \x1B[1;30m(debugDumpApp)\x1B[39m'));
expect(commandHelp.z.toString(), equals('\x1B[1mz\x1B[22m Toggle elevation checker.'));
});
@@ -195,6 +198,7 @@
expect(commandHelp.r.toString(), equals('r Hot reload. $fire$fire$fire'));
expect(commandHelp.s.toString(), equals('s Save a screenshot to flutter.png.'));
expect(commandHelp.t.toString(), equals('t Dump rendering tree to the console. (debugDumpRenderTree)'));
+ expect(commandHelp.v.toString(), equals('v Launch DevTools.'));
expect(commandHelp.w.toString(), equals('w Dump widget hierarchy to the console. (debugDumpApp)'));
expect(commandHelp.z.toString(), equals('z Toggle elevation checker.'));
});
diff --git a/packages/flutter_tools/test/general.shard/resident_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_runner_test.dart
index bbf44b2..991f772 100644
--- a/packages/flutter_tools/test/general.shard/resident_runner_test.dart
+++ b/packages/flutter_tools/test/general.shard/resident_runner_test.dart
@@ -406,6 +406,7 @@
commandHelp.p,
commandHelp.o,
commandHelp.z,
+ commandHelp.v,
commandHelp.P,
commandHelp.a,
'An Observatory debugger and profiler on null is available at: null',
diff --git a/packages/flutter_tools/test/general.shard/terminal_handler_test.dart b/packages/flutter_tools/test/general.shard/terminal_handler_test.dart
index 7364771..4c289b9 100644
--- a/packages/flutter_tools/test/general.shard/terminal_handler_test.dart
+++ b/packages/flutter_tools/test/general.shard/terminal_handler_test.dart
@@ -364,6 +364,13 @@
verifyNever(mockResidentRunner.debugDumpSemanticsTreeInInverseHitTestOrder());
});
+ testUsingContext('v - launchDevTools', () async {
+ when(mockResidentRunner.supportsServiceProtocol).thenReturn(true);
+ await terminalHandler.processTerminalInput('v');
+
+ verify(mockResidentRunner.launchDevTools()).called(1);
+ });
+
testUsingContext('w,W - debugDumpApp with service protocol', () async {
await terminalHandler.processTerminalInput('w');
await terminalHandler.processTerminalInput('W');