diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart index f746de5efd3..ba97e3f44ac 100644 --- a/packages/flutter_tools/lib/src/commands/create.dart +++ b/packages/flutter_tools/lib/src/commands/create.dart @@ -6,14 +6,12 @@ import 'dart:async'; import 'dart:io'; import 'package:args/command_runner.dart'; -import 'package:mustache4dart/mustache4dart.dart' as mustache; import 'package:path/path.dart' as path; -import '../android/android.dart' as android; import '../artifacts.dart'; import '../dart/pub.dart'; import '../globals.dart'; -import '../ios/setup_xcodeproj.dart'; +import '../template.dart'; class CreateCommand extends Command { final String name = 'create'; @@ -69,35 +67,28 @@ class CreateCommand extends Command { return 2; } - Directory out; + Directory projectDir; if (argResults.wasParsed('out')) { - out = new Directory(argResults['out']); + projectDir = new Directory(argResults['out']); } else { - out = new Directory(argResults.rest.first); + projectDir = new Directory(argResults.rest.first); } - FlutterSimpleTemplate template = new FlutterSimpleTemplate(); - - if (argResults['with-driver-test']) - template.withDriverTest(); - - template.generateInto( - dir: out, - flutterPackagesDirectory: flutterPackagesDirectory - ); + _renderTemplates(projectDir, flutterPackagesDirectory, + renderDriverTest: argResults['with-driver-test']); printStatus(''); String message = ''' All done! To run your application: - \$ cd ${out.path} + \$ cd ${projectDir.path} \$ flutter run '''; if (argResults['pub']) { - int code = await pubGet(directory: out.path); + int code = await pubGet(directory: projectDir.path); if (code != 0) return code; } @@ -106,74 +97,38 @@ All done! To run your application: printStatus(message); return 0; } -} -abstract class Template { - final String name; - final String description; - final Map files = {}; - final Map additionalTemplateVariables = {}; - - Template(this.name, this.description); - - void generateInto({ - Directory dir, - String flutterPackagesDirectory - }) { - String dirPath = path.normalize(dir.absolute.path); + void _renderTemplates(Directory projectDir, String flutterPackagesDirectory, + { bool renderDriverTest: false }) { + String dirPath = path.normalize(projectDir.absolute.path); String projectName = _normalizeProjectName(path.basename(dirPath)); String projectIdentifier = _createProjectIdentifier(path.basename(dirPath)); + String relativeFlutterPackagesDirectory = path.relative(flutterPackagesDirectory, from: dirPath); + printStatus('Creating ${path.basename(projectName)}...'); - dir.createSync(recursive: true); - String relativeFlutterPackagesDirectory = - path.relative(flutterPackagesDirectory, from: dirPath); - Iterable paths = files.keys.toList()..sort(); + projectDir.createSync(recursive: true); - for (String filePath in paths) { - String contents = files[filePath]; - Map m = { - 'projectName': projectName, - 'projectIdentifier': projectIdentifier, - 'description': description, - 'flutterPackagesDirectory': relativeFlutterPackagesDirectory, - }; - m.addAll(additionalTemplateVariables); - contents = mustache.render(contents, m); - filePath = filePath.replaceAll('/', Platform.pathSeparator); - File file = new File(path.join(dir.path, filePath)); - file.parent.createSync(recursive: true); - file.writeAsStringSync(contents); - printStatus(' ${file.path}'); + Map templateContext = { + 'projectName': projectName, + 'projectIdentifier': projectIdentifier, + 'description': description, + 'flutterPackagesDirectory': relativeFlutterPackagesDirectory, + }; + + if (renderDriverTest) + templateContext['withDriverTest?'] = {}; + + Template createTemplate = new Template.fromName('create'); + createTemplate.render(new Directory(dirPath), templateContext, + overwriteExisting: false); + + if (renderDriverTest) { + Template driverTemplate = new Template.fromName('driver'); + driverTemplate.render(new Directory(path.join(dirPath, 'test_driver')), + templateContext, overwriteExisting: false); } } - - String toString() => name; -} - -class FlutterSimpleTemplate extends Template { - FlutterSimpleTemplate() : super('flutter-simple', 'A simple Flutter app.') { - files['.analysis_options'] = _analysis_options; - files['.gitignore'] = _gitignore; - files['flutter.yaml'] = _flutterYaml; - files['pubspec.yaml'] = _pubspec; - files['README.md'] = _readme; - files['lib/main.dart'] = _libMain; - - // Android files. - files['android/AndroidManifest.xml'] = _apkManifest; - // Create a file here in order to create the res/ directory and ensure it gets committed to git. - files['android/res/.empty'] = _androidEmptyFile; - - // iOS files. - files.addAll(iosTemplateFiles); - } - - void withDriverTest() { - additionalTemplateVariables['withDriverTest?'] = {}; - files['test_driver/e2e.dart'] = _e2eApp; - files['test_driver/e2e_test.dart'] = _e2eTest; - } } String _normalizeProjectName(String name) { @@ -191,192 +146,3 @@ String _createProjectIdentifier(String name) { name = name.length == 0 ? 'untitled' : name; return 'com.yourcompany.$name'; } - -const String _analysis_options = r''' -analyzer: - exclude: - - 'ios/.generated/**' -'''; - -const String _gitignore = r''' -.DS_Store -.atom/ -.idea -.packages -.pub/ -build/ -ios/.generated/ -packages -pubspec.lock -'''; - -const String _readme = r''' -# {{projectName}} - -{{description}} - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](http://flutter.io/). -'''; - -const String _pubspec = r''' -name: {{projectName}} -description: {{description}} -dependencies: - flutter: - path: {{flutterPackagesDirectory}}/flutter -{{#withDriverTest?}} -dev_dependencies: - flutter_driver: - path: {{flutterPackagesDirectory}}/flutter_driver -{{/withDriverTest?}} -'''; - -const String _flutterYaml = r''' -name: {{projectName}} -material-design-icons: - - name: content/add - - name: navigation/arrow_back - - name: navigation/menu - - name: navigation/more_vert -'''; - -const String _libMain = r''' -import 'package:flutter/material.dart'; - -void main() { - runApp( - new MaterialApp( - title: 'Flutter Demo', - routes: { - '/': (RouteArguments args) => new FlutterDemo() - } - ) - ); -} - -class FlutterDemo extends StatefulComponent { - State createState() => new _FlutterDemoState(); -} - -class _FlutterDemoState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - _counter++; - }); - } - - Widget build(BuildContext context) { - return new Scaffold( - toolBar: new ToolBar( - center: new Text('Flutter Demo') - ), - body: new Center( - child: new Text( - 'Button tapped $_counter times.', - key: const ValueKey('counter') - ) - ), - floatingActionButton: new FloatingActionButton( - key: const ValueKey('fab'), - child: new Icon( - icon: 'content/add' - ), - onPressed: _incrementCounter - ) - ); - } -} -'''; - -const String _e2eApp = ''' -// Starts the app with Flutter Driver extension enabled to allow Flutter Driver -// to test the app. -import 'package:{{projectName}}/main.dart' as app; -import 'package:flutter_driver/driver_extension.dart'; - -main() { - enableFlutterDriverExtension(); - app.main(); -} -'''; - -const String _e2eTest = ''' -// This is a basic Flutter Driver test for the application. A Flutter Driver -// test is an end-to-end test that "drives" your application from another -// process or even from another computer. If you are familiar with -// Selenium/WebDriver for web, Espresso for Android or UI Automation for iOS, -// this is simply Flutter's version of that. -// -// To start the test run the following command from the root of your application -// package: -// -// flutter drive --target=test_driver/e2e.dart -// -import 'package:flutter_driver/flutter_driver.dart'; -import 'package:test/test.dart'; - -main() { - group('end-to-end test', () { - FlutterDriver driver; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await FlutterDriver.connect(); - }); - - tearDownAll(() async { - if (driver != null) driver.close(); - }); - - test('find the floating action button by value key', () async { - ObjectRef elem = await driver.findByValueKey('fab'); - expect(elem, isNotNull); - expect(elem.objectReferenceKey, isNotNull); - }); - - test('tap on the floating action button; verify counter', () async { - ObjectRef fab = await driver.findByValueKey('fab'); - expect(fab, isNotNull); - await driver.tap(fab); - ObjectRef counter = await driver.findByValueKey('counter'); - expect(counter, isNotNull); - String text = await driver.getText(counter); - expect(text, contains("Button tapped 1 times.")); - }); - }); -} -'''; - -final String _apkManifest = ''' - - - - - - - - - - - - - - -'''; - -final String _androidEmptyFile = ''' -Place Android resources here (http://developer.android.com/guide/topics/resources/overview.html). -'''; diff --git a/packages/flutter_tools/lib/src/ios/setup_xcodeproj.dart b/packages/flutter_tools/lib/src/ios/setup_xcodeproj.dart index f91ab4ee815..ced4c0c19bd 100644 --- a/packages/flutter_tools/lib/src/ios/setup_xcodeproj.dart +++ b/packages/flutter_tools/lib/src/ios/setup_xcodeproj.dart @@ -12,13 +12,6 @@ import '../base/process.dart'; import '../globals.dart'; import '../runner/flutter_command_runner.dart'; -/// A map from file path to file contents. -final Map iosTemplateFiles = { - 'ios/Info.plist': _infoPlistInitialContents, - 'ios/LaunchScreen.storyboard': _launchScreenInitialContents, - 'ios/Assets.xcassets/AppIcon.appiconset/Contents.json': _iconAssetInitialContents -}; - Uri _xcodeProjectUri(String revision) { String uriString = 'https://storage.googleapis.com/flutter_infra/flutter/$revision/ios/FlutterXcode.zip'; return Uri.parse(uriString); @@ -162,159 +155,3 @@ Future setupXcodeProjectHarness(String flutterProjectPath) async { return 0; } - -final String _infoPlistInitialContents = ''' - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - Runner - CFBundleIdentifier - {{projectIdentifier}} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - {{projectName}} - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - arm64 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - -'''; - -final String _launchScreenInitialContents = ''' - - - - - - - - - - - - - - - - - - - - - - - - - - - -'''; - -final String _iconAssetInitialContents = ''' -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "83.5x83.5", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} -'''; diff --git a/packages/flutter_tools/lib/src/template.dart b/packages/flutter_tools/lib/src/template.dart new file mode 100644 index 00000000000..ae40612d6eb --- /dev/null +++ b/packages/flutter_tools/lib/src/template.dart @@ -0,0 +1,132 @@ +// Copyright 2016 The Chromium 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 'dart:io'; + +import 'package:path/path.dart' as path; +import 'package:mustache4dart/mustache4dart.dart' as mustache; + +import 'artifacts.dart'; +import 'globals.dart'; + +const String _kTemplateExtension = '.tmpl'; +const String _kCopyTemplateExtension = '.copy.tmpl'; + +/// Expands templates in a directory to a destination. All files that must +/// undergo template expansion should end with the '.tmpl' extension. All other +/// files are ignored. In case the contents of entire directories must be copied +/// as is, the directory itself can end with '.tmpl' extension. Files within +/// such a directory may also contain the '.tmpl' extension and will be +/// considered for expansion. In case certain files need to be copied but +/// without template expansion (images, data files, etc.), the '.copy.tmpl' +/// extension may be used. +/// +/// Files in the destination will not contain either the '.tmpl' or '.copy.tmpl' +/// extensions. +class Template { + factory Template.fromName(String name) { + // All named templates are placed in the 'templates' directory + Directory templateDir = _templateDirectoryInPackage(name); + return new Template(templateDir, templateDir); + } + + Template(Directory templateSource, Directory baseDir) { + _templateFilePaths = new Map(); + + if (!templateSource.existsSync()) { + return; + } + + List templateFiles = + templateSource.listSync(recursive: true); + + for (FileSystemEntity entity in templateFiles) { + if (entity is! File) { + // We are only interesting in template *file* URIs. + continue; + } + + String relativePath = path.relative(entity.path, + from: baseDir.absolute.path); + + if (relativePath.contains(_kTemplateExtension)) { + // If '.tmpl' appears anywhere within the path of this entity, it is + // is a candidate for rendering. This catches cases where the folder + // itself is a template. + _templateFilePaths[relativePath] = path.absolute(entity.path); + } + } + } + + Map _templateFilePaths; + + void render(Directory destination, Map context, + { bool overwriteExisting: true }) { + destination.createSync(recursive: true); + + String destinationDirPath = destination.absolute.path; + + _templateFilePaths.forEach((String relativeDestPath, + String absoluteSrcPath) { + String finalDestinationPath = path + .join(destinationDirPath, relativeDestPath) + .replaceAll(_kCopyTemplateExtension, '') + .replaceAll(_kTemplateExtension, ''); + File finalDestinationFile = new File(finalDestinationPath); + String relativePathForLogging = relativeDestPath + .replaceAll(_kCopyTemplateExtension, '') + .replaceAll(_kTemplateExtension, ''); + + // Step 1: Check if the file needs to be overwritten. + + if (finalDestinationFile.existsSync()) { + if (overwriteExisting) { + finalDestinationFile.delete(recursive: true); + printStatus('$relativePathForLogging exists. Overwriting.'); + } else { + // The file exists but we cannot overwrite it, move on. + printStatus('$relativePathForLogging exists. Skipping.'); + return; + } + } else { + printStatus('$relativePathForLogging created.'); + } + + finalDestinationFile.createSync(recursive: true); + File sourceFile = new File(absoluteSrcPath); + + // Step 2: If the absolute paths ends with a 'copy.tmpl', this file does + // not need mustache rendering but needs to be directly copied. + + if (sourceFile.path.endsWith(_kCopyTemplateExtension)) { + finalDestinationFile.writeAsBytesSync(sourceFile.readAsBytesSync()); + + return; + } + + // Step 3: If the absolute path ends with a '.tmpl', this file needs + // rendering via mustache. + + if (sourceFile.path.endsWith(_kTemplateExtension)) { + String templateContents = sourceFile.readAsStringSync(); + String renderedContents = mustache.render(templateContents, context); + + finalDestinationFile.writeAsStringSync(renderedContents); + + return; + } + + // Step 4: This file does not end in .tmpl but is in a directory that + // does. Directly copy the file to the destination. + + finalDestinationFile.writeAsBytesSync(sourceFile.readAsBytesSync()); + }); + } +} + +Directory _templateDirectoryInPackage(String name) { + String templatesDir = path.join(ArtifactStore.flutterRoot, + 'packages', 'flutter_tools', 'templates'); + return new Directory(path.join(templatesDir, name)); +} diff --git a/packages/flutter_tools/templates/create/.analysis_options.tmpl b/packages/flutter_tools/templates/create/.analysis_options.tmpl new file mode 100644 index 00000000000..a9b94d74906 --- /dev/null +++ b/packages/flutter_tools/templates/create/.analysis_options.tmpl @@ -0,0 +1,3 @@ +analyzer: + exclude: + - 'ios/.generated/**' diff --git a/packages/flutter_tools/templates/create/.gitignore.tmpl b/packages/flutter_tools/templates/create/.gitignore.tmpl new file mode 100644 index 00000000000..14c7d4c3f73 --- /dev/null +++ b/packages/flutter_tools/templates/create/.gitignore.tmpl @@ -0,0 +1,9 @@ +.DS_Store +.atom/ +.idea +.packages +.pub/ +build/ +ios/.generated/ +packages +pubspec.lock diff --git a/packages/flutter_tools/templates/create/README.md.tmpl b/packages/flutter_tools/templates/create/README.md.tmpl new file mode 100644 index 00000000000..db004856f4f --- /dev/null +++ b/packages/flutter_tools/templates/create/README.md.tmpl @@ -0,0 +1,8 @@ +# {{projectName}} + +{{description}} + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](http://flutter.io/). diff --git a/packages/flutter_tools/templates/create/android/AndroidManifest.xml.tmpl b/packages/flutter_tools/templates/create/android/AndroidManifest.xml.tmpl new file mode 100644 index 00000000000..9c200fe5f3e --- /dev/null +++ b/packages/flutter_tools/templates/create/android/AndroidManifest.xml.tmpl @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + diff --git a/packages/flutter_tools/templates/create/android/res/.empty.copy.tmpl b/packages/flutter_tools/templates/create/android/res/.empty.copy.tmpl new file mode 100644 index 00000000000..d1531545923 --- /dev/null +++ b/packages/flutter_tools/templates/create/android/res/.empty.copy.tmpl @@ -0,0 +1 @@ +Place Android resources here (http://developer.android.com/guide/topics/resources/overview.html). diff --git a/packages/flutter_tools/templates/create/flutter.yaml.tmpl b/packages/flutter_tools/templates/create/flutter.yaml.tmpl new file mode 100644 index 00000000000..7dfaee1530d --- /dev/null +++ b/packages/flutter_tools/templates/create/flutter.yaml.tmpl @@ -0,0 +1,6 @@ +name: {{projectName}} +material-design-icons: + - name: content/add + - name: navigation/arrow_back + - name: navigation/menu + - name: navigation/more_vert diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Contents.json b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000000..363d1c4ce4f --- /dev/null +++ b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Contents.json @@ -0,0 +1,142 @@ +{ + "images" : [ + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-Small-40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-Small-40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-Small.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-Small@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-Small-40.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-Small-40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-76.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "icon_16x16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "icon_16x16@2x.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "icon_32x32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "icon_32x32@2x.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "icon_128x128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "icon_128x128@2x.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "icon_256x256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "icon_256x256@2x.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "icon_512x512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "icon_512x512@2x.png", + "scale" : "2x" + } + ] +} \ No newline at end of file diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-60@2x.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-60@2x.png new file mode 100644 index 00000000000..9996f5e598a Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-60@2x.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-60@3x.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-60@3x.png new file mode 100644 index 00000000000..7a543edbdd4 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-60@3x.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-76.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-76.png new file mode 100644 index 00000000000..05a82681049 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-76.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-76@2x.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-76@2x.png new file mode 100644 index 00000000000..bfbca28f5d5 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-76@2x.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-83.5@2x.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-83.5@2x.png new file mode 100644 index 00000000000..c924b8e497d Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-83.5@2x.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small-40.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small-40.png new file mode 100644 index 00000000000..3006f1d348c Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small-40.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small-40@2x.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small-40@2x.png new file mode 100644 index 00000000000..5f7d22d364d Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small-40@2x.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small-40@3x.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small-40@3x.png new file mode 100644 index 00000000000..9996f5e598a Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small-40@3x.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small.png new file mode 100644 index 00000000000..3433da11966 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small@2x.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small@2x.png new file mode 100644 index 00000000000..f7f9f16d1b4 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small@2x.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small@3x.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small@3x.png new file mode 100644 index 00000000000..e9c23601cf4 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/Icon-Small@3x.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_128x128.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_128x128.png new file mode 100644 index 00000000000..dd5fccb0a99 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_128x128.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_128x128@2x.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_128x128@2x.png new file mode 100644 index 00000000000..1909e275118 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_128x128@2x.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_16x16.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_16x16.png new file mode 100644 index 00000000000..40a701b5146 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_16x16.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_16x16@2x.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_16x16@2x.png new file mode 100644 index 00000000000..06c1a805609 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_16x16@2x.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_256x256.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_256x256.png new file mode 100644 index 00000000000..1909e275118 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_256x256.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_256x256@2x.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_256x256@2x.png new file mode 100644 index 00000000000..69c96ef1e27 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_256x256@2x.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_32x32.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_32x32.png new file mode 100644 index 00000000000..06c1a805609 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_32x32.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_32x32@2x.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_32x32@2x.png new file mode 100644 index 00000000000..0f943714603 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_32x32@2x.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_512x512.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_512x512.png new file mode 100644 index 00000000000..69c96ef1e27 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_512x512.png differ diff --git a/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_512x512@2x.png b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_512x512@2x.png new file mode 100644 index 00000000000..1d6355f58c8 Binary files /dev/null and b/packages/flutter_tools/templates/create/ios/Assets.xcassets.tmpl/AppIcon.appiconset/icon_512x512@2x.png differ diff --git a/packages/flutter_tools/templates/create/ios/Info.plist.tmpl b/packages/flutter_tools/templates/create/ios/Info.plist.tmpl new file mode 100644 index 00000000000..8b317f39827 --- /dev/null +++ b/packages/flutter_tools/templates/create/ios/Info.plist.tmpl @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + Runner + CFBundleIdentifier + {{projectIdentifier}} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + {{projectName}} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/flutter_tools/templates/create/ios/LaunchScreen.storyboard.tmpl b/packages/flutter_tools/templates/create/ios/LaunchScreen.storyboard.tmpl new file mode 100644 index 00000000000..78686cd0750 --- /dev/null +++ b/packages/flutter_tools/templates/create/ios/LaunchScreen.storyboard.tmpl @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/flutter_tools/templates/create/lib/main.dart.tmpl b/packages/flutter_tools/templates/create/lib/main.dart.tmpl new file mode 100644 index 00000000000..b455df23185 --- /dev/null +++ b/packages/flutter_tools/templates/create/lib/main.dart.tmpl @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; + +void main() { + runApp( + new MaterialApp( + title: 'Flutter Demo', + routes: { + '/': (RouteArguments args) => new FlutterDemo() + } + ) + ); +} + +class FlutterDemo extends StatefulComponent { + State createState() => new _FlutterDemoState(); +} + +class _FlutterDemoState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + _counter++; + }); + } + + Widget build(BuildContext context) { + return new Scaffold( + toolBar: new ToolBar( + center: new Text('Flutter Demo') + ), + body: new Center( + child: new Text( + 'Button tapped $_counter times.', + key: const ValueKey('counter') + ) + ), + floatingActionButton: new FloatingActionButton( + key: const ValueKey('fab'), + child: new Icon( + icon: 'content/add' + ), + onPressed: _incrementCounter + ) + ); + } +} diff --git a/packages/flutter_tools/templates/create/pubspec.yaml.tmpl b/packages/flutter_tools/templates/create/pubspec.yaml.tmpl new file mode 100644 index 00000000000..3f843a4f267 --- /dev/null +++ b/packages/flutter_tools/templates/create/pubspec.yaml.tmpl @@ -0,0 +1,10 @@ +name: {{projectName}} +description: {{description}} +dependencies: + flutter: + path: {{flutterPackagesDirectory}}/flutter +{{#withDriverTest?}} +dev_dependencies: + flutter_driver: + path: {{flutterPackagesDirectory}}/flutter_driver +{{/withDriverTest?}} diff --git a/packages/flutter_tools/templates/driver/e2e.dart.tmpl b/packages/flutter_tools/templates/driver/e2e.dart.tmpl new file mode 100644 index 00000000000..889670d7f7e --- /dev/null +++ b/packages/flutter_tools/templates/driver/e2e.dart.tmpl @@ -0,0 +1,9 @@ +// Starts the app with Flutter Driver extension enabled to allow Flutter Driver +// to test the app. +import 'package:{{projectName}}/main.dart' as app; +import 'package:flutter_driver/driver_extension.dart'; + +main() { + enableFlutterDriverExtension(); + app.main(); +} diff --git a/packages/flutter_tools/templates/driver/e2e_test.dart.tmpl b/packages/flutter_tools/templates/driver/e2e_test.dart.tmpl new file mode 100644 index 00000000000..02d80493df6 --- /dev/null +++ b/packages/flutter_tools/templates/driver/e2e_test.dart.tmpl @@ -0,0 +1,44 @@ +// This is a basic Flutter Driver test for the application. A Flutter Driver +// test is an end-to-end test that "drives" your application from another +// process or even from another computer. If you are familiar with +// Selenium/WebDriver for web, Espresso for Android or UI Automation for iOS, +// this is simply Flutter's version of that. +// +// To start the test run the following command from the root of your application +// package: +// +// flutter drive --target=test_driver/e2e.dart +// +import 'package:flutter_driver/flutter_driver.dart'; +import 'package:test/test.dart'; + +main() { + group('end-to-end test', () { + FlutterDriver driver; + + setUpAll(() async { + // Connect to a running Flutter application instance. + driver = await FlutterDriver.connect(); + }); + + tearDownAll(() async { + if (driver != null) driver.close(); + }); + + test('find the floating action button by value key', () async { + ObjectRef elem = await driver.findByValueKey('fab'); + expect(elem, isNotNull); + expect(elem.objectReferenceKey, isNotNull); + }); + + test('tap on the floating action button; verify counter', () async { + ObjectRef fab = await driver.findByValueKey('fab'); + expect(fab, isNotNull); + await driver.tap(fab); + ObjectRef counter = await driver.findByValueKey('counter'); + expect(counter, isNotNull); + String text = await driver.getText(counter); + expect(text, contains("Button tapped 1 times.")); + }); + }); +}