"flutter create" can generate a basic driver test

"flutter create" adds option `--with-driver-test` that adds
dependencies to `flutter_driver` in `pubspec.yaml` and creates
a basic driver test runnable via `flutter drive
--target=test_driver/e2e.dart`

"flutter drive" new options:

- `--keep-app-running` tells the driver to not stop the app after tests
  are done
- `--use-existing-app` tells the driver to not start a new app but use
  an already running instance
diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index 6ec70ae..8277248 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -30,6 +30,12 @@
       defaultsTo: true,
       help: 'Whether to run "pub get" after the project has been created.'
     );
+    argParser.addFlag(
+      'with-driver-test',
+      negatable: true,
+      defaultsTo: false,
+      help: 'Also add Flutter Driver dependencies and generate a sample driver test.'
+    );
   }
 
   String get invocation => "${runner.executableName} $name <output directory>";
@@ -50,12 +56,19 @@
 
     String flutterRoot = path.absolute(ArtifactStore.flutterRoot);
 
-    String flutterPackagePath = path.join(flutterRoot, 'packages', 'flutter');
+    String flutterPackagesDirectory = path.join(flutterRoot, 'packages');
+    String flutterPackagePath = path.join(flutterPackagesDirectory, 'flutter');
     if (!FileSystemEntity.isFileSync(path.join(flutterPackagePath, 'pubspec.yaml'))) {
       printError('Unable to find package:flutter in $flutterPackagePath');
       return 2;
     }
 
+    String flutterDriverPackagePath = path.join(flutterRoot, 'packages', 'flutter_driver');
+    if (!FileSystemEntity.isFileSync(path.join(flutterDriverPackagePath, 'pubspec.yaml'))) {
+      printError('Unable to find package:flutter_driver in $flutterDriverPackagePath');
+      return 2;
+    }
+
     Directory out;
 
     if (argResults.wasParsed('out')) {
@@ -64,7 +77,15 @@
       out = new Directory(argResults.rest.first);
     }
 
-    new FlutterSimpleTemplate().generateInto(out, flutterPackagePath);
+    FlutterSimpleTemplate template = new FlutterSimpleTemplate();
+
+    if (argResults['with-driver-test'])
+      template.withDriverTest();
+
+    template.generateInto(
+      dir: out,
+      flutterPackagesDirectory: flutterPackagesDirectory
+    );
 
     printStatus('');
 
@@ -90,19 +111,23 @@
 abstract class Template {
   final String name;
   final String description;
-
-  Map<String, String> files = <String, String>{};
+  final Map<String, String> files = <String, String>{};
+  final Map<String, dynamic> additionalTemplateVariables = <String, dynamic>{};
 
   Template(this.name, this.description);
 
-  void generateInto(Directory dir, String flutterPackagePath) {
+  void generateInto({
+    Directory dir,
+    String flutterPackagesDirectory
+  }) {
     String dirPath = path.normalize(dir.absolute.path);
     String projectName = _normalizeProjectName(path.basename(dirPath));
     String projectIdentifier = _createProjectIdentifier(path.basename(dirPath));
     printStatus('Creating ${path.basename(projectName)}...');
     dir.createSync(recursive: true);
 
-    String relativeFlutterPackagePath = path.relative(flutterPackagePath, from: dirPath);
+    String relativeFlutterPackagesDirectory =
+        path.relative(flutterPackagesDirectory, from: dirPath);
     Iterable<String> paths = files.keys.toList()..sort();
 
     for (String filePath in paths) {
@@ -111,8 +136,9 @@
         'projectName': projectName,
         'projectIdentifier': projectIdentifier,
         'description': description,
-        'flutterPackagePath': relativeFlutterPackagePath
+        'flutterPackagesDirectory': relativeFlutterPackagesDirectory,
       };
+      m.addAll(additionalTemplateVariables);
       contents = mustache.render(contents, m);
       filePath = filePath.replaceAll('/', Platform.pathSeparator);
       File file = new File(path.join(dir.path, filePath));
@@ -142,6 +168,12 @@
     // 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) {
@@ -194,7 +226,12 @@
 description: {{description}}
 dependencies:
   flutter:
-    path: {{flutterPackagePath}}
+    path: {{flutterPackagesDirectory}}/flutter
+{{#withDriverTest?}}
+dev_dependencies:
+  flutter_driver:
+    path: {{flutterPackagesDirectory}}/flutter_driver
+{{/withDriverTest?}}
 ''';
 
 const String _flutterYaml = r'''
@@ -240,10 +277,14 @@
       ),
       body: new Material(
         child: new Center(
-          child: new Text('Button tapped $_counter times.')
+          child: new Text(
+            'Button tapped $_counter times.',
+            key: const ValueKey('counter')
+          )
         )
       ),
       floatingActionButton: new FloatingActionButton(
+        key: const ValueKey('fab'),
         child: new Icon(
           icon: 'content/add'
         ),
@@ -254,6 +295,65 @@
 }
 ''';
 
+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 = '''
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="{{projectIdentifier}}"