Vend Flutter module App.framework as a local CocoaPod pod to be installed by a host app (#36793)

diff --git a/dev/integration_tests/ios_add2app/Podfile b/dev/integration_tests/ios_add2app/Podfile
index 72e1973..8885e60 100644
--- a/dev/integration_tests/ios_add2app/Podfile
+++ b/dev/integration_tests/ios_add2app/Podfile
@@ -1,26 +1,15 @@
 platform :ios, '12.0'
 
-# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
-install! 'cocoapods', :disable_input_output_paths => true
-
 flutter_application_path = 'flutterapp/'
-framework_dir = File.join(flutter_application_path, '.ios', 'Flutter')
 
-engine_dir = File.join(framework_dir, 'engine')
-if !File.exist?(engine_dir)
-  # Copy the debug engine to have something to link against if the xcode backend script has not run yet.
-  debug_framework_dir = File.join(flutter_root(flutter_application_path), 'bin', 'cache', 'artifacts', 'engine', 'ios')
-  FileUtils.mkdir_p(engine_dir)
-  FileUtils.cp_r(File.join(debug_framework_dir, 'Flutter.framework'), engine_dir)
-  FileUtils.cp(File.join(debug_framework_dir, 'Flutter.podspec'), engine_dir)
-end
+load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
 
 target 'ios_add2app' do
-  eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)
+  install_all_flutter_pods(flutter_application_path, 'ios_add2app_flutter')
 end
 
 target 'ios_add2appTests' do
-  pod 'Flutter', :path => engine_dir
   inherit! :search_paths
+  install_flutter_engine_pod
   pod 'EarlGrey'
 end
diff --git a/dev/integration_tests/ios_add2app/ios_add2app.xcodeproj/project.pbxproj b/dev/integration_tests/ios_add2app/ios_add2app.xcodeproj/project.pbxproj
index 1859736..cc75d34 100644
--- a/dev/integration_tests/ios_add2app/ios_add2app.xcodeproj/project.pbxproj
+++ b/dev/integration_tests/ios_add2app/ios_add2app.xcodeproj/project.pbxproj
@@ -19,8 +19,8 @@
 		24E221E021A28B23008ADF09 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 24E221D721A28B23008ADF09 /* Launch Screen.storyboard */; };
 		24E221E221A28B36008ADF09 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 24E221E121A28B36008ADF09 /* Assets.xcassets */; };
 		24E7A1FE21A2AF26003A7FAD /* FlutterViewControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 24E7A1FD21A2AF26003A7FAD /* FlutterViewControllerTests.m */; };
+		4C21FEB4D629B570C74073D1 /* libPods-ios_add2appTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F90B141A9A8A680033E97924 /* libPods-ios_add2appTests.a */; };
 		8BDD73EC12DFA6CD2AC4EEDA /* EarlGrey.framework in EarlGrey Copy Files */ = {isa = PBXBuildFile; fileRef = DE1EF0F5719FBB45225A9EC3 /* EarlGrey.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
-		AE3B0A0EAE765C1EC16866BF /* libPods-ios_add2appTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 699900B7FF01268E98EC69ED /* libPods-ios_add2appTests.a */; };
 		DB9A200AFEB7AAE22B4E12E4 /* libPods-ios_add2app.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A318514E0307AE0FF0EFC76 /* libPods-ios_add2app.a */; };
 /* End PBXBuildFile section */
 
@@ -73,13 +73,15 @@
 		24E7A1FD21A2AF26003A7FAD /* FlutterViewControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FlutterViewControllerTests.m; sourceTree = "<group>"; };
 		36DA6BEAA5127D74EC6E3E13 /* Pods-ios_add2appUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios_add2appUITests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ios_add2appUITests/Pods-ios_add2appUITests.debug.xcconfig"; sourceTree = "<group>"; };
 		52EA0B290EEBC1D28BF6E803 /* libPods-ios_add2appUITests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ios_add2appUITests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
-		699900B7FF01268E98EC69ED /* libPods-ios_add2appTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ios_add2appTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		74266A4D94FA1C3157B5B560 /* Pods-ios_add2appTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios_add2appTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ios_add2appTests/Pods-ios_add2appTests.debug.xcconfig"; sourceTree = "<group>"; };
+		762E954C71D922C091619CD4 /* Pods-ios_add2app-ios_add2appTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios_add2app-ios_add2appTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ios_add2app-ios_add2appTests/Pods-ios_add2app-ios_add2appTests.debug.xcconfig"; sourceTree = "<group>"; };
 		7A318514E0307AE0FF0EFC76 /* libPods-ios_add2app.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ios_add2app.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		83017A33946358F55BAE24E1 /* Pods-ios_add2appTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios_add2appTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-ios_add2appTests/Pods-ios_add2appTests.release.xcconfig"; sourceTree = "<group>"; };
+		98A4882F55A3E107DFCB67D8 /* Pods-ios_add2app-ios_add2appTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios_add2app-ios_add2appTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-ios_add2app-ios_add2appTests/Pods-ios_add2app-ios_add2appTests.release.xcconfig"; sourceTree = "<group>"; };
 		A18D4CDABD6795975FD6B49F /* Pods-ios_add2app.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios_add2app.release.xcconfig"; path = "Pods/Target Support Files/Pods-ios_add2app/Pods-ios_add2app.release.xcconfig"; sourceTree = "<group>"; };
 		CCFA174387D083C931FB85D0 /* Pods-ios_add2app.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios_add2app.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ios_add2app/Pods-ios_add2app.debug.xcconfig"; sourceTree = "<group>"; };
 		DE1EF0F5719FBB45225A9EC3 /* EarlGrey.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = EarlGrey.framework; path = Pods/EarlGrey/EarlGrey/EarlGrey.framework; sourceTree = SOURCE_ROOT; };
+		F90B141A9A8A680033E97924 /* libPods-ios_add2appTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ios_add2appTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -88,7 +90,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				09C65652A2B48A21C94FF831 /* EarlGrey.framework in Frameworks */,
-				AE3B0A0EAE765C1EC16866BF /* libPods-ios_add2appTests.a in Frameworks */,
+				4C21FEB4D629B570C74073D1 /* libPods-ios_add2appTests.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -162,7 +164,7 @@
 				7A318514E0307AE0FF0EFC76 /* libPods-ios_add2app.a */,
 				DE1EF0F5719FBB45225A9EC3 /* EarlGrey.framework */,
 				52EA0B290EEBC1D28BF6E803 /* libPods-ios_add2appUITests.a */,
