Move plugin injection to just after pub get (#14743)

diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart
index e0d5149..75af21b 100644
--- a/packages/flutter_tools/lib/src/android/gradle.dart
+++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -16,7 +16,6 @@
 import '../build_info.dart';
 import '../cache.dart';
 import '../globals.dart';
-import '../plugins.dart';
 import 'android_sdk.dart';
 import 'android_studio.dart';
 
@@ -94,7 +93,6 @@
 Future<GradleProject> _readGradleProject() async {
   final String gradle = await _ensureGradle();
   updateLocalProperties();
-  injectPlugins();
   try {
     final Status status = logger.startProgress('Resolving dependencies...', expectSlowOperation: true);
     final RunResult runResult = await runCheckedAsync(
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
index f98dd46a..ea4e9dc3 100644
--- a/packages/flutter_tools/lib/src/application_package.dart
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -181,11 +181,11 @@
     if (id == null)
       return null;
     final String projectPath = fs.path.join('ios', 'Runner.xcodeproj');
-    final Map<String, String> buildSettings = getXcodeBuildSettings(projectPath, 'Runner');
+    final Map<String, String> buildSettings = xcodeProjectInterpreter.getBuildSettings(projectPath, 'Runner');
     id = substituteXcodeVariables(id, buildSettings);
 
     return new BuildableIOSApp(
-      appDirectory: fs.path.join('ios'),
+      appDirectory: 'ios',
       projectBundleId: id,
       buildSettings: buildSettings,
     );
diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index 643a452..5956021 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -14,14 +14,10 @@
 import '../base/file_system.dart';
 import '../base/os.dart';
 import '../base/utils.dart';
-import '../build_info.dart';
 import '../cache.dart';
 import '../dart/pub.dart';
 import '../doctor.dart';
-import '../flx.dart' as flx;
 import '../globals.dart';
-import '../ios/xcodeproj.dart';
-import '../plugins.dart';
 import '../project.dart';
 import '../runner/flutter_command.dart';
 import '../template.dart';
@@ -232,17 +228,9 @@
     printStatus('Wrote $generatedCount files.');
     printStatus('');
 
-    updateXcodeGeneratedProperties(
-      projectPath: appPath,
-      buildInfo: BuildInfo.debug,
-      target: flx.defaultMainPath,
-      hasPlugins: generatePlugin,
-      previewDart2: false,
-    );
-
     if (argResults['pub']) {
       await pubGet(context: PubContext.create, directory: appPath, offline: argResults['offline']);
-      injectPlugins(directory: appPath);
+      new FlutterProject(fs.directory(appPath)).ensureReadyForPlatformSpecificTooling();
     }
 
     if (android_sdk.androidSdk != null)
diff --git a/packages/flutter_tools/lib/src/commands/inject_plugins.dart b/packages/flutter_tools/lib/src/commands/inject_plugins.dart
index f1df432..6886937 100644
--- a/packages/flutter_tools/lib/src/commands/inject_plugins.dart
+++ b/packages/flutter_tools/lib/src/commands/inject_plugins.dart
@@ -24,7 +24,8 @@
 
   @override
   Future<Null> runCommand() async {
-    final bool result = injectPlugins().hasPlugin;
+    injectPlugins();
+    final bool result = hasPlugins();
     if (result) {
       printStatus('GeneratedPluginRegistrants successfully written.');
     } else {
diff --git a/packages/flutter_tools/lib/src/commands/packages.dart b/packages/flutter_tools/lib/src/commands/packages.dart
index 44aafbc..3600289 100644
--- a/packages/flutter_tools/lib/src/commands/packages.dart
+++ b/packages/flutter_tools/lib/src/commands/packages.dart
@@ -5,8 +5,10 @@
 import 'dart:async';
 
 import '../base/common.dart';
+import '../base/file_system.dart';
 import '../base/os.dart';
 import '../dart/pub.dart';
+import '../project.dart';
 import '../runner/flutter_command.dart';
 
 class PackagesCommand extends FlutterCommand {
@@ -75,6 +77,7 @@
       offline: argResults['offline'],
       checkLastModified: false,
     );
+    new FlutterProject(fs.directory(target)).ensureReadyForPlatformSpecificTooling();
   }
 }
 
diff --git a/packages/flutter_tools/lib/src/ios/cocoapods.dart b/packages/flutter_tools/lib/src/ios/cocoapods.dart
index da63cdc..1d558d4 100644
--- a/packages/flutter_tools/lib/src/ios/cocoapods.dart
+++ b/packages/flutter_tools/lib/src/ios/cocoapods.dart
@@ -16,6 +16,7 @@
 import '../base/version.dart';
 import '../cache.dart';
 import '../globals.dart';
+import 'xcodeproj.dart';
 
 const String noCocoaPodsConsequence = '''
   CocoaPods is used to retrieve the iOS platform side's plugin code that responds to your plugin usage on the Dart side.
@@ -60,13 +61,13 @@
     // For backward compatibility with previously created Podfile only.
     @required String iosEngineDir,
     bool isSwift: false,
-    bool pluginOrFlutterPodChanged: true,
+    bool flutterPodChanged: true,
   }) async {
+    if (!(await appIosDir.childFile('Podfile').exists())) {
+      throwToolExit('Podfile missing');
+    }
     if (await _checkPodCondition()) {
-      if (!fs.file(fs.path.join(appIosDir.path, 'Podfile')).existsSync()) {
-        await _createPodfile(appIosDir, isSwift);
-      } // TODO(xster): Add more logic for handling merge conflicts.
-      if (_shouldRunPodInstall(appIosDir.path, pluginOrFlutterPodChanged))
+      if (_shouldRunPodInstall(appIosDir.path, flutterPodChanged))
         await _runPodInstall(appIosDir, iosEngineDir);
     }
   }
@@ -99,39 +100,69 @@
     return true;
   }
 
-  Future<Null> _createPodfile(Directory bundle, bool isSwift) async {
-    final File podfileTemplate = fs.file(fs.path.join(
-      Cache.flutterRoot,
-      'packages',
-      'flutter_tools',
-      'templates',
-      'cocoapods',
-      isSwift ? 'Podfile-swift' : 'Podfile-objc',
-    ));
-    podfileTemplate.copySync(fs.path.join(bundle.path, 'Podfile'));
+  /// Ensures the `ios` sub-project of the Flutter project at [directory]
+  /// contains a suitable `Podfile` and that its `Flutter/Xxx.xcconfig` files
+  /// include pods configuration.
+  void setupPodfile(String directory) {
+    if (!xcodeProjectInterpreter.canInterpretXcodeProjects) {
+      // Don't do anything for iOS when host platform doesn't support it.
+      return;
+    }
+    final String podfilePath = fs.path.join(directory, 'ios', 'Podfile');
+    if (!fs.file(podfilePath).existsSync()) {
+      final bool isSwift = xcodeProjectInterpreter.getBuildSettings(
+        fs.path.join(directory, 'ios', 'Runner.xcodeproj'),
+        'Runner',
+      ).containsKey('SWIFT_VERSION');
+      final File podfileTemplate = fs.file(fs.path.join(
+        Cache.flutterRoot,
+        'packages',
+        'flutter_tools',
+        'templates',
+        'cocoapods',
+        isSwift ? 'Podfile-swift' : 'Podfile-objc',
+      ));
+      podfileTemplate.copySync(podfilePath);
+    }
+    _addPodsDependencyToFlutterXcconfig(directory, 'Debug');
+    _addPodsDependencyToFlutterXcconfig(directory, 'Release');
+  }
+
+  void _addPodsDependencyToFlutterXcconfig(String directory, String mode) {
+    final File file = fs.file(fs.path.join(directory, 'ios', 'Flutter', '$mode.xcconfig'));
+    if (file.existsSync()) {
+      final String content = file.readAsStringSync();
+      final String include = '#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.${mode
+          .toLowerCase()}.xcconfig"';
+      if (!content.contains(include))
+        file.writeAsStringSync('$include\n$content', flush: true);
+    }
+  }
+
+  /// Ensures that pod install is deemed needed on next check.
+  void invalidatePodInstallOutput(String directory) {
+    final File manifest = fs.file(
+      fs.path.join(directory, 'ios', 'Pods', 'Manifest.lock'),
+    );
+    if (manifest.existsSync())
+      manifest.deleteSync();
   }
 
   // Check if you need to run pod install.
   // The pod install will run if any of below is true.
-  // 1. Any plugins changed (add/update/delete)
-  // 2. The flutter.framework has changed (debug/release/profile)
-  // 3. The podfile.lock doesn't exists
-  // 4. The Pods/manifest.lock doesn't exists
-  // 5. The podfile.lock doesn't match Pods/manifest.lock.
-  bool _shouldRunPodInstall(String appDir, bool pluginOrFlutterPodChanged) {
-    if (pluginOrFlutterPodChanged)
+  // 1. The flutter.framework has changed (debug/release/profile)
+  // 2. The podfile.lock doesn't exist
+  // 3. The Pods/Manifest.lock doesn't exist (It is deleted when plugins change)
+  // 4. The podfile.lock doesn't match Pods/Manifest.lock.
+  bool _shouldRunPodInstall(String appDir, bool flutterPodChanged) {
+    if (flutterPodChanged)
       return true;
-    // Check if podfile.lock and Pods/Manifest.lock exists and matches.
+    // Check if podfile.lock and Pods/Manifest.lock exist and match.
     final File podfileLockFile = fs.file(fs.path.join(appDir, 'Podfile.lock'));
-    final File manifestLockFile =
-        fs.file(fs.path.join(appDir, 'Pods', 'Manifest.lock'));
-    if (!podfileLockFile.existsSync()
+    final File manifestLockFile = fs.file(fs.path.join(appDir, 'Pods', 'Manifest.lock'));
+    return !podfileLockFile.existsSync()
         || !manifestLockFile.existsSync()
-        || podfileLockFile.readAsStringSync() !=
-            manifestLockFile.readAsStringSync()) {
-      return true;
-    }
-    return false;
+        || podfileLockFile.readAsStringSync() != manifestLockFile.readAsStringSync();
   }
 
   Future<Null> _runPodInstall(Directory bundle, String engineDirectory) async {
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index 32d37b1..b2a5f6f 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -219,7 +219,7 @@
     return new XcodeBuildResult(success: false);
   }
 
-  final XcodeProjectInfo projectInfo = new XcodeProjectInfo.fromProjectSync(app.appDirectory);
+  final XcodeProjectInfo projectInfo = xcodeProjectInterpreter.getInfo(app.appDirectory);
   if (!projectInfo.targets.contains('Runner')) {
     printError('The Xcode project does not define target "Runner" which is needed by Flutter tooling.');
     printError('Open Xcode to fix the problem:');
@@ -256,26 +256,22 @@
   // copied over to a location that is suitable for Xcodebuild to find them.
   final Directory appDirectory = fs.directory(app.appDirectory);
   await _addServicesToBundle(appDirectory);
-  final InjectPluginsResult injectPluginsResult = injectPlugins();
-  final bool hasFlutterPlugins = injectPluginsResult.hasPlugin;
   final String previousGeneratedXcconfig = readGeneratedXcconfig(app.appDirectory);
 
-  updateXcodeGeneratedProperties(
+  updateGeneratedXcodeProperties(
     projectPath: fs.currentDirectory.path,
     buildInfo: buildInfo,
     target: target,
-    hasPlugins: hasFlutterPlugins,
     previewDart2: buildInfo.previewDart2,
   );
 
-  if (hasFlutterPlugins) {
+  if (hasPlugins()) {
     final String currentGeneratedXcconfig = readGeneratedXcconfig(app.appDirectory);
     await cocoaPods.processPods(
-        appIosDir: appDirectory,
-        iosEngineDir: flutterFrameworkDir(buildInfo.mode),
-        isSwift: app.isSwift,
-        pluginOrFlutterPodChanged: injectPluginsResult.hasChanged
-            || previousGeneratedXcconfig != currentGeneratedXcconfig,
+      appIosDir: appDirectory,
+      iosEngineDir: flutterFrameworkDir(buildInfo.mode),
+      isSwift: app.isSwift,
+      flutterPodChanged: (previousGeneratedXcconfig != currentGeneratedXcconfig),
     );
   }
 
@@ -465,7 +461,7 @@
 
 String readGeneratedXcconfig(String appPath) {
   final String generatedXcconfigPath =
-      fs.path.join(fs.currentDirectory.path, appPath, 'Flutter','Generated.xcconfig');
+      fs.path.join(fs.currentDirectory.path, appPath, 'Flutter', 'Generated.xcconfig');
   final File generatedXcconfigFile = fs.file(generatedXcconfigPath);
   if (!generatedXcconfigFile.existsSync())
     return null;
diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
index 1bc8e79..9a50d30 100644
--- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart
+++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
@@ -5,11 +5,13 @@
 import 'package:meta/meta.dart';
 
 import '../artifacts.dart';
+import '../base/context.dart';
 import '../base/file_system.dart';
 import '../base/process.dart';
 import '../base/utils.dart';
 import '../build_info.dart';
 import '../cache.dart';
+import '../flx.dart' as flx;
 import '../globals.dart';
 
 final RegExp _settingExpr = new RegExp(r'(\w+)\s*=\s*(.*)$');
@@ -19,11 +21,28 @@
   return fs.path.normalize(fs.path.dirname(artifacts.getArtifactPath(Artifact.flutterFramework, TargetPlatform.ios, mode)));
 }
 
-void updateXcodeGeneratedProperties({
+String _generatedXcodePropertiesPath(String projectPath) {
+  return fs.path.join(projectPath, 'ios', 'Flutter', 'Generated.xcconfig');
+}
+
+/// Writes default Xcode properties files in the Flutter project at
+/// [projectPath], if such files do not already exist.
+void generateXcodeProperties(String projectPath) {
+  if (fs.file(_generatedXcodePropertiesPath(projectPath)).existsSync())
+    return;
+  updateGeneratedXcodeProperties(
+      projectPath: projectPath,
+      buildInfo: BuildInfo.debug,
+      target: flx.defaultMainPath,
+      previewDart2: false,
+  );
+}
+
+/// Writes or rewrites Xcode property files with the specified information.
+void updateGeneratedXcodeProperties({
   @required String projectPath,
   @required BuildInfo buildInfo,
   @required String target,
-  @required bool hasPlugins,
   @required bool previewDart2,
 }) {
   final StringBuffer localsBuffer = new StringBuffer();
@@ -58,21 +77,42 @@
     localsBuffer.writeln('PREVIEW_DART_2=true');
   }
 
-  // Add dependency to CocoaPods' generated project only if plugins are used.
-  if (hasPlugins)
-    localsBuffer.writeln('#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"');
-
-  final File localsFile = fs.file(fs.path.join(projectPath, 'ios', 'Flutter', 'Generated.xcconfig'));
+  final File localsFile = fs.file(_generatedXcodePropertiesPath(projectPath));
   localsFile.createSync(recursive: true);
   localsFile.writeAsStringSync(localsBuffer.toString());
 }
 
-Map<String, String> getXcodeBuildSettings(String xcodeProjPath, String target) {
-  final String absProjPath = fs.path.absolute(xcodeProjPath);
-  final String out = runCheckedSync(<String>[
-    '/usr/bin/xcodebuild', '-project', absProjPath, '-target', target, '-showBuildSettings'
-  ]);
-  return parseXcodeBuildSettings(out);
+XcodeProjectInterpreter get xcodeProjectInterpreter => context.putIfAbsent(
+  XcodeProjectInterpreter,
+  () => const XcodeProjectInterpreter(),
+);
+
+/// Interpreter of Xcode projects settings.
+class XcodeProjectInterpreter {
+  static const String _executable = '/usr/bin/xcodebuild';
+
+  const XcodeProjectInterpreter();
+
+  bool get canInterpretXcodeProjects => fs.isFileSync(_executable);
+
+  Map<String, String> getBuildSettings(String projectPath, String target) {
+    final String out = runCheckedSync(<String>[
+      _executable,
+      '-project',
+      fs.path.absolute(projectPath),
+      '-target',
+      target,
+      '-showBuildSettings'
+    ], workingDirectory: projectPath);
+    return parseXcodeBuildSettings(out);
+  }
+
+  XcodeProjectInfo getInfo(String projectPath) {
+    final String out = runCheckedSync(<String>[
+      _executable, '-list',
+    ], workingDirectory: projectPath);
+    return new XcodeProjectInfo.fromXcodeBuildOutput(out);
+  }
 }
 
 Map<String, String> parseXcodeBuildSettings(String showBuildSettingsOutput) {
@@ -101,13 +141,6 @@
 class XcodeProjectInfo {
   XcodeProjectInfo(this.targets, this.buildConfigurations, this.schemes);
 
-  factory XcodeProjectInfo.fromProjectSync(String projectPath) {
-    final String out = runCheckedSync(<String>[
-      '/usr/bin/xcodebuild', '-list',
-    ], workingDirectory: projectPath);
-    return new XcodeProjectInfo.fromXcodeBuildOutput(out);
-  }
-
   factory XcodeProjectInfo.fromXcodeBuildOutput(String output) {
     final List<String> targets = <String>[];
     final List<String> buildConfigurations = <String>[];
diff --git a/packages/flutter_tools/lib/src/plugins.dart b/packages/flutter_tools/lib/src/plugins.dart
index 0d03f04..697cfdc 100644
--- a/packages/flutter_tools/lib/src/plugins.dart
+++ b/packages/flutter_tools/lib/src/plugins.dart
@@ -9,6 +9,7 @@
 import 'base/file_system.dart';
 import 'dart/package_map.dart';
 import 'globals.dart';
+import 'ios/cocoapods.dart';
 
 class Plugin {
   final String name;
@@ -80,21 +81,25 @@
 
 /// Returns true if .flutter-plugins has changed, otherwise returns false.
 bool _writeFlutterPluginsList(String directory, List<Plugin> plugins) {
-  final File pluginsProperties = fs.file(fs.path.join(directory, '.flutter-plugins'));
-  final String previousFlutterPlugins =
-      pluginsProperties.existsSync() ? pluginsProperties.readAsStringSync() : null;
+  final File pluginsFile = fs.file(fs.path.join(directory, '.flutter-plugins'));
+  final String oldContents = _readFlutterPluginsList(directory);
   final String pluginManifest =
       plugins.map((Plugin p) => '${p.name}=${escapePath(p.path)}').join('\n');
   if (pluginManifest.isNotEmpty) {
-    pluginsProperties.writeAsStringSync('$pluginManifest\n');
+    pluginsFile.writeAsStringSync('$pluginManifest\n', flush: true);
   } else {
-    if (pluginsProperties.existsSync()) {
-      pluginsProperties.deleteSync();
-    }
+    if (pluginsFile.existsSync())
+      pluginsFile.deleteSync();
   }
-  final String currentFlutterPlugins =
-      pluginsProperties.existsSync() ? pluginsProperties.readAsStringSync() : null;
-  return currentFlutterPlugins != previousFlutterPlugins;
+  final String newContents = _readFlutterPluginsList(directory);
+  return oldContents != newContents;
+}
+
+/// Returns the contents of the `.flutter-plugins` file in [directory], or
+/// null if that file does not exist.
+String _readFlutterPluginsList(String directory) {
+  final File pluginsFile = fs.file(fs.path.join(directory, '.flutter-plugins'));
+  return pluginsFile.existsSync() ? pluginsFile.readAsStringSync() : null;
 }
 
 const String _androidPluginRegistryTemplate = '''package io.flutter.plugins;
@@ -128,7 +133,7 @@
 }
 ''';
 
-void _writeAndroidPluginRegistry(String directory, List<Plugin> plugins) {
+void _writeAndroidPluginRegistrant(String directory, List<Plugin> plugins) {
   final List<Map<String, dynamic>> androidPlugins = plugins
       .where((Plugin p) => p.androidPackage != null && p.pluginClass != null)
       .map((Plugin p) => <String, dynamic>{
@@ -187,7 +192,7 @@
 @end
 ''';
 
-void _writeIOSPluginRegistry(String directory, List<Plugin> plugins) {
+void _writeIOSPluginRegistrant(String directory, List<Plugin> plugins) {
   final List<Map<String, dynamic>> iosPlugins = plugins
       .where((Plugin p) => p.pluginClass != null)
       .map((Plugin p) => <String, dynamic>{
@@ -210,7 +215,6 @@
   registryHeaderFile.writeAsStringSync(pluginRegistryHeader);
   final File registryImplementationFile = registryDirectory.childFile('GeneratedPluginRegistrant.m');
   registryImplementationFile.writeAsStringSync(pluginRegistryImplementation);
-
 }
 
 class InjectPluginsResult{
@@ -224,17 +228,30 @@
   final bool hasChanged;
 }
 
-/// Finds Flutter plugins in the pubspec.yaml, creates platform injection
-/// registries classes and add them to the build dependencies.
-///
-/// Returns whether any Flutter plugins are added and whether they changed.
-InjectPluginsResult injectPlugins({String directory}) {
+/// Injects plugins found in `pubspec.yaml` into the platform-specific projects.
+void injectPlugins({String directory}) {
   directory ??= fs.currentDirectory.path;
+  if (fs.file(fs.path.join(directory, 'example', 'pubspec.yaml')).existsSync()) {
+    // Switch to example app if in plugin project template.
+    directory = fs.path.join(directory, 'example');
+  }
   final List<Plugin> plugins = _findPlugins(directory);
-  final bool hasPluginsChanged = _writeFlutterPluginsList(directory, plugins);
+  final bool changed = _writeFlutterPluginsList(directory, plugins);
   if (fs.isDirectorySync(fs.path.join(directory, 'android')))
-    _writeAndroidPluginRegistry(directory, plugins);
-  if (fs.isDirectorySync(fs.path.join(directory, 'ios')))
-    _writeIOSPluginRegistry(directory, plugins);
-  return new InjectPluginsResult(hasPlugin: plugins.isNotEmpty, hasChanged: hasPluginsChanged);
+    _writeAndroidPluginRegistrant(directory, plugins);
+  if (fs.isDirectorySync(fs.path.join(directory, 'ios'))) {
+    _writeIOSPluginRegistrant(directory, plugins);
+    final CocoaPods cocoaPods = const CocoaPods();
+    if (plugins.isNotEmpty)
+      cocoaPods.setupPodfile(directory);
+    if (changed)
+      cocoaPods.invalidatePodInstallOutput(directory);
+  }
+}
+
+/// Returns whether the Flutter project at the specified [directory]
+/// has any plugin dependencies.
+bool hasPlugins({String directory}) {
+  directory ??= fs.currentDirectory.path;
+  return _readFlutterPluginsList(directory) != null;
 }
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
index 1fd1b4f..7800f2a 100644
--- a/packages/flutter_tools/lib/src/project.dart
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -4,7 +4,11 @@
 
 import 'dart:async';
 import 'dart:convert';
+
 import 'base/file_system.dart';
+import 'ios/xcodeproj.dart';
+import 'plugins.dart';
+
 
 /// Represents the contents of a Flutter project at the specified [directory].
 class FlutterProject {
@@ -43,8 +47,25 @@
   /// The Android sub project of this project.
   AndroidProject get android => new AndroidProject(directory.childDirectory('android'));
 
+  /// Returns true if this project is a plugin project.
+  bool get isPluginProject => directory.childDirectory('example').childFile('pubspec.yaml').existsSync();
+
   /// The example sub project of this (plugin) project.
   FlutterProject get example => new FlutterProject(directory.childDirectory('example'));
+
+  /// Generates project files necessary to make Gradle builds work on Android
+  /// and CocoaPods+Xcode work on iOS.
+  void ensureReadyForPlatformSpecificTooling() {
+    if (!directory.existsSync()) {
+      return;
+    }
+    if (isPluginProject) {
+      example.ensureReadyForPlatformSpecificTooling();
+    } else {
+      injectPlugins(directory: directory.path);
+      generateXcodeProperties(directory.path);
+    }
+  }
 }
 
 /// Represents the contents of the ios/ folder of a Flutter project.
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index 9d08b3e..ca48a1a 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -21,6 +21,7 @@
 import '../doctor.dart';
 import '../flx.dart' as flx;
 import '../globals.dart';
+import '../project.dart';
 import '../usage.dart';
 import 'flutter_command_runner.dart';
 
@@ -272,8 +273,10 @@
     if (shouldUpdateCache)
       await cache.updateAll();
 
-    if (shouldRunPub)
+    if (shouldRunPub) {
       await pubGet(context: PubContext.getVerifyContext(name));
+      new FlutterProject(fs.currentDirectory).ensureReadyForPlatformSpecificTooling();
+    }
 
     setupApplicationPackages();