Rename module --> application in flutter create command. (#22565)

This renames the "module" template to the "application" template, and makes "application" the default. The existing "app" template is now deprecated.

flutter create also now recognizes the type of project in an existing directory, and is able to recreate it without having the template type explicitly specified (although you can still do that). It does this now by first looking in the .metadata file for the new project_type field, and if it doesn't find that, then it looks at the directory structure. Also, the .metadata file is now overwritten even on an existing directory so that 1) the project_type can be added to legacy projects, and 2) the version of Flutter that updated the project last is updated.

I also cleaned up a bunch of things in create_test.dart, added many more tests, and added an example test to the test/ directory in the generated output of the application template.

Fixes #22530
Fixes #22344
This commit is contained in:
Greg Spencer 2018-10-04 13:03:20 -07:00 committed by GitHub
parent 66f1c6757b
commit 9f23866a0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
155 changed files with 921 additions and 541 deletions

View File

@ -143,7 +143,7 @@ Future<void> cleanupSystem() async {
final String gradlewBinaryName = Platform.isWindows ? 'gradlew.bat' : 'gradlew';
final Directory tempDir = Directory.systemTemp.createTempSync('flutter_devicelab_shutdown_gradle.');
recursiveCopy(Directory(path.join(flutterDirectory.path, 'bin', 'cache', 'artifacts', 'gradle_wrapper')), tempDir);
copy(File(path.join(path.join(flutterDirectory.path, 'packages', 'flutter_tools'), 'templates', 'create', 'android.tmpl', 'gradle', 'wrapper', 'gradle-wrapper.properties')), Directory(path.join(tempDir.path, 'gradle', 'wrapper')));
copy(File(path.join(path.join(flutterDirectory.path, 'packages', 'flutter_tools'), 'templates', 'app', 'android.tmpl', 'gradle', 'wrapper', 'gradle-wrapper.properties')), Directory(path.join(tempDir.path, 'gradle', 'wrapper')));
if (!Platform.isWindows) {
await exec(
'chmod',

View File

@ -56,7 +56,7 @@ Future<Null> main(List<String> args) async {
ChannelCommand(verboseHelp: verboseHelp),
CleanCommand(),
ConfigCommand(verboseHelp: verboseHelp),
CreateCommand(),
CreateCommand(verboseHelp: verboseHelp),
DaemonCommand(hidden: !verboseHelp),
DevicesCommand(),
DoctorCommand(verbose: verbose),

View File

@ -6,6 +6,7 @@ import 'dart:async';
import 'package:linter/src/rules/pub/package_names.dart' as package_names; // ignore: implementation_imports
import 'package:linter/src/utils.dart' as linter_utils; // ignore: implementation_imports
import 'package:yaml/yaml.dart' as yaml;
import '../android/android.dart' as android;
import '../android/android_sdk.dart' as android_sdk;
@ -23,8 +24,33 @@ import '../runner/flutter_command.dart';
import '../template.dart';
import '../version.dart';
enum ProjectType {
app,
application,
module, // TODO(gspencer): deprecated -- should be removed once IntelliJ no longer uses it.
package,
plugin,
}
ProjectType _stringToProjectType(String value) {
ProjectType result;
// TODO(gspencer): remove module when it is no longer used by IntelliJ plugin.
// Module is just an alias for application.
if (value == 'module') {
value = 'application';
}
for (ProjectType type in ProjectType.values) {
if (value == getEnumName(type)) {
result = type;
break;
}
}
assert(result != null, 'Unsupported template type $value requested.');
return result;
}
class CreateCommand extends FlutterCommand {
CreateCommand() {
CreateCommand({bool verboseHelp = false }) {
argParser.addFlag('pub',
defaultsTo: true,
help: 'Whether to run "flutter packages get" after the project has been created.'
@ -39,21 +65,33 @@ class CreateCommand extends FlutterCommand {
'with-driver-test',
negatable: true,
defaultsTo: false,
help: 'Also add a flutter_driver dependency and generate a sample \'flutter drive\' test.'
help: "Also add a flutter_driver dependency and generate a sample 'flutter drive' test."
);
argParser.addOption(
'template',
abbr: 't',
allowed: <String>['app', 'module', 'package', 'plugin'],
allowed: ProjectType.values.map<String>((ProjectType type) => getEnumName(type)),
help: 'Specify the type of project to create.',
valueHelp: 'type',
allowedHelp: <String, String>{
'app': '(default) Generate a Flutter application.',
'package': 'Generate a shareable Flutter project containing modular Dart code.',
'plugin': 'Generate a shareable Flutter project containing an API in Dart code\n'
'with a platform-specific implementation for Android, for iOS code, or for both.',
},
defaultsTo: 'app',
getEnumName(ProjectType.application): '(default) Generate a Flutter application.',
getEnumName(ProjectType.package): 'Generate a shareable Flutter project containing modular '
'Dart code.',
getEnumName(ProjectType.plugin): 'Generate a shareable Flutter project containing an API '
'in Dart code with a platform-specific implementation for Android, for iOS code, or '
'for both.',
}..addAll(verboseHelp
? <String, String>{
getEnumName(ProjectType.app): 'Generate the legacy form of an application project. Use '
'"application" instead, unless you are working with an existing legacy app project. '
'This is not just an alias for the "application" template, it produces different '
'output.',
getEnumName(ProjectType.module): 'Legacy, deprecated form of an application project. Use '
'"application" instead. This is just an alias for the "application" template, it '
'produces the same output. It will be removed in a future release.',
}
: <String, String>{}),
defaultsTo: null,
);
argParser.addOption(
'description',
@ -90,6 +128,48 @@ class CreateCommand extends FlutterCommand {
@override
String get invocation => '${runner.executableName} $name <output directory>';
// If it has a .metadata file with the project_type in it, use that.
// If it has an android dir and an android/app dir, it's a legacy app
// If it has an android dir and an android/src dir, it's a plugin
// If it has .ios and/or .android dirs, it's an application (nee module)
// If it has an ios dir and an ios/Classes dir, it's a plugin
// If it has no ios dir, no android dir, and no .ios or .android, then it's a package.
ProjectType _determineTemplateType(Directory projectDir) {
yaml.YamlMap loadMetadata(Directory projectDir) {
if (!projectDir.existsSync())
return null;
final File metadataFile =fs.file(fs.path.join(projectDir.absolute.path, '.metadata'));
if (!metadataFile.existsSync())
return null;
return yaml.loadYaml(metadataFile.readAsStringSync());
}
// If it exists, the project type in the metadata is definitive.
final yaml.YamlMap metadata = loadMetadata(projectDir);
if (metadata != null && metadata['project_type'] != null) {
return _stringToProjectType(metadata['project_type']);
}
// There either wasn't any metadata, or it didn't contain the project type,
// so try and figure out what type of project it is from the existing
// directory structure.
if (fs.directory(fs.path.join(projectDir.absolute.path, 'android', 'app')).existsSync())
return ProjectType.app;
final bool dotPlatformDirExists = fs.directory(fs.path.join(projectDir.absolute.path, '.ios')).existsSync() ||
fs.directory(fs.path.join(projectDir.absolute.path, '.android')).existsSync();
final bool platformDirExists = fs.directory(fs.path.join(projectDir.absolute.path, 'ios')).existsSync() ||
fs.directory(fs.path.join(projectDir.absolute.path, 'android')).existsSync();
if (dotPlatformDirExists)
return ProjectType.application;
if (!platformDirExists && !dotPlatformDirExists)
return ProjectType.package;
if (platformDirExists &&
(fs.directory(fs.path.join(projectDir.absolute.path, 'ios', 'Classes')).existsSync() ||
fs.directory(fs.path.join(projectDir.absolute.path, 'android', 'src')).existsSync()))
return ProjectType.plugin;
return null;
}
@override
Future<Null> runCommand() async {
if (argResults.rest.isEmpty)
@ -123,13 +203,33 @@ class CreateCommand extends FlutterCommand {
if (!fs.isFileSync(fs.path.join(flutterDriverPackagePath, 'pubspec.yaml')))
throwToolExit('Unable to find package:flutter_driver in $flutterDriverPackagePath', exitCode: 2);
final String template = argResults['template'];
final bool generateModule = template == 'module';
final bool generatePlugin = template == 'plugin';
final bool generatePackage = template == 'package';
final Directory projectDir = fs.directory(argResults.rest.first);
final String dirPath = fs.path.normalize(projectDir.absolute.path);
ProjectType detectedProjectType;
if (projectDir.existsSync()) {
detectedProjectType = _determineTemplateType(projectDir);
if (detectedProjectType == null) {
throwToolExit('Sorry, unable to detect the type of project to recreate. '
'Try creating a fresh project and migrating your existing code to '
'the new project manually.');
}
}
ProjectType template;
if (argResults['template'] != null) {
template = _stringToProjectType(argResults['template']);
}
template ??= detectedProjectType ?? ProjectType.application;
if (detectedProjectType != null && template != detectedProjectType) {
throwToolExit("The requested template type '${getEnumName(template)}' doesn't match the "
"existing template type of '${getEnumName(detectedProjectType)}'.");
}
final bool generateApplication = template == ProjectType.application;
final bool generatePlugin = template == ProjectType.plugin;
final bool generatePackage = template == ProjectType.package;
String organization = argResults['org'];
if (!argResults.wasParsed('org')) {
final FlutterProject project = await FlutterProject.fromDirectory(projectDir);
@ -164,54 +264,58 @@ class CreateCommand extends FlutterCommand {
iosLanguage: argResults['ios-language'],
);
printStatus('Creating project ${fs.path.relative(dirPath)}...');
final String relativeDirPath = fs.path.relative(dirPath);
printStatus('Creating project $relativeDirPath...');
final Directory directory = fs.directory(dirPath);
int generatedFileCount = 0;
switch (template) {
case 'app':
generatedFileCount += await _generateApp(directory, templateContext);
case ProjectType.app:
generatedFileCount += await _generateLegacyApp(directory, templateContext);
break;
case 'module':
generatedFileCount += await _generateModule(directory, templateContext);
case ProjectType.module:
case ProjectType.application:
generatedFileCount += await _generateApplication(directory, templateContext);
break;
case 'package':
case ProjectType.package:
generatedFileCount += await _generatePackage(directory, templateContext);
break;
case 'plugin':
case ProjectType.plugin:
generatedFileCount += await _generatePlugin(directory, templateContext);
break;
}
printStatus('Wrote $generatedFileCount files.');
printStatus('');
printStatus('\nAll done!');
if (generatePackage) {
final String relativePath = fs.path.relative(dirPath);
printStatus('Your package code is in lib/${templateContext['projectName']}.dart in the $relativePath directory.');
} else if (generateModule) {
final String relativePath = fs.path.relative(dirPath);
printStatus('Your module code is in lib/main.dart in the $relativePath directory.');
final String relativeMainPath = fs.path.normalize(fs.path.join(relativeDirPath, 'lib', '${templateContext['projectName']}.dart'));
printStatus('Your package code is in $relativeMainPath');
} else if (generateApplication) {
final String relativeMainPath = fs.path.normalize(fs.path.join(relativeDirPath, 'lib', 'main.dart'));
printStatus('Your application code is in $relativeMainPath.');
} else {
// Run doctor; tell the user the next steps.
final FlutterProject project = await FlutterProject.fromPath(dirPath);
final FlutterProject app = project.hasExampleApp ? project.example : project;
final String relativeAppPath = fs.path.relative(app.directory.path);
final String relativePluginPath = fs.path.relative(dirPath);
final String relativeAppPath = fs.path.normalize(fs.path.relative(app.directory.path));
final String relativeAppMain = fs.path.join(relativeAppPath, 'lib', 'main.dart');
final String relativePluginPath = fs.path.normalize(fs.path.relative(dirPath));
final String relativePluginMain = fs.path.join(relativePluginPath, 'lib', '$projectName.dart');
if (doctor.canLaunchAnything) {
// Let them know a summary of the state of their tooling.
await doctor.summary();
printStatus('''
All done! In order to run your application, type:
In order to run your application, type:
\$ cd $relativeAppPath
\$ flutter run
Your main program file is lib/main.dart in the $relativeAppPath directory.
Your application code is in $relativeAppMain.
''');
if (generatePlugin) {
printStatus('''
Your plugin code is in lib/$projectName.dart in the $relativePluginPath directory.
Your plugin code is in $relativePluginMain.
Host platform code is in the android/ and ios/ directories under $relativePluginPath.
Host platform code is in the "android" and "ios" directories under $relativePluginPath.
To edit platform code in an IDE see https://flutter.io/developing-packages/#edit-plugin-package.
''');
}
@ -227,18 +331,18 @@ To edit platform code in an IDE see https://flutter.io/developing-packages/#edit
're-validate your setup.');
printStatus("When complete, type 'flutter run' from the '$relativeAppPath' "
'directory in order to launch your app.');
printStatus('Your main program file is: $relativeAppPath/lib/main.dart');
printStatus('Your application code is in $relativeAppMain');
}
}
}
Future<int> _generateModule(Directory directory, Map<String, dynamic> templateContext) async {
Future<int> _generateApplication(Directory directory, Map<String, dynamic> templateContext) async {
int generatedCount = 0;
final String description = argResults.wasParsed('description')
? argResults['description']
: 'A new flutter module project.';
: 'A new flutter application project.';
templateContext['description'] = description;
generatedCount += _renderTemplate(fs.path.join('module', 'common'), directory, templateContext);
generatedCount += _renderTemplate(fs.path.join('application', 'common'), directory, templateContext);
if (argResults['pub']) {
await pubGet(
context: PubContext.create,
@ -296,13 +400,13 @@ To edit platform code in an IDE see https://flutter.io/developing-packages/#edit
templateContext['pluginProjectName'] = projectName;
templateContext['androidPluginIdentifier'] = androidPluginIdentifier;
generatedCount += await _generateApp(project.example.directory, templateContext);
generatedCount += await _generateLegacyApp(project.example.directory, templateContext);
return generatedCount;
}
Future<int> _generateApp(Directory directory, Map<String, dynamic> templateContext) async {
Future<int> _generateLegacyApp(Directory directory, Map<String, dynamic> templateContext) async {
int generatedCount = 0;
generatedCount += _renderTemplate('create', directory, templateContext);
generatedCount += _renderTemplate('app', directory, templateContext);
final FlutterProject project = await FlutterProject.fromDirectory(directory);
generatedCount += _injectGradleWrapper(project);

View File

@ -44,8 +44,8 @@ abstract class MakeHostAppEditableSubCommand extends FlutterCommand {
Future<Null> validateCommand() async {
await super.validateCommand();
_project = await FlutterProject.current();
if (!_project.isModule)
throw ToolExit("Only projects created using 'flutter create -t module' can have their host apps made editable.");
if (!_project.isApplication)
throw ToolExit("Only projects created using 'flutter create -t application' can have their host apps made editable.");
}
}

View File

@ -110,14 +110,14 @@ class FlutterManifest {
return _flutterDescriptor['uses-material-design'] ?? false;
}
/// True if this manifest declares a Flutter module project.
/// True if this manifest declares a Flutter application project.
///
/// A Flutter project is considered a module when it has a `module:`
/// descriptor. A Flutter module project supports integration into an
/// A Flutter project is considered an application when it has a `application:`
/// descriptor. A Flutter application project supports integration into an
/// existing host app.
///
/// Such a project can be created using `flutter create -t module`.
bool get isModule => _flutterDescriptor.containsKey('module');
/// Such a project can be created using `flutter create -t application`.
bool get isApplication => _flutterDescriptor.containsKey('application');
/// True if this manifest declares a Flutter plugin project.
///
@ -130,21 +130,21 @@ class FlutterManifest {
bool get isPlugin => _flutterDescriptor.containsKey('plugin');
/// Returns the Android package declared by this manifest in its
/// module or plugin descriptor. Returns null, if there is no
/// application or plugin descriptor. Returns null, if there is no
/// such declaration.
String get androidPackage {
if (isModule)
return _flutterDescriptor['module']['androidPackage'];
if (isApplication)
return _flutterDescriptor['application']['androidPackage'];
if (isPlugin)
return _flutterDescriptor['plugin']['androidPackage'];
return null;
}
/// Returns the iOS bundle identifier declared by this manifest in its
/// module descriptor. Returns null, if there is no such declaration.
/// application descriptor. Returns null, if there is no such declaration.
String get iosBundleIdentifier {
if (isModule)
return _flutterDescriptor['module']['iosBundleIdentifier'];
if (isApplication)
return _flutterDescriptor['application']['iosBundleIdentifier'];
return null;
}

View File

@ -335,7 +335,7 @@ Future<XcodeBuildResult> buildXcodeProject({
buildInfo: buildInfo,
);
refreshPluginsList(project);
if (hasPlugins(project) || (project.isModule && project.ios.podfile.existsSync())) {
if (hasPlugins(project) || (project.isApplication && project.ios.podfile.existsSync())) {
// If the Xcode project, Podfile, or Generated.xcconfig have changed since
// last run, pods should be updated.
final Fingerprinter fingerprinter = Fingerprinter(

View File

@ -57,8 +57,8 @@ Future<void> updateGeneratedXcodeProperties({
localsBuffer.writeln('SYMROOT=\${SOURCE_ROOT}/../${getIosBuildDirectory()}');
if (!project.isModule) {
// For module projects we do not want to write the FLUTTER_FRAMEWORK_DIR
if (!project.isApplication) {
// For application projects we do not want to write the FLUTTER_FRAMEWORK_DIR
// explicitly. Rather we rely on the xcode backend script and the Podfile
// logic to derive it from FLUTTER_ROOT and FLUTTER_BUILD_MODE.
localsBuffer.writeln('FLUTTER_FRAMEWORK_DIR=${flutterFrameworkDir(buildInfo.mode)}');

View File

@ -248,7 +248,7 @@ Future<void> _writeIOSPluginRegistrant(FlutterProject project, List<Plugin> plug
};
final String registryDirectory = project.ios.pluginRegistrantHost.path;
if (project.isModule) {
if (project.isApplication) {
final String registryClassesDirectory = fs.path.join(registryDirectory, 'Classes');
_renderTemplateToFile(
_iosPluginRegistrantPodspecTemplate,
@ -297,7 +297,7 @@ Future<void> injectPlugins(FlutterProject project) async {
final List<Plugin> plugins = findPlugins(project);
await _writeAndroidPluginRegistrant(project, plugins);
await _writeIOSPluginRegistrant(project, plugins);
if (!project.isModule && project.ios.hostAppRoot.existsSync()) {
if (!project.isApplication && project.ios.hostAppRoot.existsSync()) {
final CocoaPods cocoaPods = CocoaPods();
if (plugins.isNotEmpty)
cocoaPods.setupPodfile(project.ios);

View File

@ -110,8 +110,8 @@ class FlutterProject {
FlutterManifest.empty(),
);
/// True, if this project is a Flutter module.
bool get isModule => manifest.isModule;
/// True, if this project is a Flutter application.
bool get isApplication => manifest.isApplication;
/// True, if this project has an example application.
bool get hasExampleApp => _exampleDirectory(directory).existsSync();
@ -132,7 +132,7 @@ class FlutterProject {
}
/// Generates project files necessary to make Gradle builds work on Android
/// and CocoaPods+Xcode work on iOS, for app and module projects only.
/// and CocoaPods+Xcode work on iOS, for app and application projects only.
Future<void> ensureReadyForPlatformSpecificTooling() async {
if (!directory.existsSync() || hasExampleApp)
return;
@ -146,7 +146,7 @@ class FlutterProject {
/// Represents the iOS sub-project of a Flutter project.
///
/// Instances will reflect the contents of the `ios/` sub-folder of
/// Flutter applications and the `.ios/` sub-folder of Flutter modules.
/// Flutter applications and the `.ios/` sub-folder of Flutter applications.
class IosProject {
IosProject._(this.parent);
@ -162,7 +162,7 @@ class IosProject {
/// This parent folder of `Runner.xcodeproj`.
Directory get hostAppRoot {
if (!isModule || _editableDirectory.existsSync())
if (!isApplication || _editableDirectory.existsSync())
return _editableDirectory;
return _ephemeralDirectory;
}
@ -172,14 +172,14 @@ class IosProject {
/// during build.
///
/// This is the same as [hostAppRoot] except when the project is
/// a Flutter module with an editable host app.
Directory get _flutterLibRoot => isModule ? _ephemeralDirectory : _editableDirectory;
/// a Flutter application with an editable host app.
Directory get _flutterLibRoot => isApplication ? _ephemeralDirectory : _editableDirectory;
/// The bundle name of the host app, `Runner.app`.
String get hostAppBundleName => '$_hostAppBundleName.app';
/// True, if the parent Flutter project is a module.
bool get isModule => parent.isModule;
/// True, if the parent Flutter project is an application.
bool get isApplication => parent.isApplication;
/// The xcode config file for [mode].
File xcodeConfigFor(String mode) => _flutterLibRoot.childDirectory('Flutter').childFile('$mode.xcconfig');
@ -264,32 +264,32 @@ class IosProject {
}
void _regenerateFromTemplateIfNeeded() {
if (!isModule)
if (!isApplication)
return;
final bool pubspecChanged = isOlderThanReference(entity: _ephemeralDirectory, referenceFile: parent.pubspecFile);
final bool toolingChanged = Cache.instance.isOlderThanToolsStamp(_ephemeralDirectory);
if (!pubspecChanged && !toolingChanged)
return;
_deleteIfExistsSync(_ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'library'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'ios', 'library'), _ephemeralDirectory);
// Add ephemeral host app, if a editable host app does not already exist.
if (!_editableDirectory.existsSync()) {
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'ios', 'host_app_ephemeral'), _ephemeralDirectory);
if (hasPlugins(parent)) {
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'ios', 'host_app_ephemeral_cocoapods'), _ephemeralDirectory);
}
}
}
Future<void> makeHostAppEditable() async {
assert(isModule);
assert(isApplication);
if (_editableDirectory.existsSync())
throwToolExit('iOS host app is already editable. To start fresh, delete the ios/ folder.');
_deleteIfExistsSync(_ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'library'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral'), _editableDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'), _editableDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_editable_cocoapods'), _editableDirectory);
_overwriteFromTemplate(fs.path.join('application', 'ios', 'library'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'ios', 'host_app_ephemeral'), _editableDirectory);
_overwriteFromTemplate(fs.path.join('application', 'ios', 'host_app_ephemeral_cocoapods'), _editableDirectory);
_overwriteFromTemplate(fs.path.join('application', 'ios', 'host_app_editable_cocoapods'), _editableDirectory);
await _updateGeneratedXcodeConfigIfNeeded();
await injectPlugins(parent);
}
@ -297,7 +297,7 @@ class IosProject {
File get generatedXcodePropertiesFile => _flutterLibRoot.childDirectory('Flutter').childFile('Generated.xcconfig');
Directory get pluginRegistrantHost {
return isModule
return isApplication
? _flutterLibRoot.childDirectory('Flutter').childDirectory('FlutterPluginRegistrant')
: hostAppRoot.childDirectory(_hostAppBundleName);
}
@ -319,7 +319,7 @@ class IosProject {
/// Represents the Android sub-project of a Flutter project.
///
/// Instances will reflect the contents of the `android/` sub-folder of
/// Flutter applications and the `.android/` sub-folder of Flutter modules.
/// Flutter applications and the `.android/` sub-folder of Flutter applications.
class AndroidProject {
AndroidProject._(this.parent);
@ -333,21 +333,21 @@ class AndroidProject {
/// containing the `app/` subdirectory and the `settings.gradle` file that
/// includes it in the overall Gradle project.
Directory get hostAppGradleRoot {
if (!isModule || _editableHostAppDirectory.existsSync())
if (!isApplication || _editableHostAppDirectory.existsSync())
return _editableHostAppDirectory;
return _ephemeralDirectory;
}
/// The Gradle root directory of the Android wrapping of Flutter and plugins.
/// This is the same as [hostAppGradleRoot] except when the project is
/// a Flutter module with an editable host app.
Directory get _flutterLibGradleRoot => isModule ? _ephemeralDirectory : _editableHostAppDirectory;
/// a Flutter application with an editable host app.
Directory get _flutterLibGradleRoot => isApplication ? _ephemeralDirectory : _editableHostAppDirectory;
Directory get _ephemeralDirectory => parent.directory.childDirectory('.android');
Directory get _editableHostAppDirectory => parent.directory.childDirectory('android');
/// True, if the parent Flutter project is a module.
bool get isModule => parent.isModule;
/// True, if the parent Flutter project is an application.
bool get isApplication => parent.isApplication;
File get appManifestFile {
return isUsingGradle
@ -376,12 +376,12 @@ class AndroidProject {
}
Future<void> ensureReadyForPlatformSpecificTooling() async {
if (isModule && _shouldRegenerateFromTemplate()) {
if (isApplication && _shouldRegenerateFromTemplate()) {
_regenerateLibrary();
// Add ephemeral host app, if an editable host app does not already exist.
if (!_editableHostAppDirectory.existsSync()) {
_overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_common'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_ephemeral'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'android', 'host_app_common'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'android', 'host_app_ephemeral'), _ephemeralDirectory);
}
}
if (!hostAppGradleRoot.existsSync()) {
@ -396,13 +396,13 @@ class AndroidProject {
}
Future<void> makeHostAppEditable() async {
assert(isModule);
assert(isApplication);
if (_editableHostAppDirectory.existsSync())
throwToolExit('Android host app is already editable. To start fresh, delete the android/ folder.');
_regenerateLibrary();
_overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_common'), _editableHostAppDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_editable'), _editableHostAppDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), _editableHostAppDirectory);
_overwriteFromTemplate(fs.path.join('application', 'android', 'host_app_common'), _editableHostAppDirectory);
_overwriteFromTemplate(fs.path.join('application', 'android', 'host_app_editable'), _editableHostAppDirectory);
_overwriteFromTemplate(fs.path.join('application', 'android', 'gradle'), _editableHostAppDirectory);
gradle.injectGradleWrapper(_editableHostAppDirectory);
gradle.writeLocalProperties(_editableHostAppDirectory.childFile('local.properties'));
await injectPlugins(parent);
@ -410,12 +410,12 @@ class AndroidProject {
File get localPropertiesFile => _flutterLibGradleRoot.childFile('local.properties');
Directory get pluginRegistrantHost => _flutterLibGradleRoot.childDirectory(isModule ? 'Flutter' : 'app');
Directory get pluginRegistrantHost => _flutterLibGradleRoot.childDirectory(isApplication ? 'Flutter' : 'app');
void _regenerateLibrary() {
_deleteIfExistsSync(_ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'library'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'android', 'library'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'android', 'gradle'), _ephemeralDirectory);
gradle.injectGradleWrapper(_ephemeralDirectory);
}

View File

@ -43,7 +43,7 @@
}
}
},
"module": {
"application": {
"type": "object",
"additionalProperties": false,
"properties": {

View File

@ -6,3 +6,5 @@
version:
revision: {{flutterRevision}}
channel: {{flutterChannel}}
project_type: app

View File

@ -1,8 +1,9 @@
// This is a basic Flutter widget test.
// To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter
// provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to
// find child widgets in the widget tree, read text, and verify that the values of widget properties
// are correct.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

View File

@ -2,7 +2,7 @@
## common
Written to root of Flutter module.
Written to root of Flutter application.
Adds Dart project files including `pubspec.yaml`.

View File

@ -19,9 +19,8 @@ import io.flutter.plugins.GeneratedPluginRegistrant;
/**
* Main entry point for using Flutter in Android applications.
*
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling. Do not edit.
* It may be moved into flutter.jar or another library dependency of the Flutter module project
* at a later point.</p>
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling.
* DO NOT EDIT.</p>
*/
public final class Flutter {
private Flutter() {

View File

@ -13,9 +13,8 @@ import io.flutter.view.FlutterView;
/**
* A {@link Fragment} managing a {@link FlutterView}.
*
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling. Do not edit.
* It may be moved into flutter.jar or another library dependency of the Flutter module project
* at a later point.</p>
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling.
* DO NOT EDIT.</p>
*/
public class FlutterFragment extends Fragment {
public static final String ARG_ROUTE = "route";

View File

@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: {{flutterRevision}}
channel: {{flutterChannel}}
project_type: application

Some files were not shown because too many files have changed in this diff Show More