-				699900B7FF01268E98EC69ED /* libPods-ios_add2appTests.a */,
+				F90B141A9A8A680033E97924 /* libPods-ios_add2appTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -176,6 +178,8 @@
 				16D27B9BE77DC8804292A9A9 /* Pods-ios_add2appUITests.release.xcconfig */,
 				74266A4D94FA1C3157B5B560 /* Pods-ios_add2appTests.debug.xcconfig */,
 				83017A33946358F55BAE24E1 /* Pods-ios_add2appTests.release.xcconfig */,
+				762E954C71D922C091619CD4 /* Pods-ios_add2app-ios_add2appTests.debug.xcconfig */,
+				98A4882F55A3E107DFCB67D8 /* Pods-ios_add2app-ios_add2appTests.release.xcconfig */,
 			);
 			name = Pods;
 			sourceTree = "<group>";
@@ -209,7 +213,7 @@
 			buildConfigurationList = 24E221CB21A28A0C008ADF09 /* Build configuration list for PBXNativeTarget "ios_add2app" */;
 			buildPhases = (
 				4375EE880A727341E9C9A57D /* [CP] Check Pods Manifest.lock */,
-				24E221E321A28B53008ADF09 /* ShellScript */,
+				CE6831C2A33C6C5C84521FDE /* [CP-User] Run Flutter Build Script */,
 				24E221B121A28A0B008ADF09 /* Sources */,
 				24E221B221A28A0B008ADF09 /* Frameworks */,
 				24E221B321A28A0B008ADF09 /* Resources */,
@@ -281,23 +285,6 @@
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
-		24E221E321A28B53008ADF09 /* ShellScript */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-			);
-			outputFileListPaths = (
-			);
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed\n";
-		};
 		4375EE880A727341E9C9A57D /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -326,19 +313,33 @@
 			files = (
 			);
 			inputFileListPaths = (
-			);
-			inputPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-ios_add2app/Pods-ios_add2app-frameworks-${CONFIGURATION}-input-files.xcfilelist",
 			);
 			name = "[CP] Embed Pods Frameworks";
 			outputFileListPaths = (
-			);
-			outputPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-ios_add2app/Pods-ios_add2app-frameworks-${CONFIGURATION}-output-files.xcfilelist",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ios_add2app/Pods-ios_add2app-frameworks.sh\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ios_add2app/Pods-ios_add2app-frameworks.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
+		CE6831C2A33C6C5C84521FDE /* [CP-User] Run Flutter Build Script */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${SRCROOT}/flutterapp/.metadata",
+				"${SRCROOT}/flutterapp/.ios/Flutter/App.framework/App",
+				"${SRCROOT}/flutterapp/.ios/Flutter/engine/Flutter.framework/Flutter",
+				"${SRCROOT}/flutterapp/.ios/Flutter/flutter_export_environment.sh",
+			);
+			name = "[CP-User] Run Flutter Build Script";
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "source \"${SRCROOT}/flutterapp/.ios/Flutter/flutter_export_environment.sh\"\n\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/xcode_backend.sh build";
+		};
 		DE5CDCD8B3565EAB9F38F455 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -367,22 +368,15 @@
 			files = (
 			);
 			inputFileListPaths = (
-			);
-			inputPaths = (
-				"${SRCROOT}/Pods/Target Support Files/Pods-ios_add2appTests/Pods-ios_add2appTests-frameworks.sh",
-				"${PODS_ROOT}/../flutterapp/.ios/Flutter/engine/Flutter.framework",
-				"${PODS_ROOT}/EarlGrey/EarlGrey/EarlGrey.framework",
+				"${PODS_ROOT}/Target Support Files/Pods-ios_add2appTests/Pods-ios_add2appTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
 			);
 			name = "[CP] Embed Pods Frameworks";
 			outputFileListPaths = (
-			);
-			outputPaths = (
-				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
-				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/EarlGrey.framework",
+				"${PODS_ROOT}/Target Support Files/Pods-ios_add2appTests/Pods-ios_add2appTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ios_add2appTests/Pods-ios_add2appTests-frameworks.sh\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ios_add2appTests/Pods-ios_add2appTests-frameworks.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
 /* End PBXShellScriptBuildPhase section */
diff --git a/dev/integration_tests/ios_host_app/Config/Debug.xcconfig b/dev/integration_tests/ios_host_app/Config/Debug.xcconfig
deleted file mode 100644
index 3f48cde..0000000
--- a/dev/integration_tests/ios_host_app/Config/Debug.xcconfig
+++ /dev/null
@@ -1,2 +0,0 @@
-#include "Flutter.xcconfig"
-#include "../Pods/Target Support Files/Pods-Host/Pods-Host.debug.xcconfig"
diff --git a/dev/integration_tests/ios_host_app/Config/Flutter.xcconfig b/dev/integration_tests/ios_host_app/Config/Flutter.xcconfig
deleted file mode 100644
index ccfdd63..0000000
--- a/dev/integration_tests/ios_host_app/Config/Flutter.xcconfig
+++ /dev/null
@@ -1,2 +0,0 @@
-#include "../../hello/.ios/Flutter/Generated.xcconfig"
-ENABLE_BITCODE=NO
diff --git a/dev/integration_tests/ios_host_app/Config/Release.xcconfig b/dev/integration_tests/ios_host_app/Config/Release.xcconfig
deleted file mode 100644
index e9764d1..0000000
--- a/dev/integration_tests/ios_host_app/Config/Release.xcconfig
+++ /dev/null
@@ -1,3 +0,0 @@
-#include "Flutter.xcconfig"
-#include "../Pods/Target Support Files/Pods-Host/Pods-Host.release.xcconfig"
-FLUTTER_BUILD_MODE=release
diff --git a/dev/integration_tests/ios_host_app/Flutter/.gitkeep b/dev/integration_tests/ios_host_app/Flutter/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/dev/integration_tests/ios_host_app/Flutter/.gitkeep
+++ /dev/null
diff --git a/dev/integration_tests/ios_host_app/Host.xcodeproj/project.pbxproj b/dev/integration_tests/ios_host_app/Host.xcodeproj/project.pbxproj
index b7edca5..ce8eb70 100644
--- a/dev/integration_tests/ios_host_app/Host.xcodeproj/project.pbxproj
+++ b/dev/integration_tests/ios_host_app/Host.xcodeproj/project.pbxproj
@@ -13,26 +13,9 @@
 		74F9786E215AB9E9005A0F04 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 74F9786D215AB9E9005A0F04 /* Assets.xcassets */; };
 		74F97871215AB9E9005A0F04 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 74F9786F215AB9E9005A0F04 /* LaunchScreen.storyboard */; };
 		74F97874215AB9E9005A0F04 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 74F97873215AB9E9005A0F04 /* main.m */; };
