| // Copyright 2014 The Flutter 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:args/args.dart'; | |
| import '../base/common.dart'; | |
| import '../base/logger.dart'; | |
| import '../base/platform.dart'; | |
| import '../base/utils.dart'; | |
| import '../context/tool_context.dart'; | |
| import '../doctor.dart'; | |
| import '../doctor_validator.dart'; | |
| import '../emulator.dart'; | |
| import '../runner/flutter_command.dart'; | |
| /// The `flutter emulators` command, which lists, launches, and creates emulators. | |
| class EmulatorsCommand extends FlutterCommand { | |
| EmulatorsCommand({ | |
| required this._doctor, | |
| required this._emulatorManager, | |
| required ToolContext super.toolContext, | |
| super.verboseHelp, | |
| }) { | |
| argParser.addOption('launch', help: 'The full or partial ID of the emulator to launch.'); | |
| argParser.addFlag( | |
| 'cold', | |
| help: 'Used with the "--launch" flag to cold boot the emulator instance (Android only).', | |
| negatable: false, | |
| ); | |
| argParser.addFlag( | |
| 'create', | |
| help: 'Creates a new Android emulator based on a Pixel device.', | |
| negatable: false, | |
| ); | |
| argParser.addOption( | |
| 'name', | |
| help: 'Used with the "--create" flag. Specifies a name for the emulator being created.', | |
| ); | |
| } | |
| final Doctor _doctor; | |
| final EmulatorManager _emulatorManager; | |
| @override | |
| ToolContext get toolContext => super.toolContext!; | |
| @override | |
| final name = 'emulators'; | |
| @override | |
| final description = 'List, launch and create emulators.'; | |
| @override | |
| final String category = FlutterCommandCategory.tools; | |
| @override | |
| final aliases = <String>['emulator']; | |
| @override | |
| Future<FlutterCommandResult> runCommand() async { | |
| final Platform platform = toolContext.platform; | |
| if (_doctor.workflows.every((Workflow w) => !w.canListEmulators)) { | |
| throwToolExit( | |
| 'Unable to find any emulator sources. Please ensure you have some\n' | |
| 'Android AVD images ${platform.isMacOS ? 'or an iOS Simulator ' : ''}available.', | |
| exitCode: 1, | |
| ); | |
| } | |
| final ArgResults argumentResults = argResults!; | |
| if (argumentResults.wasParsed('launch')) { | |
| final bool coldBoot = argumentResults.wasParsed('cold'); | |
| await _launchEmulator(stringArg('launch')!, coldBoot: coldBoot); | |
| } else if (argumentResults.wasParsed('create')) { | |
| await _createEmulator(name: stringArg('name')); | |
| } else { | |
| final String? searchText = argumentResults.rest.firstOrNull; | |
| await _listEmulators(searchText); | |
| } | |
| return FlutterCommandResult.success(); | |
| } | |
| Future<void> _launchEmulator(String id, {required bool coldBoot}) async { | |
| final Logger logger = toolContext.logger; | |
| final List<Emulator> emulators = await _emulatorManager.getEmulatorsMatching(id); | |
| if (emulators.isEmpty) { | |
| logger.printStatus("No emulator found that matches '$id'."); | |
| } else if (emulators.length > 1) { | |
| _printEmulatorList(emulators, "More than one emulator matches '$id':"); | |
| } else { | |
| await emulators.first.launch(coldBoot: coldBoot); | |
| } | |
| } | |
| Future<void> _createEmulator({String? name}) async { | |
| final Logger logger = toolContext.logger; | |
| final CreateEmulatorResult createResult = await _emulatorManager.createEmulator(name: name); | |
| if (createResult.success) { | |
| logger.printStatus("Emulator '${createResult.emulatorName}' created successfully."); | |
| } else { | |
| logger.printStatus("Failed to create emulator '${createResult.emulatorName}'.\n"); | |
| final String? error = createResult.error; | |
| if (error != null) { | |
| logger.printStatus(error.trim()); | |
| } | |
| _printAdditionalInfo(); | |
| } | |
| } | |
| Future<void> _listEmulators(String? searchText) async { | |
| final Logger logger = toolContext.logger; | |
| final List<Emulator> emulators = searchText == null | |
| ? await _emulatorManager.getAllAvailableEmulators() | |
| : await _emulatorManager.getEmulatorsMatching(searchText); | |
| if (emulators.isEmpty) { | |
| logger.printStatus('No emulators available.'); | |
| _printAdditionalInfo(showCreateInstruction: true); | |
| } else { | |
| _printEmulatorList( | |
| emulators, | |
| '${emulators.length} available ${pluralize('emulator', emulators.length)}:', | |
| ); | |
| } | |
| } | |
| void _printEmulatorList(List<Emulator> emulators, String message) { | |
| final Logger logger = toolContext.logger; | |
| logger.printStatus('$message\n'); | |
| Emulator.printEmulators(emulators, logger); | |
| _printAdditionalInfo(showCreateInstruction: true, showRunInstruction: true); | |
| } | |
| void _printAdditionalInfo({bool showRunInstruction = false, bool showCreateInstruction = false}) { | |
| final Logger logger = toolContext.logger; | |
| logger.printStatus(''); | |
| if (showRunInstruction) { | |
| logger.printStatus("To run an emulator, run 'flutter emulators --launch <emulator id>'."); | |
| } | |
| if (showCreateInstruction) { | |
| logger.printStatus( | |
| "To create a new emulator, run 'flutter emulators --create [--name xyz]'.", | |
| ); | |
| } | |
| if (showRunInstruction || showCreateInstruction) { | |
| logger.printStatus(''); | |
| } | |
| // TODO(dantup): Update this link to flutter.dev if/when we have a better page. | |
| // That page can then link out to these places if required. | |
| logger.printStatus( | |
| 'You can find more information on managing emulators at the links below:\n' | |
| ' https://developer.android.com/studio/run/managing-avds\n' | |
| ' https://developer.android.com/studio/command-line/avdmanager', | |
| ); | |
| } | |
| } |