diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart index 8bf228289ce067d24eff3a047d5b3493fccff02a..3428caa438f0de0e4bc4da1dcff3080765eddcb2 100644 --- a/packages/flutter_tools/lib/src/commands/create.dart +++ b/packages/flutter_tools/lib/src/commands/create.dart @@ -13,6 +13,7 @@ import '../android/android.dart' as android; import '../artifacts.dart'; import '../base/globals.dart'; import '../base/process.dart'; +import 'ios.dart'; class CreateCommand extends Command { final String name = 'create'; @@ -108,7 +109,7 @@ abstract class Template { final String name; final String description; - Map<String, String> files = {}; + Map<String, String> files = <String, String>{}; Template(this.name, this.description); @@ -119,9 +120,11 @@ abstract class Template { dir.createSync(recursive: true); String relativeFlutterPackagePath = path.relative(flutterPackagePath, from: dirPath); + Iterable<String> paths = files.keys.toList()..sort(); - files.forEach((String filePath, String contents) { - Map m = { + for (String filePath in paths) { + String contents = files[filePath]; + Map m = <String, String>{ 'projectName': projectName, 'description': description, 'flutterPackagePath': relativeFlutterPackagePath @@ -129,10 +132,10 @@ abstract class Template { contents = mustache.render(contents, m); filePath = filePath.replaceAll('/', Platform.pathSeparator); File file = new File(path.join(dir.path, filePath)); - file.parent.createSync(); + file.parent.createSync(recursive: true); file.writeAsStringSync(contents); printStatus(' ${file.path}'); - }); + } } String toString() => name; @@ -145,8 +148,15 @@ class FlutterSimpleTemplate extends Template { files['flutter.yaml'] = _flutterYaml; files['pubspec.yaml'] = _pubspec; files['README.md'] = _readme; - files['android/AndroidManifest.xml'] = _apkManifest; files['lib/main.dart'] = _libMain; + + // Android files. + files['android/AndroidManifest.xml'] = _apkManifest; + // Create a file here, so we create the directory for the user and it gets committed with git. + files['android/res/README.md'] = _androidResReadme; + + // iOS files. + files.addAll(iosTemplateFiles); } } @@ -161,7 +171,7 @@ String _normalizeProjectName(String name) { const String _analysis_options = r''' analyzer: exclude: - - 'ios/build/**' + - 'ios/.generated/**' '''; const String _gitignore = r''' @@ -171,6 +181,7 @@ const String _gitignore = r''' .packages .pub/ build/ +ios/.generated/ packages pubspec.lock '''; @@ -273,3 +284,7 @@ final String _apkManifest = ''' </application> </manifest> '''; + +final String _androidResReadme = ''' +Place Android resources here (http://developer.android.com/guide/topics/resources/overview.html). +'''; diff --git a/packages/flutter_tools/lib/src/commands/ios.dart b/packages/flutter_tools/lib/src/commands/ios.dart index 64e64cd654bb8ff552bb31e1e01e381b5db3fa89..4345dffe827fe03c90b2b4bb32b6949e7c28ad40 100644 --- a/packages/flutter_tools/lib/src/commands/ios.dart +++ b/packages/flutter_tools/lib/src/commands/ios.dart @@ -13,6 +13,13 @@ import "../base/process.dart"; import "../runner/flutter_command.dart"; import "../runner/flutter_command_runner.dart"; +/// A map from file path to file contents. +final Map<String, String> iosTemplateFiles = <String, String>{ + 'ios/Info.plist': _infoPlistInitialContents, + 'ios/LaunchScreen.storyboard': _launchScreenInitialContents, + 'ios/Assets.xcassets/AppIcon.appiconset/Contents.json': _iconAssetInitialContents +}; + class IOSCommand extends FlutterCommand { final String name = "ios"; final String description = "Commands for creating and updating Flutter iOS projects."; @@ -93,46 +100,23 @@ class IOSCommand extends FlutterCommand { return true; } - void _writeUserEditableFilesIfNecessary(String directory) { - printStatus("Checking if user editable files need updates"); - - // Step 1: Check if the Info.plist exists and write one if not - File infoPlist = new File(path.join(directory, "Info.plist")); - if (!infoPlist.existsSync()) { - printStatus("Did not find an existing Info.plist. Creating one."); - infoPlist.writeAsStringSync(_infoPlistInitialContents); - } else { - printStatus("Info.plist present. Using existing."); - } - - // Step 2: Check if the LaunchScreen.storyboard exists and write one if not - File launchScreen = new File(path.join(directory, "LaunchScreen.storyboard")); - if (!launchScreen.existsSync()) { - printStatus("Did not find an existing LaunchScreen.storyboard. Creating one."); - launchScreen.writeAsStringSync(_launchScreenInitialContents); - } else { - printStatus("LaunchScreen.storyboard present. Using existing."); - } + iosTemplateFiles.forEach((String filePath, String contents) { + File file = new File(filePath); - // Step 3: Check if the Assets.xcassets exists and write one if not - Directory xcassets = new Directory(path.join(directory, "Assets.xcassets")); - if (!xcassets.existsSync()) { - printStatus("Did not find an existing Assets.xcassets. Creating one."); - Directory iconsAssetsDir = new Directory(path.join(xcassets.path, "AppIcon.appiconset")); - iconsAssetsDir.createSync(recursive: true); - File iconContents = new File(path.join(iconsAssetsDir.path, "Contents.json")); - iconContents.writeAsStringSync(_iconAssetInitialContents); - } else { - printStatus("Assets.xcassets present. Using existing."); - } + if (!file.existsSync()) { + file.parent.createSync(recursive: true); + file.writeAsStringSync(contents); + printStatus('Created $filePath.'); + } + }); } void _setupXcodeProjXcconfig(String filePath) { StringBuffer localsBuffer = new StringBuffer(); localsBuffer.writeln("// Generated. Do not edit or check into version control!"); - localsBuffer.writeln("// Recreate using `flutter ios`."); + localsBuffer.writeln("// Recreate using `flutter ios --init`."); String flutterRoot = path.normalize(Platform.environment[kFlutterRootEnvironmentVariableName]); localsBuffer.writeln("FLUTTER_ROOT=$flutterRoot"); @@ -167,27 +151,19 @@ class IOSCommand extends FlutterCommand { return 1; } - // Step 3: The generated project should NOT be checked into the users - // version control system. Be nice and write a gitignore for them if - // one does not exist. - File generatedGitignore = new File(path.join(iosFilesPath, ".gitignore")); - if (!generatedGitignore.existsSync()) { - generatedGitignore.writeAsStringSync(".generated/\n"); - } - - // Step 4: Setup default user editable files if this is the first run of + // Step 3: Setup default user editable files if this is the first run of // the init command. _writeUserEditableFilesIfNecessary(iosFilesPath); - // Step 5: Populate the Local.xcconfig with project specific paths + // Step 4: Populate the Local.xcconfig with project specific paths _setupXcodeProjXcconfig(path.join(xcodeprojPath, "Local.xcconfig")); - // Step 6: Write the REVISION file + // Step 5: Write the REVISION file File revisionFile = new File(path.join(xcodeprojPath, "REVISION")); revisionFile.createSync(); revisionFile.writeAsStringSync(ArtifactStore.engineRevision); - // Step 7: Tell the user the location of the generated project. + // Step 6: Tell the user the location of the generated project. printStatus("An Xcode project has been placed in 'ios/'."); printStatus("You may edit it to modify iOS specific configuration."); return 0;