-		74F978AF215AD6F9005A0F04 /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 74F978AC215AD6E8005A0F04 /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
-		DAEA7B95412864C7F3A4AE98 /* libPods-Host.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2499C53F0BD30E24745E2F6B /* libPods-Host.a */; };
 /* End PBXBuildFile section */
 
-/* Begin PBXCopyFilesBuildPhase section */
-		74F978A2215ACF9B005A0F04 /* Embed Frameworks */ = {
-			isa = PBXCopyFilesBuildPhase;
-			buildActionMask = 2147483647;
-			dstPath = "";
-			dstSubfolderSpec = 10;
-			files = (
-				74F978AF215AD6F9005A0F04 /* App.framework in Embed Frameworks */,
-			);
-			name = "Embed Frameworks";
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXCopyFilesBuildPhase section */
-
 /* Begin PBXFileReference section */
-		2499C53F0BD30E24745E2F6B /* libPods-Host.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Host.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		74F97861215AB9E8005A0F04 /* Host.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Host.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		74F97864215AB9E8005A0F04 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
 		74F97865215AB9E8005A0F04 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
@@ -43,10 +26,6 @@
 		74F97870215AB9E9005A0F04 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		74F97872215AB9E9005A0F04 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		74F97873215AB9E9005A0F04 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
-		74F9787B215ABA08005A0F04 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
-		74F9787C215ABA73005A0F04 /* Flutter.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Flutter.xcconfig; sourceTree = "<group>"; };
-		74F9787D215ABA9D005A0F04 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
-		74F978AC215AD6E8005A0F04 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = ../../hello/.ios/Flutter/App.framework; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -54,7 +33,6 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				DAEA7B95412864C7F3A4AE98 /* libPods-Host.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -64,8 +42,6 @@
 		74F97858215AB9E8005A0F04 = {
 			isa = PBXGroup;
 			children = (
-				74F978A3215AD111005A0F04 /* Flutter */,
-				74F9787A215AB9F3005A0F04 /* Config */,
 				74F97863215AB9E8005A0F04 /* Host */,
 				74F97862215AB9E8005A0F04 /* Products */,
 				74F9788B215AC328005A0F04 /* Frameworks */,
@@ -97,32 +73,13 @@
 			path = Host;
 			sourceTree = "<group>";
 		};
-		74F9787A215AB9F3005A0F04 /* Config */ = {
-			isa = PBXGroup;
-			children = (
-				74F9787C215ABA73005A0F04 /* Flutter.xcconfig */,
-				74F9787B215ABA08005A0F04 /* Debug.xcconfig */,
-				74F9787D215ABA9D005A0F04 /* Release.xcconfig */,
-			);
-			path = Config;
-			sourceTree = "<group>";
-		};
 		74F9788B215AC328005A0F04 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
-				2499C53F0BD30E24745E2F6B /* libPods-Host.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
 		};
-		74F978A3215AD111005A0F04 /* Flutter */ = {
-			isa = PBXGroup;
-			children = (
-				74F978AC215AD6E8005A0F04 /* App.framework */,
-			);
-			path = Flutter;
-			sourceTree = "<group>";
-		};
 		A4A9971F50C4EE357B74B6E0 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
@@ -137,13 +94,9 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 74F97877215AB9E9005A0F04 /* Build configuration list for PBXNativeTarget "Host" */;
 			buildPhases = (
-				EDACAC7378E52BD5BBDB34D5 /* [CP] Check Pods Manifest.lock */,
-				74F9787E215ABB1B005A0F04 /* Run Script */,
 				74F9785D215AB9E8005A0F04 /* Sources */,
 				74F9785E215AB9E8005A0F04 /* Frameworks */,
 				74F9785F215AB9E8005A0F04 /* Resources */,
-				53642CAE5328D86A50FDAD59 /* [CP] Embed Pods Frameworks */,
-				74F978A2215ACF9B005A0F04 /* Embed Frameworks */,
 			);
 			buildRules = (
 			);
