Add --template=<type> option to create command. (#11105)

Allows the user to specify the kind of project to create. The default is 'app'. Other choices are 'plugin' (the old '--plugin' behavior), and 'package'.

A Flutter 'package' is a Dart package that depends on Flutter, but does not contain native code.

Fixes #10377.
diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index 75f4795..cd600ed 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -36,11 +36,26 @@
       defaultsTo: false,
       help: 'Also add a flutter_driver dependency and generate a sample \'flutter drive\' test.'
     );
+    argParser.addOption(
+      'template',
+      abbr: 't',
+      allowed: <String>['app', 'package', 'plugin'],
+      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 with a platform-specific implementation for Android, for '
+            'iOS code, or for both.',
+      },
+      defaultsTo: 'app',
+    );
     argParser.addFlag(
-      'plugin',
-      negatable: true,
-      defaultsTo: false,
-      help: 'Generate a Flutter plugin project.'
+        'plugin',
+        negatable: false,
+        defaultsTo: false,
+        hide: true,
     );
     argParser.addOption(
       'description',
@@ -110,7 +125,11 @@
     if (!fs.isFileSync(fs.path.join(flutterDriverPackagePath, 'pubspec.yaml')))
       throwToolExit('Unable to find package:flutter_driver in $flutterDriverPackagePath', exitCode: 2);
 
-    final bool generatePlugin = argResults['plugin'];
+    String template = argResults['template'];
+    if (argResults['plugin'])
+      template = 'plugin';
+    final bool generatePlugin = template == 'plugin';
+    final bool generatePackage = template == 'package';
 
     final Directory projectDir = fs.directory(argResults.rest.first);
     String dirPath = fs.path.normalize(projectDir.absolute.path);
@@ -142,6 +161,23 @@
 
     printStatus('Creating project ${fs.path.relative(dirPath)}...');
     int generatedCount = 0;
+    if (generatePackage) {
+      final String description = argResults.wasParsed('description')
+          ? argResults['description']
+          : 'A new flutter package project.';
+      templateContext['description'] = description;
+      generatedCount += _renderTemplate('package', dirPath, templateContext);
+
+      if (argResults['pub'])
+        await pubGet(directory: dirPath);
+
+      final String relativePath = fs.path.relative(dirPath);
+      printStatus('Wrote $generatedCount files.');
+      printStatus('');
+      printStatus('Your plugin code is in lib/$projectName.dart in the $relativePath directory.');
+      return;
+    }
+
     String appPath = dirPath;
     if (generatePlugin) {
       final String description = argResults.wasParsed('description')
diff --git a/packages/flutter_tools/templates/package/.gitignore.tmpl b/packages/flutter_tools/templates/package/.gitignore.tmpl
new file mode 100644
index 0000000..75e9658
--- /dev/null
+++ b/packages/flutter_tools/templates/package/.gitignore.tmpl
@@ -0,0 +1,7 @@
+.DS_Store
+.atom/
+.idea
+.packages
+.pub/
+packages
+pubspec.lock
diff --git a/packages/flutter_tools/templates/package/CHANGELOG.md.tmpl b/packages/flutter_tools/templates/package/CHANGELOG.md.tmpl
new file mode 100644
index 0000000..ac07159
--- /dev/null
+++ b/packages/flutter_tools/templates/package/CHANGELOG.md.tmpl
@@ -0,0 +1,3 @@
+## [0.0.1] - TODO: Add release date.
+
+* TODO: Describe initial release.
diff --git a/packages/flutter_tools/templates/package/LICENSE.tmpl b/packages/flutter_tools/templates/package/LICENSE.tmpl
new file mode 100644
index 0000000..ba75c69
--- /dev/null
+++ b/packages/flutter_tools/templates/package/LICENSE.tmpl
@@ -0,0 +1 @@
+TODO: Add your license here.
diff --git a/packages/flutter_tools/templates/package/README.md.tmpl b/packages/flutter_tools/templates/package/README.md.tmpl
new file mode 100644
index 0000000..b5d6778
--- /dev/null
+++ b/packages/flutter_tools/templates/package/README.md.tmpl
@@ -0,0 +1,9 @@
+# {{projectName}}
+
+{{description}}
+
+## Getting Started
+
+For help getting started with Flutter, view our online [documentation](http://flutter.io/).
+
+For help on editing package code, view the [documentation](https://flutter.io/developing-packages/).
diff --git a/packages/flutter_tools/templates/package/lib/projectName.dart.tmpl b/packages/flutter_tools/templates/package/lib/projectName.dart.tmpl
new file mode 100644
index 0000000..4048343
--- /dev/null
+++ b/packages/flutter_tools/templates/package/lib/projectName.dart.tmpl
@@ -0,0 +1,7 @@
+library {{projectName}};
+
+/// A Calculator.
+class Calculator {
+  /// Returns [value] plus 1.
+  int addOne(int value) => value + 1;
+}
diff --git a/packages/flutter_tools/templates/package/projectName.iml.tmpl b/packages/flutter_tools/templates/package/projectName.iml.tmpl
new file mode 100644
index 0000000..dafb001
--- /dev/null
+++ b/packages/flutter_tools/templates/package/projectName.iml.tmpl
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/.idea" />
+      <excludeFolder url="file://$MODULE_DIR$/.pub" />
+      <excludeFolder url="file://$MODULE_DIR$/build" />
+      <excludeFolder url="file://$MODULE_DIR$/packages" />
+    </content>
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Dart Packages" level="project" />
+    <orderEntry type="library" name="Dart SDK" level="project" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/packages/flutter_tools/templates/package/pubspec.yaml.tmpl b/packages/flutter_tools/templates/package/pubspec.yaml.tmpl
new file mode 100644
index 0000000..138ae3d
--- /dev/null
+++ b/packages/flutter_tools/templates/package/pubspec.yaml.tmpl
@@ -0,0 +1,14 @@
+name: {{projectName}}
+description: {{description}}
+version: 0.0.1
+author:
+homepage:
+
+flutter:
+
+dependencies:
+  flutter:
+    sdk: flutter
+
+dev_dependencies:
+  test: ^0.12.0
diff --git a/packages/flutter_tools/templates/package/test/projectName_test.dart.tmpl b/packages/flutter_tools/templates/package/test/projectName_test.dart.tmpl
new file mode 100644
index 0000000..fbf9571
--- /dev/null
+++ b/packages/flutter_tools/templates/package/test/projectName_test.dart.tmpl
@@ -0,0 +1,13 @@
+import 'package:test/test.dart';
+
+import 'package:{{projectName}}/{{projectName}}.dart';
+
+void main() {
+  test('adds one to input values', () {
+    final calculator = new Calculator();
+    expect(calculator.addOne(2), 3);
+    expect(calculator.addOne(-7), -6);
+    expect(calculator.addOne(0), 1);
+    expect(() => calculator.addOne(null), throwsNoSuchMethodError);
+  });
+}
diff --git a/packages/flutter_tools/templates/plugin/LICENSE.tmpl b/packages/flutter_tools/templates/plugin/LICENSE.tmpl
index 86928f6..ba75c69 100644
--- a/packages/flutter_tools/templates/plugin/LICENSE.tmpl
+++ b/packages/flutter_tools/templates/plugin/LICENSE.tmpl
@@ -1,27 +1 @@
-// Copyright 2017 Your Company. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//    * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//    * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//    * Neither the name of Your Company nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+TODO: Add your license here.
diff --git a/packages/flutter_tools/test/commands/create_test.dart b/packages/flutter_tools/test/commands/create_test.dart
index f8240a0..065c344 100644
--- a/packages/flutter_tools/test/commands/create_test.dart
+++ b/packages/flutter_tools/test/commands/create_test.dart
@@ -70,9 +70,55 @@
       );
     });
 
+    testUsingContext('package project', () async {
+      return _createAndAnalyzeProject(
+        projectDir,
+        <String>['--template=package'],
+        <String>[
+          'lib/flutter_project.dart',
+          'test/flutter_project_test.dart',
+        ],
+        unexpectedPaths: <String>[
+          'android/app/src/main/java/com/yourcompany/flutterproject/MainActivity.java',
+          'android/src/main/java/com/yourcompany/flutterproject/FlutterProjectPlugin.java',
+          'ios/Classes/FlutterProjectPlugin.h',
+          'ios/Classes/FlutterProjectPlugin.m',
+          'ios/Runner/AppDelegate.h',
+          'ios/Runner/AppDelegate.m',
+          'ios/Runner/main.m',
+          'lib/main.dart',
+          'example/android/app/src/main/java/com/yourcompany/flutterprojectexample/MainActivity.java',
+          'example/ios/Runner/AppDelegate.h',
+          'example/ios/Runner/AppDelegate.m',
+          'example/ios/Runner/main.m',
+          'example/lib/main.dart',
+          'test/widget_test.dart',
+        ],
+      );
+    });
+
     testUsingContext('plugin project', () async {
       return _createAndAnalyzeProject(
         projectDir,
+        <String>['--template=plugin'],
+        <String>[
+          'android/src/main/java/com/yourcompany/flutterproject/FlutterProjectPlugin.java',
+          'ios/Classes/FlutterProjectPlugin.h',
+          'ios/Classes/FlutterProjectPlugin.m',
+          'lib/flutter_project.dart',
+          'example/android/app/src/main/java/com/yourcompany/flutterprojectexample/MainActivity.java',
+          'example/ios/Runner/AppDelegate.h',
+          'example/ios/Runner/AppDelegate.m',
+          'example/ios/Runner/main.m',
+          'example/lib/main.dart',
+        ],
+        plugin: true,
+      );
+    });
+
+    testUsingContext('plugin project (legacy)', () async {
+      return _createAndAnalyzeProject(
+        projectDir,
         <String>['--plugin'],
         <String>[
           'android/src/main/java/com/yourcompany/flutterproject/FlutterProjectPlugin.java',
@@ -92,7 +138,7 @@
     testUsingContext('kotlin/swift plugin project', () async {
       return _createAndAnalyzeProject(
         projectDir,
-        <String>['--plugin', '-a', 'kotlin', '--ios-language', 'swift'],
+        <String>['--template=plugin', '-a', 'kotlin', '--ios-language', 'swift'],
         <String>[
           'android/src/main/kotlin/com/yourcompany/flutterproject/FlutterProjectPlugin.kt',
           'ios/Classes/FlutterProjectPlugin.h',
@@ -118,7 +164,7 @@
     testUsingContext('plugin project with custom org', () async {
       return _createAndAnalyzeProject(
           projectDir,
-          <String>['--plugin', '--org', 'com.bar.foo'],
+          <String>['--template=plugin', '--org', 'com.bar.foo'],
           <String>[
             'android/src/main/java/com/bar/foo/flutterproject/FlutterProjectPlugin.java',
             'example/android/app/src/main/java/com/bar/foo/flutterprojectexample/MainActivity.java',