add default-flavor field to flutter pubspec, which will be used as the flavor in flutter build/run if --flavor is not provided (#147968)

This PR adds a new flag `default-flavor` in the `flutter` section of `pubspec.yaml`. It allows developers of multi-flavor android apps to specify a default flavor to be used for `flutter run`, `flutter build` etc.
Using `flutter run` on flavored apps already works without specifying `--flavor` already works on iOS (it defaults to the `runner` schema), so I (and others in #22856) figured this would be nice to have.

fixes #22856
This commit is contained in:
holzgeist 2024-05-22 07:11:24 +02:00 committed by GitHub
parent ca198c8585
commit 43548359c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 82 additions and 2 deletions

View File

@ -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<String> 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;

View File

@ -1094,7 +1094,8 @@ abstract class FlutterCommand extends Command<void> {
'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<void> {
}
}
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.');

View File

@ -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({

View File

@ -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: <Type, Generator>{
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<void> runner = createTestCommandRunner(flutterCommand);
await runner.run(<String>['dummy', '--flavor', 'bar']);
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(forcedBuildMode: BuildMode.debug);
expect(buildInfo.flavor, 'bar');
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.empty(),
});
});
});
}