@@ -199,56 +152,6 @@
 		};
 /* End PBXResourcesBuildPhase section */
 
-/* Begin PBXShellScriptBuildPhase section */
-		53642CAE5328D86A50FDAD59 /* [CP] Embed Pods Frameworks */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputPaths = (
-			);
-			name = "[CP] Embed Pods Frameworks";
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Host/Pods-Host-frameworks.sh\"\n";
-			showEnvVarsInLog = 0;
-		};
-		74F9787E215ABB1B005A0F04 /* Run Script */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputPaths = (
-			);
-			name = "Run Script";
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed\n";
-		};
-		EDACAC7378E52BD5BBDB34D5 /* [CP] Check Pods Manifest.lock */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-Host-checkManifestLockResult.txt",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
-			showEnvVarsInLog = 0;
-		};
-/* End PBXShellScriptBuildPhase section */
-
 /* Begin PBXSourcesBuildPhase section */
 		74F9785D215AB9E8005A0F04 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
@@ -317,6 +220,7 @@
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				COPY_PHASE_STRIP = NO;
 				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_BITCODE = NO;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				ENABLE_TESTABILITY = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -376,6 +280,7 @@
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				COPY_PHASE_STRIP = NO;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_BITCODE = NO;
 				ENABLE_NS_ASSERTIONS = NO;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -396,7 +301,6 @@
 		};
 		74F97878215AB9E9005A0F04 /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 74F9787B215ABA08005A0F04 /* Debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CODE_SIGN_STYLE = Automatic;
@@ -414,7 +318,6 @@
 		};
 		74F97879215AB9E9005A0F04 /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 74F9787D215ABA9D005A0F04 /* Release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CODE_SIGN_STYLE = Automatic;
diff --git a/dev/integration_tests/ios_host_app/Host/ViewController.m b/dev/integration_tests/ios_host_app/Host/ViewController.m
index 6101171..3eb47c0 100644
--- a/dev/integration_tests/ios_host_app/Host/ViewController.m
+++ b/dev/integration_tests/ios_host_app/Host/ViewController.m
@@ -1,6 +1,6 @@
 #import "ViewController.h"
-#import "Flutter/Flutter.h"
-#import "FlutterPluginRegistrant/GeneratedPluginRegistrant.h"
+#import <Flutter/Flutter.h>
+#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h>
 
 @implementation ViewController
 
diff --git a/dev/integration_tests/ios_host_app/Podfile b/dev/integration_tests/ios_host_app/Podfile
index 2b94908..a74eec9 100644
--- a/dev/integration_tests/ios_host_app/Podfile
+++ b/dev/integration_tests/ios_host_app/Podfile
@@ -1,9 +1,8 @@
 platform :ios, '9.0'
 
-# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
-install! 'cocoapods', :disable_input_output_paths => true
+flutter_application_path = '../hello'
+load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
 
 target 'Host' do
-  flutter_application_path = '../hello'
-  eval(File.read("#{flutter_application_path}/.ios/Flutter/podhelper.rb"))
+  install_all_flutter_pods flutter_application_path
 end
diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
index 7be0ef1..7234890 100644
--- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart
+++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
@@ -10,6 +10,7 @@
 import '../base/context.dart';
 import '../base/file_system.dart';
 import '../base/io.dart';
+import '../base/os.dart';
 import '../base/platform.dart';
 import '../base/process.dart';
 import '../base/process_manager.dart';
@@ -48,25 +49,96 @@
   bool useMacOSConfig = false,
   bool setSymroot = true,
 }) async {
+  final List<String> xcodeBuildSettings = _xcodeBuildSettingsLines(
+    project: project,
+    buildInfo: buildInfo,
+    targetOverride: targetOverride,
+    useMacOSConfig: useMacOSConfig,
+    setSymroot: setSymroot
+  );
+
+  _updateGeneratedXcodePropertiesFile(
+    project: project,
+    xcodeBuildSettings: xcodeBuildSettings,
+    useMacOSConfig: useMacOSConfig,
+  );
+
+  _updateGeneratedEnvironmentVariablesScript(
+    project: project,
+    xcodeBuildSettings: xcodeBuildSettings,
+    useMacOSConfig: useMacOSConfig,
+  );
+}
+
+/// Generate a xcconfig file to inherit FLUTTER_ build settings
+/// for Xcode targets that need them.
+/// See [XcodeBasedProject.generatedXcodePropertiesFile].
+void _updateGeneratedXcodePropertiesFile({
+  @required FlutterProject project,
+  @required List<String> xcodeBuildSettings,
+  bool useMacOSConfig = false,
+}) {
   final StringBuffer localsBuffer = StringBuffer();
 
   localsBuffer.writeln('// This is a generated file; do not edit or check into version control.');
+  xcodeBuildSettings.forEach(localsBuffer.writeln);
+  final File generatedXcodePropertiesFile = useMacOSConfig
+    ? project.macos.generatedXcodePropertiesFile
+    : project.ios.generatedXcodePropertiesFile;
+
+  generatedXcodePropertiesFile.createSync(recursive: true);
+  generatedXcodePropertiesFile.writeAsStringSync(localsBuffer.toString());
+}
+
+/// Generate a script to export all the FLUTTER_ environment variables needed
+/// as flags for Flutter tools.
+/// See [XcodeBasedProject.generatedEnvironmentVariableExportScript].
+void _updateGeneratedEnvironmentVariablesScript({
+  @required FlutterProject project,
+  @required List<String> xcodeBuildSettings,
+  bool useMacOSConfig = false,
+}) {
+  final StringBuffer localsBuffer = StringBuffer();
+
+  localsBuffer.writeln('#!/bin/sh');
+  localsBuffer.writeln('# This is a generated file; do not edit or check into version control.');
+  for (String line in xcodeBuildSettings) {
+    localsBuffer.writeln('export "$line"');
+  }
+
+  final File generatedModuleBuildPhaseScript = useMacOSConfig
+    ? project.macos.generatedEnvironmentVariableExportScript
+    : project.ios.generatedEnvironmentVariableExportScript;
+  generatedModuleBuildPhaseScript.createSync(recursive: true);
+  generatedModuleBuildPhaseScript.writeAsStringSync(localsBuffer.toString());
+  os.chmod(generatedModuleBuildPhaseScript, '755');
+}
+
+/// List of lines of build settings. Example: 'FLUTTER_BUILD_DIR=build'
+List<String> _xcodeBuildSettingsLines({
+  @required FlutterProject project,
+  @required BuildInfo buildInfo,
+  String targetOverride,
+  bool useMacOSConfig = false,
+  bool setSymroot = true,
+}) {
+  final List<String> xcodeBuildSettings = <String>[];
 
   final String flutterRoot = fs.path.normalize(Cache.flutterRoot);
-  localsBuffer.writeln('FLUTTER_ROOT=$flutterRoot');
+  xcodeBuildSettings.add('FLUTTER_ROOT=$flutterRoot');
 
   // This holds because requiresProjectRoot is true for this command
-  localsBuffer.writeln('FLUTTER_APPLICATION_PATH=${fs.path.normalize(project.directory.path)}');
+  xcodeBuildSettings.add('FLUTTER_APPLICATION_PATH=${fs.path.normalize(project.directory.path)}');
 
   // Relative to FLUTTER_APPLICATION_PATH, which is [Directory.current].
   if (targetOverride != null)
-    localsBuffer.writeln('FLUTTER_TARGET=$targetOverride');
+    xcodeBuildSettings.add('FLUTTER_TARGET=$targetOverride');
 
   // The build outputs directory, relative to FLUTTER_APPLICATION_PATH.
-  localsBuffer.writeln('FLUTTER_BUILD_DIR=${getBuildDirectory()}');
+  xcodeBuildSettings.add('FLUTTER_BUILD_DIR=${getBuildDirectory()}');
 
   if (setSymroot) {
-    localsBuffer.writeln('SYMROOT=\${SOURCE_ROOT}/../${getIosBuildDirectory()}');
+    xcodeBuildSettings.add('SYMROOT=\${SOURCE_ROOT}/../${getIosBuildDirectory()}');
   }
 
   if (!project.isModule) {
@@ -75,26 +147,26 @@
     // logic to derive it from FLUTTER_ROOT and FLUTTER_BUILD_MODE.
     // However, this is necessary for regular projects using Cocoapods.
     final String frameworkDir = useMacOSConfig
-        ? flutterMacOSFrameworkDir(buildInfo.mode)
-        : flutterFrameworkDir(buildInfo.mode);
-    localsBuffer.writeln('FLUTTER_FRAMEWORK_DIR=$frameworkDir');
+      ? flutterMacOSFrameworkDir(buildInfo.mode)
+      : flutterFrameworkDir(buildInfo.mode);
+    xcodeBuildSettings.add('FLUTTER_FRAMEWORK_DIR=$frameworkDir');
   }
 
   final String buildName = validatedBuildNameForPlatform(TargetPlatform.ios, buildInfo?.buildName ?? project.manifest.buildName);
   if (buildName != null) {
-    localsBuffer.writeln('FLUTTER_BUILD_NAME=$buildName');
+    xcodeBuildSettings.add('FLUTTER_BUILD_NAME=$buildName');
   }
 
   final String buildNumber = validatedBuildNumberForPlatform(TargetPlatform.ios, buildInfo?.buildNumber ?? project.manifest.buildNumber);
   if (buildNumber != null) {
-    localsBuffer.writeln('FLUTTER_BUILD_NUMBER=$buildNumber');
+    xcodeBuildSettings.add('FLUTTER_BUILD_NUMBER=$buildNumber');
   }
 
   if (artifacts is LocalEngineArtifacts) {
     final LocalEngineArtifacts localEngineArtifacts = artifacts;
     final String engineOutPath = localEngineArtifacts.engineOutPath;
-    localsBuffer.writeln('FLUTTER_ENGINE=${fs.path.dirname(fs.path.dirname(engineOutPath))}');
-    localsBuffer.writeln('LOCAL_ENGINE=${fs.path.basename(engineOutPath)}');
+    xcodeBuildSettings.add('FLUTTER_ENGINE=${fs.path.dirname(fs.path.dirname(engineOutPath))}');
+    xcodeBuildSettings.add('LOCAL_ENGINE=${fs.path.basename(engineOutPath)}');
 
     // Tell Xcode not to build universal binaries for local engines, which are
     // single-architecture.
@@ -106,19 +178,15 @@
     // Skip this step for macOS builds.
     if (!useMacOSConfig) {
       final String arch = engineOutPath.endsWith('_arm') ? 'armv7' : 'arm64';
-      localsBuffer.writeln('ARCHS=$arch');
+      xcodeBuildSettings.add('ARCHS=$arch');
     }
   }
 
   if (buildInfo.trackWidgetCreation) {
-    localsBuffer.writeln('TRACK_WIDGET_CREATION=true');
+    xcodeBuildSettings.add('TRACK_WIDGET_CREATION=true');
   }
 
-  final File generatedXcodePropertiesFile = useMacOSConfig
-      ? project.macos.generatedXcodePropertiesFile
-      : project.ios.generatedXcodePropertiesFile;
-  generatedXcodePropertiesFile.createSync(recursive: true);
-  generatedXcodePropertiesFile.writeAsStringSync(localsBuffer.toString());
+  return xcodeBuildSettings;
 }
 
 XcodeProjectInterpreter get xcodeProjectInterpreter => context.get<XcodeProjectInterpreter>();
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
index ece0846..d9d7a3e 100644
--- a/packages/flutter_tools/lib/src/project.dart
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -260,6 +260,12 @@
   /// The Flutter-managed Xcode config file for [mode].
   File xcodeConfigFor(String mode);
 
+  /// The script that exports environment variables needed for Flutter tools.
+  /// Can be run first in a Xcode Script build phase to make FLUTTER_ROOT,
+  /// LOCAL_ENGINE, and other Flutter variables available to any flutter
+  /// tooling (`flutter build`, etc) to convert into flags.
+  File get generatedEnvironmentVariableExportScript;
+
   /// The CocoaPods 'Podfile'.
   File get podfile;
 
@@ -318,6 +324,9 @@
   File xcodeConfigFor(String mode) => _flutterLibRoot.childDirectory('Flutter').childFile('$mode.xcconfig');
 
   @override
+  File get generatedEnvironmentVariableExportScript => _flutterLibRoot.childDirectory('Flutter').childFile('flutter_export_environment.sh');
+
+  @override
   File get podfile => hostAppRoot.childFile('Podfile');
 
   @override
@@ -662,6 +671,9 @@
   File xcodeConfigFor(String mode) => managedDirectory.childFile('Flutter-$mode.xcconfig');
 
   @override
+  File get generatedEnvironmentVariableExportScript => managedDirectory.childFile('flutter_export_environment.sh');
+
+  @override
   File get podfile => _macOSDirectory.childFile('Podfile');
 
   @override
diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Podfile.copy.tmpl b/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Podfile.copy.tmpl
index b2b9a40..1e70150 100644
--- a/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Podfile.copy.tmpl
+++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Podfile.copy.tmpl
@@ -1,8 +1,18 @@
 platform :ios, '8.0'
 
+flutter_application_path = '../'
+load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
+
 target 'Runner' do
-  flutter_application_path = '../'
-  eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)
+  install_flutter_plugin_pods flutter_application_path
+end
+
+post_install do |installer|
+  installer.pods_project.targets.each do |target|
+    target.build_configurations.each do |config|
+      config.build_settings['ENABLE_BITCODE'] = 'NO'
+    end
+  end
 end
 
 # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
diff --git a/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/podhelper.rb b/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/podhelper.rb
index 3956697..f833b85 100644
--- a/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/podhelper.rb
+++ b/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/podhelper.rb
@@ -1,75 +1,137 @@
-def parse_KV_file(file, separator='=')
-    file_abs_path = File.expand_path(file)
-    if !File.exists? file_abs_path
-        return [];
-    end
-    pods_array = []
-    skip_line_start_symbols = ["#", "/"]
-    File.foreach(file_abs_path) { |line|
-        next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
-        plugin = line.split(pattern=separator)
-        if plugin.length == 2
-            podname = plugin[0].strip()
-            path = plugin[1].strip()
-            podpath = File.expand_path("#{path}", file_abs_path)
-            pods_array.push({:name => podname, :path => podpath});
-         else
-            puts "Invalid plugin specification: #{line}"
-        end
-    }
-    return pods_array
+# Install pods needed to embed Flutter application, Flutter engine, and plugins
+# from the host application Podfile.
+#
+# @example
+#   target 'MyApp' do
+#     install_all_flutter_pods 'my_flutter'
+#   end
+# @param [String] flutter_application_path Path of the root directory of the Flutter module.
+#                                          Optional, defaults to two levels up from the directory of this script.
+#                                          MyApp/my_flutter/.ios/Flutter/../..
+# @param [String] flutter_application_name Name of the Flutter module. Optional, defaults to module directory name.
+def install_all_flutter_pods(flutter_application_path = nil, flutter_application_name = nil)
+  flutter_application_name ||= File.basename(File.expand_path(flutter_application_path))
+  flutter_application_path ||= File.join('..', '..')
+  install_flutter_engine_pod
+  install_flutter_plugin_pods(flutter_application_path)
+  install_flutter_application_pod(flutter_application_path, flutter_application_name)
 end
 
-def flutter_root(f)
-    generated_xcode_build_settings = parse_KV_file(File.join(f, File.join('.ios', 'Flutter', 'Generated.xcconfig')))
-    if generated_xcode_build_settings.empty?
-        puts "Generated.xcconfig must exist. Make sure `flutter pub get` is executed in #{f}."
-        exit
-    end
-    generated_xcode_build_settings.map { |p|
-        if p[:name] == 'FLUTTER_ROOT'
-            return p[:path]
-        end
-    }
-end
-
-# If this wasn't specified, assume it's two levels up from the directory of this script.
-flutter_application_path ||= File.join(__dir__, '..', '..')
-framework_dir = File.join(flutter_application_path, '.ios', 'Flutter')
-
-engine_dir = File.join(framework_dir, 'engine')
-if !File.exist?(engine_dir)
+# Install Flutter engine pod.
+#
+# @example
+#   target 'MyApp' do
+#     install_flutter_engine_pod
+#   end
+def install_flutter_engine_pod
+  engine_dir = File.join(__dir__, 'engine')
+  if !File.exist?(engine_dir)
     # Copy the debug engine to have something to link against if the xcode backend script has not run yet.
-    debug_framework_dir = File.join(flutter_root(flutter_application_path), 'bin', 'cache', 'artifacts', 'engine', 'ios')
+    # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
+    debug_framework_dir = File.join(flutter_root, 'bin', 'cache', 'artifacts', 'engine', 'ios')
     FileUtils.mkdir_p(engine_dir)
     FileUtils.cp_r(File.join(debug_framework_dir, 'Flutter.framework'), engine_dir)
     FileUtils.cp(File.join(debug_framework_dir, 'Flutter.podspec'), engine_dir)
+  end
+
+  pod 'Flutter', :path => engine_dir, :inhibit_warnings => true
 end
 
-pod 'Flutter', :path => engine_dir
-pod 'FlutterPluginRegistrant', :path => File.join(framework_dir, 'FlutterPluginRegistrant')
+# Install Flutter plugin pods.
+#
+# @example
+#   target 'MyApp' do
+#     install_flutter_plugin_pods 'my_flutter'
+#   end
+# @param [String] flutter_application_path Path of the root directory of the Flutter module.
+#                                          Optional, defaults to two levels up from the directory of this script.
+#                                          MyApp/my_flutter/.ios/Flutter/../..
+def install_flutter_plugin_pods(flutter_application_path)
+  flutter_application_path ||= File.join('..', '..')
+  pod 'FlutterPluginRegistrant', :path => File.join(__dir__, 'FlutterPluginRegistrant'), :inhibit_warnings => true
 
-symlinks_dir = File.join(framework_dir, '.symlinks')
-FileUtils.mkdir_p(symlinks_dir)
-plugin_pods = parse_KV_file(File.join(flutter_application_path, '.flutter-plugins'))
-plugin_pods.map { |r|
+  symlinks_dir = File.join(__dir__, '.symlinks')
+  FileUtils.mkdir_p(symlinks_dir)
+  plugin_pods = parse_KV_file(File.join(flutter_application_path, '.flutter-plugins'))
+  plugin_pods.map do |r|
     symlink = File.join(symlinks_dir, r[:name])
     FileUtils.rm_f(symlink)
     File.symlink(r[:path], symlink)
-    pod r[:name], :path => File.join(symlink, 'ios')
-}
+    pod r[:name], :path => File.join(symlink, 'ios'), :inhibit_warnings => true
+  end
+end
 
-# Ensure that ENABLE_BITCODE is set to NO, add a #include to Generated.xcconfig, and
-# add a run script to the Build Phases.
-post_install do |installer|
-    installer.pods_project.targets.each do |target|
-        target.build_configurations.each do |config|
-            config.build_settings['ENABLE_BITCODE'] = 'NO'
-            next if  config.base_configuration_reference == nil
-            xcconfig_path = config.base_configuration_reference.real_path
-            File.open(xcconfig_path, 'a+') do |file|
-                file.puts "#include \"#{File.realpath(File.join(framework_dir, 'Generated.xcconfig'))}\""
-            end
-        end
+# Install Flutter application pod.
+#
+# @example
+#   target 'MyApp' do
+#     install_flutter_application_pod '../flutter_settings_repository', 'settings_module'
+#   end
+# @param [String] flutter_application_path Path of the root directory of the Flutter module.
+#                                          Optional, defaults to two levels up from the directory of this script.
+#                                          MyApp/my_flutter/.ios/Flutter/../..
+def install_flutter_application_pod(flutter_application_path, flutter_application_name)
+  app_framework_dir = File.join(__dir__, 'App.framework')
+  app_framework_dylib = File.join(app_framework_dir, 'App')
+  if !File.exist?(app_framework_dylib)
+    # Fake an App.framework to have something to link against if the xcode backend script has not run yet.
+    # CocoaPods will not embed the framework on pod install (before any build phases can run) if the dylib does not exist.
+    # Create a dummy dylib.
+    FileUtils.mkdir_p(app_framework_dir)
+    `echo "static const int Moo = 88;" | xcrun clang -x c -dynamiclib -o "#{app_framework_dylib}" -`
+  end
+
+  pod flutter_application_name, :path => __dir__, :inhibit_warnings => true
+
+  # Use relative paths for script phase paths since these strings will likely be checked into source controls.
+  # Process will be run from project directory.
+  current_directory_pathname = Pathname.new __dir__.to_s
+  project_directory_pathname = Pathname.new Dir.pwd
+  relative = current_directory_pathname.relative_path_from project_directory_pathname
+
+  flutter_export_environment_path = File.join('${SRCROOT}', relative, 'flutter_export_environment.sh');
+  script_phase :name => 'Run Flutter Build Script',
+    :script => "source \"#{flutter_export_environment_path}\"\n\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/xcode_backend.sh build",
+    :input_files => [
+      File.join('${SRCROOT}', flutter_application_path, '.metadata'),
+      File.join('${SRCROOT}', relative, 'App.framework', 'App'),
+      File.join('${SRCROOT}', relative, 'engine', 'Flutter.framework', 'Flutter'),
+      flutter_export_environment_path
+    ],
+    :execution_position => :before_compile
+end
+
+def parse_KV_file(file, separator='=')
+  file_abs_path = File.expand_path(file)
+  if !File.exists? file_abs_path
+    return [];
+  end
+  pods_array = []
+  skip_line_start_symbols = ["#", "/"]
+  File.foreach(file_abs_path) { |line|
+    next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
+    plugin = line.split(pattern=separator)
+    if plugin.length == 2
+      podname = plugin[0].strip()
+      path = plugin[1].strip()
+      podpath = File.expand_path("#{path}", file_abs_path)
+      pods_array.push({:name => podname, :path => podpath});
+     else
+      puts "Invalid plugin specification: #{line}"
     end
+  }
+  return pods_array
+end
+
+def flutter_root
+  generated_xcode_build_settings = parse_KV_file(File.join(__dir__, 'Generated.xcconfig'))
+  if generated_xcode_build_settings.empty?
+    puts "Generated.xcconfig must exist. Make sure `flutter pub get` is executed in the Flutter module."
+    exit
+  end
+  generated_xcode_build_settings.map { |p|
+    if p[:name] == 'FLUTTER_ROOT'
+      return p[:path]
+    end
+  }
 end
diff --git a/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/projectName.podspec.tmpl b/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/projectName.podspec.tmpl
new file mode 100644
index 0000000..2fbe501
--- /dev/null
+++ b/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/projectName.podspec.tmpl
@@ -0,0 +1,13 @@
+Pod::Spec.new do |s|
+  s.name                  = '{{projectName}}'
+  s.version               = '0.0.1'
+  s.summary               = 'Flutter module'
+  s.description           = 'Flutter module - {{projectName}}'
+  s.homepage              = 'https://flutter.dev'
+  s.license               = { :type => 'BSD' }
+  s.author                = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
+  s.source                = { :path => '.' }
+  s.ios.deployment_target = '8.0'
+  s.vendored_frameworks   = 'App.framework'
+  s.dependency 'Flutter'
+end
diff --git a/packages/flutter_tools/test/general.shard/commands/create_test.dart b/packages/flutter_tools/test/general.shard/commands/create_test.dart
index 7a8c087..98babb7 100644
--- a/packages/flutter_tools/test/general.shard/commands/create_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/create_test.dart
@@ -100,6 +100,7 @@
       '.android/app/',
       '.gitignore',
       '.ios/Flutter',
+      '.ios/Flutter/flutter_project.podspec',
       '.metadata',
       'lib/main.dart',
       'pubspec.yaml',
@@ -545,6 +546,23 @@
     expect(xcodeConfig, contains('FLUTTER_ROOT='));
     expect(xcodeConfig, contains('FLUTTER_APPLICATION_PATH='));
     expect(xcodeConfig, contains('FLUTTER_TARGET='));
+
+    // Generated export environment variables script
+    final String buildPhaseScriptPath = fs.path.join('.ios', 'Flutter', 'flutter_export_environment.sh');
+    expectExists(buildPhaseScriptPath);
+    final File buildPhaseScriptFile = fs.file(fs.path.join(projectDir.path, buildPhaseScriptPath));
+    final String buildPhaseScript = buildPhaseScriptFile.readAsStringSync();
+    expect(buildPhaseScript, contains('FLUTTER_ROOT='));
+    expect(buildPhaseScript, contains('FLUTTER_APPLICATION_PATH='));
+    expect(buildPhaseScript, contains('FLUTTER_TARGET='));
+
+    // Generated podspec
+    final String podspecPath = fs.path.join('.ios', 'Flutter', 'flutter_project.podspec');
+    expectExists(podspecPath);
+    final File podspecFile = fs.file(fs.path.join(projectDir.path, podspecPath));
+    final String podspec = podspecFile.readAsStringSync();
+    expect(podspec, contains('Flutter module - flutter_project'));
+
     // App identification
     final String xcodeProjectPath = fs.path.join('.ios', 'Runner.xcodeproj', 'project.pbxproj');
     expectExists(xcodeProjectPath);
diff --git a/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart b/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
index b560538..ec81949 100644
--- a/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
@@ -304,6 +304,12 @@
 
       final String contents = config.readAsStringSync();
       expect(contents.contains('ARCHS=armv7'), isTrue);
+
+      final File buildPhaseScript = fs.file('path/to/project/ios/Flutter/flutter_export_environment.sh');
+      expect(buildPhaseScript.existsSync(), isTrue);
+
+      final String buildPhaseScriptContents = buildPhaseScript.readAsStringSync();
+      expect(buildPhaseScriptContents.contains('ARCHS=armv7'), isTrue);
     });
 
     testUsingOsxContext('sets TRACK_WIDGET_CREATION=true when trackWidgetCreation is true', () async {
@@ -322,6 +328,12 @@
 
       final String contents = config.readAsStringSync();
       expect(contents.contains('TRACK_WIDGET_CREATION=true'), isTrue);
+
+      final File buildPhaseScript = fs.file('path/to/project/ios/Flutter/flutter_export_environment.sh');
+      expect(buildPhaseScript.existsSync(), isTrue);
+
+      final String buildPhaseScriptContents = buildPhaseScript.readAsStringSync();
+      expect(buildPhaseScriptContents.contains('TRACK_WIDGET_CREATION=true'), isTrue);
     });
 
     testUsingOsxContext('does not set TRACK_WIDGET_CREATION when trackWidgetCreation is false', () async {
@@ -340,6 +352,12 @@
 
       final String contents = config.readAsStringSync();
       expect(contents.contains('TRACK_WIDGET_CREATION=true'), isFalse);
+
+      final File buildPhaseScript = fs.file('path/to/project/ios/Flutter/flutter_export_environment.sh');
+      expect(buildPhaseScript.existsSync(), isTrue);
+
+      final String buildPhaseScriptContents = buildPhaseScript.readAsStringSync();
+      expect(buildPhaseScriptContents.contains('TRACK_WIDGET_CREATION=true'), isFalse);
     });
 
     testUsingOsxContext('sets ARCHS=armv7 when armv7 local engine is set', () async {
diff --git a/packages/flutter_tools/test/general.shard/project_test.dart b/packages/flutter_tools/test/general.shard/project_test.dart
index dd800b4..f5b26d1 100644
--- a/packages/flutter_tools/test/general.shard/project_test.dart
+++ b/packages/flutter_tools/test/general.shard/project_test.dart
@@ -200,6 +200,8 @@
         await project.ensureReadyForPlatformSpecificTooling();
         final Directory flutter = project.ios.hostAppRoot.childDirectory('Flutter');
         expectExists(flutter.childFile('podhelper.rb'));
+        expectExists(flutter.childFile('flutter_export_environment.sh'));
+        expectExists(flutter.childFile('${project.manifest.appName}.podspec'));
         expectExists(flutter.childFile('Generated.xcconfig'));
         final Directory pluginRegistrantClasses = flutter
             .childDirectory('FlutterPluginRegistrant')