diff --git a/packages/flutter_tools/lib/src/flutter_manifest.dart b/packages/flutter_tools/lib/src/flutter_manifest.dart index 0445550657..0164544236 100644 --- a/packages/flutter_tools/lib/src/flutter_manifest.dart +++ b/packages/flutter_tools/lib/src/flutter_manifest.dart @@ -388,6 +388,8 @@ class FlutterManifest { } return value; } + + String? get defaultFlavor => _flutterDescriptor['default-flavor'] as String?; } class Font { @@ -557,6 +559,10 @@ void _validateFlutter(YamlMap? yaml, List errors) { if (yamlValue is! bool) { errors.add('Expected "$yamlKey" to be a bool, but got $yamlValue (${yamlValue.runtimeType}).'); } + case 'default-flavor': + if (yamlValue is! String) { + errors.add('Expected "$yamlKey" to be a string, but got $yamlValue (${yamlValue.runtimeType}).'); + } default: errors.add('Unexpected child "$yamlKey" found under "flutter".'); break; diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 54671c0605..8a30f61ff1 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -1094,7 +1094,8 @@ abstract class FlutterCommand extends Command { 'flavor', help: 'Build a custom app flavor as defined by platform-specific build setup.\n' 'Supports the use of product flavors in Android Gradle scripts, and ' - 'the use of custom Xcode schemes.', + 'the use of custom Xcode schemes.\n' + 'Overrides the value of the "default-flavor" entry in the flutter pubspec.', ); } @@ -1283,7 +1284,9 @@ abstract class FlutterCommand extends Command { } } - final String? flavor = argParser.options.containsKey('flavor') ? stringArg('flavor') : null; + final String? defaultFlavor = FlutterProject.current().manifest.defaultFlavor; + final String? cliFlavor = argParser.options.containsKey('flavor') ? stringArg('flavor') : null; + final String? flavor = cliFlavor ?? defaultFlavor; if (flavor != null) { if (globals.platform.environment['FLUTTER_APP_FLAVOR'] != null) { throwToolExit('FLUTTER_APP_FLAVOR is used by the framework and cannot be set in the environment.'); diff --git a/packages/flutter_tools/test/general.shard/flutter_manifest_test.dart b/packages/flutter_tools/test/general.shard/flutter_manifest_test.dart index c46d945034..ef1a5ac733 100644 --- a/packages/flutter_tools/test/general.shard/flutter_manifest_test.dart +++ b/packages/flutter_tools/test/general.shard/flutter_manifest_test.dart @@ -36,6 +36,7 @@ void main() { expect(flutterManifest.fonts, isEmpty); expect(flutterManifest.assets, isEmpty); expect(flutterManifest.additionalLicenses, isEmpty); + expect(flutterManifest.defaultFlavor, null); }); testWithoutContext('FlutterManifest is null when the pubspec.yaml file is not a map', () async { @@ -1448,6 +1449,37 @@ flutter: expect(flutterManifest.disabledSwiftPackageManager, false); }); + + testWithoutContext('FlutterManifest can parse default flavor', () async { + const String manifest = ''' +name: test +flutter: + default-flavor: prod +'''; + final FlutterManifest? flutterManifest = FlutterManifest.createFromString( + manifest, + logger: BufferLogger.test(), + ); + + expect(flutterManifest, isNotNull); + expect(flutterManifest!.defaultFlavor, 'prod'); + }); + + testWithoutContext('FlutterManifest fails on invalid default flavor', () async { + const String manifest = ''' +name: test +flutter: + default-flavor: 3 +'''; + + final FlutterManifest? flutterManifest = FlutterManifest.createFromString( + manifest, + logger: logger, + ); + + expect(flutterManifest, null); + expect(logger.errorText, 'Expected "default-flavor" to be a string, but got 3 (int).\n'); + }); } Matcher matchesManifest({ diff --git a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart index 2ba2a9c0d8..c394e915b5 100644 --- a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart +++ b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart @@ -1217,6 +1217,45 @@ void main() { FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), }); + + testUsingContext('CLI option overrides default flavor from manifest', () async { + final File pubspec = fileSystem.file('pubspec.yaml'); + await pubspec.create(); + await pubspec.writeAsString(''' +name: test +flutter: + default-flavor: foo + '''); + + final DummyFlutterCommand flutterCommand = DummyFlutterCommand(); + final BuildInfo buildInfo = await flutterCommand.getBuildInfo(forcedBuildMode: BuildMode.debug); + expect(buildInfo.flavor, 'foo'); + }, overrides: { + FileSystem: () => fileSystem, + ProcessManager: () => FakeProcessManager.empty(), + }); + + testUsingContext('tool loads default flavor from manifest, but cli overrides', () async { + final File pubspec = fileSystem.file('pubspec.yaml'); + await pubspec.create(); + await pubspec.writeAsString(''' +name: test +flutter: + default-flavor: foo + '''); + + final DummyFlutterCommand flutterCommand = DummyFlutterCommand(commandFunction: () async { + return FlutterCommandResult.success(); + },); + flutterCommand.usesFlavorOption(); + final CommandRunner runner = createTestCommandRunner(flutterCommand); + await runner.run(['dummy', '--flavor', 'bar']); + final BuildInfo buildInfo = await flutterCommand.getBuildInfo(forcedBuildMode: BuildMode.debug); + expect(buildInfo.flavor, 'bar'); + }, overrides: { + FileSystem: () => fileSystem, + ProcessManager: () => FakeProcessManager.empty(), + }); }); }); }