Support for app flavors in flutter tooling, #11676 retake (#11734)

diff --git a/dev/devicelab/bin/tasks/flavors_test.dart b/dev/devicelab/bin/tasks/flavors_test.dart
new file mode 100644
index 0000000..b5618a7
--- /dev/null
+++ b/dev/devicelab/bin/tasks/flavors_test.dart
@@ -0,0 +1,14 @@
+// Copyright 2017 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:async';
+
+import 'package:flutter_devicelab/framework/adb.dart';
+import 'package:flutter_devicelab/framework/framework.dart';
+import 'package:flutter_devicelab/tasks/integration_tests.dart';
+
+Future<Null> main() async {
+  deviceOperatingSystem = DeviceOperatingSystem.android;
+  await task(createFlavorsTest());
+}
diff --git a/dev/devicelab/bin/tasks/flavors_test_ios.dart b/dev/devicelab/bin/tasks/flavors_test_ios.dart
new file mode 100644
index 0000000..7bb864c
--- /dev/null
+++ b/dev/devicelab/bin/tasks/flavors_test_ios.dart
@@ -0,0 +1,14 @@
+// Copyright 2017 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:async';
+
+import 'package:flutter_devicelab/framework/adb.dart';
+import 'package:flutter_devicelab/framework/framework.dart';
+import 'package:flutter_devicelab/tasks/integration_tests.dart';
+
+Future<Null> main() async {
+  deviceOperatingSystem = DeviceOperatingSystem.ios;
+  await task(createFlavorsTest());
+}
diff --git a/dev/devicelab/bin/tasks/flavors_test_win.dart b/dev/devicelab/bin/tasks/flavors_test_win.dart
new file mode 100644
index 0000000..b5618a7
--- /dev/null
+++ b/dev/devicelab/bin/tasks/flavors_test_win.dart
@@ -0,0 +1,14 @@
+// Copyright 2017 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:async';
+
+import 'package:flutter_devicelab/framework/adb.dart';
+import 'package:flutter_devicelab/framework/framework.dart';
+import 'package:flutter_devicelab/tasks/integration_tests.dart';
+
+Future<Null> main() async {
+  deviceOperatingSystem = DeviceOperatingSystem.android;
+  await task(createFlavorsTest());
+}
diff --git a/dev/devicelab/lib/tasks/integration_tests.dart b/dev/devicelab/lib/tasks/integration_tests.dart
index b5aea30..f612326 100644
--- a/dev/devicelab/lib/tasks/integration_tests.dart
+++ b/dev/devicelab/lib/tasks/integration_tests.dart
@@ -23,6 +23,14 @@
   );
 }
 
+TaskFunction createFlavorsTest() {
+  return new DriverTest(
+    '${flutterDirectory.path}/dev/integration_tests/flavors',
+    'lib/main.dart',
+    extraOptions: <String>['--flavor', 'paid']
+  );
+}
+
 TaskFunction createPlatformChannelSampleTest() {
   return new DriverTest(
     '${flutterDirectory.path}/examples/platform_channel',
@@ -32,10 +40,16 @@
 
 class DriverTest {
 
-  DriverTest(this.testDirectory, this.testTarget);
+  DriverTest(
+    this.testDirectory,
+    this.testTarget, {
+      this.extraOptions = const <String>[],
+    }
+  );
 
   final String testDirectory;
   final String testTarget;
+  final List<String> extraOptions;
 
   Future<TaskResult> call() {
     return inDirectory(testDirectory, () async {
@@ -46,14 +60,15 @@
 
       if (deviceOperatingSystem == DeviceOperatingSystem.ios)
         await prepareProvisioningCertificates(testDirectory);
-
-      await flutter('drive', options: <String>[
+      final List<String> options = <String>[
         '-v',
         '-t',
         testTarget,
         '-d',
         deviceId,
-      ]);
+      ];
+      options.addAll(extraOptions);
+      await flutter('drive', options: options);
 
       return new TaskResult.success(null);
     });
diff --git a/dev/devicelab/manifest.yaml b/dev/devicelab/manifest.yaml
index c06e442..12a4fe5 100644
--- a/dev/devicelab/manifest.yaml
+++ b/dev/devicelab/manifest.yaml
@@ -59,6 +59,13 @@
     stage: devicelab
     required_agent_capabilities: ["has-android-device"]
 
+  flavors_test:
+    description: >
+      Checks that flavored builds work on Android.
+    stage: devicelab
+    required_agent_capabilities: ["has-android-device"]
+    flaky: true
+
   channels_integration_test:
     description: >
       Checks that platform channels work on Android.
@@ -161,6 +168,13 @@
 
   # iOS on-device tests
 
+  flavors_test_ios:
+    description: >
+      Checks that flavored builds work on iOS.
+    stage: devicelab_ios
+    required_agent_capabilities: ["has-ios-device"]
+    flaky: true
+
   channels_integration_test_ios:
     description: >
       Checks that platform channels work on iOS.
@@ -242,6 +256,13 @@
 
   # Tests running on Windows host
 
+  flavors_test_win:
+    description: >
+      Checks that flavored builds work on Windows.
+    stage: devicelab_win
+    required_agent_capabilities: ["windows"]
+    flaky: true
+
   channels_integration_test_win:
     description: >
       Checks that platform channels work when app is launched from Windows.
diff --git a/dev/integration_tests/flavors/.gitignore b/dev/integration_tests/flavors/.gitignore
new file mode 100644
index 0000000..eb15c3d
--- /dev/null
+++ b/dev/integration_tests/flavors/.gitignore
@@ -0,0 +1,10 @@
+.DS_Store
+.atom/
+.idea
+.packages
+.pub/
+build/
+ios/.generated/
+packages
+pubspec.lock
+.flutter-plugins
diff --git a/dev/integration_tests/flavors/README.md b/dev/integration_tests/flavors/README.md
new file mode 100644
index 0000000..e4afcd4
--- /dev/null
+++ b/dev/integration_tests/flavors/README.md
@@ -0,0 +1,3 @@
+# flavors
+
+Integration test of build flavors (Android product flavors, Xcode schemes).
diff --git a/dev/integration_tests/flavors/android/.gitignore b/dev/integration_tests/flavors/android/.gitignore
new file mode 100644
index 0000000..1658458
--- /dev/null
+++ b/dev/integration_tests/flavors/android/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+GeneratedPluginRegistrant.java
diff --git a/dev/integration_tests/flavors/android/app/build.gradle b/dev/integration_tests/flavors/android/app/build.gradle
new file mode 100644
index 0000000..60a8db8
--- /dev/null
+++ b/dev/integration_tests/flavors/android/app/build.gradle
@@ -0,0 +1,62 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+    localPropertiesFile.withInputStream { stream ->
+        localProperties.load(stream)
+    }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+apply plugin: 'com.android.application'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+    compileSdkVersion 25
+    buildToolsVersion '25.0.3'
+
+    lintOptions {
+        disable 'InvalidPackage'
+    }
+
+    defaultConfig {
+        applicationId "com.yourcompany.flavors"
+        minSdkVersion 16
+        targetSdkVersion 25
+        versionCode 1
+        versionName "1.0"
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        release {
+            signingConfig signingConfigs.debug
+        }
+    }
+    
+    aaptOptions {
+        // TODO(goderbauer): remove when https://github.com/flutter/flutter/issues/8986 is resolved.
+        if(System.getenv("FLUTTER_CI_WIN")) {
+            println "AAPT cruncher disabled when running on Win CI."
+            cruncherEnabled false
+        }
+    }
+
+    productFlavors {
+        free {}
+        paid {}
+    }
+}
+
+flutter {
+    source '../..'
+}
+
+dependencies {
+    androidTestCompile 'com.android.support:support-annotations:25.4.0'
+    androidTestCompile 'com.android.support.test:runner:0.5'
+    androidTestCompile 'com.android.support.test:rules:0.5'
+}
diff --git a/dev/integration_tests/flavors/android/app/src/main/AndroidManifest.xml b/dev/integration_tests/flavors/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..56ee31f
--- /dev/null
+++ b/dev/integration_tests/flavors/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.yourcompany.flavors">
+
+    <uses-permission android:name="android.permission.INTERNET"/>
+
+    <application
+        android:name="io.flutter.app.FlutterApplication"
+        android:label="flavors">
+        <activity
+            android:name=".MainActivity"
+            android:launchMode="singleTop"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
+            android:hardwareAccelerated="true"
+            android:windowSoftInputMode="adjustResize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/dev/integration_tests/flavors/android/app/src/main/java/com/yourcompany/flavors/MainActivity.java b/dev/integration_tests/flavors/android/app/src/main/java/com/yourcompany/flavors/MainActivity.java
new file mode 100644
index 0000000..f306ca9
--- /dev/null
+++ b/dev/integration_tests/flavors/android/app/src/main/java/com/yourcompany/flavors/MainActivity.java
@@ -0,0 +1,22 @@
+package com.yourcompany.flavors;
+
+import android.os.Bundle;
+
+import io.flutter.app.FlutterActivity;
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugins.GeneratedPluginRegistrant;
+
+public class MainActivity extends FlutterActivity {
+  @Override
+  protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    GeneratedPluginRegistrant.registerWith(this);
+    new MethodChannel(getFlutterView(), "flavor").setMethodCallHandler(new MethodChannel.MethodCallHandler() {
+      @Override
+      public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
+        result.success(BuildConfig.FLAVOR);
+      }
+    });
+  }
+}
diff --git a/dev/integration_tests/flavors/android/build.gradle b/dev/integration_tests/flavors/android/build.gradle
new file mode 100644
index 0000000..77cbd09
--- /dev/null
+++ b/dev/integration_tests/flavors/android/build.gradle
@@ -0,0 +1,31 @@
+buildscript {
+    repositories {
+        jcenter()
+        maven {
+            url "https://maven.google.com"
+        }
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:2.3.3'
+    }
+}
+
+allprojects {
+    repositories {
+        jcenter()
+        maven {
+            url "https://maven.google.com"
+        }
+    }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+    project.buildDir = "${rootProject.buildDir}/${project.name}"
+    project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}
diff --git a/dev/integration_tests/flavors/android/gradle.properties b/dev/integration_tests/flavors/android/gradle.properties
new file mode 100644
index 0000000..8bd86f6
--- /dev/null
+++ b/dev/integration_tests/flavors/android/gradle.properties
@@ -0,0 +1 @@
+org.gradle.jvmargs=-Xmx1536M
diff --git a/dev/integration_tests/flavors/android/gradle/wrapper/gradle-wrapper.properties b/dev/integration_tests/flavors/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..45e7f14
--- /dev/null
+++ b/dev/integration_tests/flavors/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/dev/integration_tests/flavors/android/settings.gradle b/dev/integration_tests/flavors/android/settings.gradle
new file mode 100644
index 0000000..115da6c
--- /dev/null
+++ b/dev/integration_tests/flavors/android/settings.gradle
@@ -0,0 +1,15 @@
+include ':app'
+
+def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+
+def plugins = new Properties()
+def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
+if (pluginsFile.exists()) {
+    pluginsFile.withInputStream { stream -> plugins.load(stream) }
+}
+
+plugins.each { name, path ->
+    def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
+    include ":$name"
+    project(":$name").projectDir = pluginDirectory
+}
diff --git a/dev/integration_tests/flavors/ios/.gitignore b/dev/integration_tests/flavors/ios/.gitignore
new file mode 100644
index 0000000..38864ee
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/.gitignore
@@ -0,0 +1,41 @@
+.idea/
+.vagrant/
+.sconsign.dblite
+.svn/
+
+.DS_Store
+*.swp
+profile
+
+DerivedData/
+build/
+GeneratedPluginRegistrant.h
+GeneratedPluginRegistrant.m
+
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspectivev3
+
+!default.pbxuser
+!default.mode1v3
+!default.mode2v3
+!default.perspectivev3
+
+xcuserdata
+
+*.moved-aside
+
+*.pyc
+*sync/
+Icon?
+.tags*
+
+/Flutter/app.flx
+/Flutter/app.zip
+/Flutter/App.framework
+/Flutter/Flutter.framework
+/Flutter/Generated.xcconfig
+/ServiceDefinitions.json
+
+Pods/
diff --git a/dev/integration_tests/flavors/ios/Flutter/AppFrameworkInfo.plist b/dev/integration_tests/flavors/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..6c2de80
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>App</string>
+  <key>CFBundleIdentifier</key>
+  <string>io.flutter.flutter.app</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>App</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>1.0</string>
+  <key>UIRequiredDeviceCapabilities</key>
+  <array>
+    <string>arm64</string>
+  </array>
+  <key>MinimumOSVersion</key>
+  <string>8.0</string>
+</dict>
+</plist>
diff --git a/dev/integration_tests/flavors/ios/Flutter/Debug.xcconfig b/dev/integration_tests/flavors/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/dev/integration_tests/flavors/ios/Flutter/Release.xcconfig b/dev/integration_tests/flavors/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/dev/integration_tests/flavors/ios/Runner.xcodeproj/project.pbxproj b/dev/integration_tests/flavors/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..a94eae3
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,569 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+		3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
+		3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
+		9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
+		9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; };
+		9740EEBB1CF902C7004384FC /* app.flx in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB71CF902C7004384FC /* app.flx */; };
+		978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
+		97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
+		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
+				9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
+		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+		3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
+		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
+		7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+		7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
+		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
+		9740EEB71CF902C7004384FC /* app.flx */ = {isa = PBXFileReference; lastKnownFileType = file; name = app.flx; path = Flutter/app.flx; sourceTree = "<group>"; };
+		9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
+		97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+		97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		97C146EB1CF9000F007C117D /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
+				3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		9740EEB11CF90186004384FC /* Flutter */ = {
+			isa = PBXGroup;
+			children = (
+				9740EEB71CF902C7004384FC /* app.flx */,
+				3B80C3931E831B6300D905FE /* App.framework */,
+				3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+				9740EEBA1CF902C7004384FC /* Flutter.framework */,
+				9740EEB21CF90195004384FC /* Debug.xcconfig */,
+				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+				9740EEB31CF90195004384FC /* Generated.xcconfig */,
+			);
+			name = Flutter;
+			sourceTree = "<group>";
+		};
+		97C146E51CF9000F007C117D = {
+			isa = PBXGroup;
+			children = (
+				9740EEB11CF90186004384FC /* Flutter */,
+				97C146F01CF9000F007C117D /* Runner */,
+				97C146EF1CF9000F007C117D /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		97C146EF1CF9000F007C117D /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				97C146EE1CF9000F007C117D /* Runner.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		97C146F01CF9000F007C117D /* Runner */ = {
+			isa = PBXGroup;
+			children = (
+				7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
+				7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
+				97C146FA1CF9000F007C117D /* Main.storyboard */,
+				97C146FD1CF9000F007C117D /* Assets.xcassets */,
+				97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+				97C147021CF9000F007C117D /* Info.plist */,
+				97C146F11CF9000F007C117D /* Supporting Files */,
+				1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+				1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+			);
+			path = Runner;
+			sourceTree = "<group>";
+		};
+		97C146F11CF9000F007C117D /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				97C146F21CF9000F007C117D /* main.m */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		97C146ED1CF9000F007C117D /* Runner */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+			buildPhases = (
+				9740EEB61CF901F6004384FC /* Run Script */,
+				97C146EA1CF9000F007C117D /* Sources */,
+				97C146EB1CF9000F007C117D /* Frameworks */,
+				97C146EC1CF9000F007C117D /* Resources */,
+				9705A1C41CF9048500538489 /* Embed Frameworks */,
+				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = Runner;
+			productName = Runner;
+			productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		97C146E61CF9000F007C117D /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0830;
+				ORGANIZATIONNAME = "The Chromium Authors";
+				TargetAttributes = {
+					97C146ED1CF9000F007C117D = {
+						CreatedOnToolsVersion = 7.3.1;
+					};
+				};
+			};
+			buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 97C146E51CF9000F007C117D;
+			productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				97C146ED1CF9000F007C117D /* Runner */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		97C146EC1CF9000F007C117D /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9740EEBB1CF902C7004384FC /* app.flx in Resources */,
+				97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+				9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */,
+				3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+				9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
+				97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+				97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Thin Binary";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
+		};
+		9740EEB61CF901F6004384FC /* Run Script */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Run Script";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		97C146EA1CF9000F007C117D /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
+				97C146F31CF9000F007C117D /* main.m in Sources */,
+				1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+		97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				97C146FB1CF9000F007C117D /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+		97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				97C147001CF9000F007C117D /* Base */,
+			);
+			name = LaunchScreen.storyboard;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		744E33D21F45D145007AB5E2 /* Debug Paid */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				PRODUCT_FLAVOR = paid;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = "Debug Paid";
+		};
+		744E33D31F45D145007AB5E2 /* Debug Paid */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+			buildSettings = {
+				ARCHS = arm64;
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				DEVELOPMENT_TEAM = AQ7UHDBEXJ;
+				ENABLE_BITCODE = NO;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.flavors;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = "Debug Paid";
+		};
+		744E33D41F45D159007AB5E2 /* Release Paid */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				PRODUCT_FLAVOR = paid;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = "Release Paid";
+		};
+		744E33D51F45D159007AB5E2 /* Release Paid */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ARCHS = arm64;
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				DEVELOPMENT_TEAM = AQ7UHDBEXJ;
+				ENABLE_BITCODE = NO;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.flavors;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = "Release Paid";
+		};
+		97C147031CF9000F007C117D /* Debug Free */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				PRODUCT_FLAVOR = free;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = "Debug Free";
+		};
+		97C147041CF9000F007C117D /* Release Free */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				PRODUCT_FLAVOR = free;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = "Release Free";
+		};
+		97C147061CF9000F007C117D /* Debug Free */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+			buildSettings = {
+				ARCHS = arm64;
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				DEVELOPMENT_TEAM = AQ7UHDBEXJ;
+				ENABLE_BITCODE = NO;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.flavors;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = "Debug Free";
+		};
+		97C147071CF9000F007C117D /* Release Free */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ARCHS = arm64;
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				DEVELOPMENT_TEAM = AQ7UHDBEXJ;
+				ENABLE_BITCODE = NO;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.flavors;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = "Release Free";
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				97C147031CF9000F007C117D /* Debug Free */,
+				744E33D21F45D145007AB5E2 /* Debug Paid */,
+				97C147041CF9000F007C117D /* Release Free */,
+				744E33D41F45D159007AB5E2 /* Release Paid */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = "Release Free";
+		};
+		97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				97C147061CF9000F007C117D /* Debug Free */,
+				744E33D31F45D145007AB5E2 /* Debug Paid */,
+				97C147071CF9000F007C117D /* Release Free */,
+				744E33D51F45D159007AB5E2 /* Release Paid */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = "Release Free";
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/dev/integration_tests/flavors/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/dev/integration_tests/flavors/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "group:Runner.xcodeproj">
+   </FileRef>
+</Workspace>
diff --git a/dev/integration_tests/flavors/ios/Runner.xcodeproj/xcshareddata/xcschemes/Free.xcscheme b/dev/integration_tests/flavors/ios/Runner.xcodeproj/xcshareddata/xcschemes/Free.xcscheme
new file mode 100644
index 0000000..0a38b9d
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner.xcodeproj/xcshareddata/xcschemes/Free.xcscheme
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0830"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+               BuildableName = "Runner.app"
+               BlueprintName = "Runner"
+               ReferencedContainer = "container:Runner.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug Free"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+            BuildableName = "Runner.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug Free"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+            BuildableName = "Runner.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release Free"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+            BuildableName = "Runner.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug Free">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release Free"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/dev/integration_tests/flavors/ios/Runner.xcodeproj/xcshareddata/xcschemes/Paid.xcscheme b/dev/integration_tests/flavors/ios/Runner.xcodeproj/xcshareddata/xcschemes/Paid.xcscheme
new file mode 100644
index 0000000..50052dc
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner.xcodeproj/xcshareddata/xcschemes/Paid.xcscheme
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0830"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+               BuildableName = "Runner.app"
+               BlueprintName = "Runner"
+               ReferencedContainer = "container:Runner.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug Paid"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+            BuildableName = "Runner.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug Paid"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+            BuildableName = "Runner.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release Paid"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+            BuildableName = "Runner.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug Paid">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release Paid"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/dev/integration_tests/flavors/ios/Runner.xcworkspace/contents.xcworkspacedata b/dev/integration_tests/flavors/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "group:Runner.xcodeproj">
+   </FileRef>
+</Workspace>
diff --git a/dev/integration_tests/flavors/ios/Runner/AppDelegate.h b/dev/integration_tests/flavors/ios/Runner/AppDelegate.h
new file mode 100644
index 0000000..cf210d2
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner/AppDelegate.h
@@ -0,0 +1,6 @@
+#import <UIKit/UIKit.h>
+#import <Flutter/Flutter.h>
+
+@interface AppDelegate : FlutterAppDelegate
+
+@end
diff --git a/dev/integration_tests/flavors/ios/Runner/AppDelegate.m b/dev/integration_tests/flavors/ios/Runner/AppDelegate.m
new file mode 100644
index 0000000..4082691
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner/AppDelegate.m
@@ -0,0 +1,19 @@
+#include "AppDelegate.h"
+#include "GeneratedPluginRegistrant.h"
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+  [GeneratedPluginRegistrant registerWithRegistry:self];
+  // Override point for customization after application launch.
+  FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
+  FlutterMethodChannel* flavorChannel = [FlutterMethodChannel methodChannelWithName:@"flavor" binaryMessenger:controller];
+  
+  [flavorChannel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) {
+    NSString* flavor = (NSString*)[[NSBundle mainBundle].infoDictionary valueForKey:@"Flavor"];
+    result(flavor);
+  }];
+  return [super application:application didFinishLaunchingWithOptions:launchOptions];
+}
+
+@end
diff --git a/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..410c354
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "1x"
+    },
+    {
+      "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" : "20x20",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "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"
+  }
+}
\ No newline at end of file
diff --git a/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "LaunchImage.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "LaunchImage@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "LaunchImage@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
diff --git a/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
Binary files differ
diff --git a/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
Binary files differ
diff --git a/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
Binary files differ
diff --git a/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/dev/integration_tests/flavors/ios/Runner/Base.lproj/LaunchScreen.storyboard b/dev/integration_tests/flavors/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
+                        <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
+                            </imageView>
+                        </subviews>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
+                            <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
+                        </constraints>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="53" y="375"/>
+        </scene>
+    </scenes>
+    <resources>
+        <image name="LaunchImage" width="168" height="185"/>
+    </resources>
+</document>
diff --git a/dev/integration_tests/flavors/ios/Runner/Base.lproj/Main.storyboard b/dev/integration_tests/flavors/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+    </dependencies>
+    <scenes>
+        <!--Flutter View Controller-->
+        <scene sceneID="tne-QT-ifu">
+            <objects>
+                <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+    </scenes>
+</document>
diff --git a/dev/integration_tests/flavors/ios/Runner/Info.plist b/dev/integration_tests/flavors/ios/Runner/Info.plist
new file mode 100644
index 0000000..7a6821f
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner/Info.plist
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>flavors</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>Main</string>
+  <key>Flavor</key>
+  <string>${PRODUCT_FLAVOR}</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>arm64</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UIViewControllerBasedStatusBarAppearance</key>
+	<false/>
+</dict>
+</plist>
diff --git a/dev/integration_tests/flavors/ios/Runner/main.m b/dev/integration_tests/flavors/ios/Runner/main.m
new file mode 100644
index 0000000..0ccc450
--- /dev/null
+++ b/dev/integration_tests/flavors/ios/Runner/main.m
@@ -0,0 +1,9 @@
+#import <UIKit/UIKit.h>
+#import <Flutter/Flutter.h>
+#import "AppDelegate.h"
+
+int main(int argc, char * argv[]) {
+  @autoreleasepool {
+    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+  }
+}
diff --git a/dev/integration_tests/flavors/lib/main.dart b/dev/integration_tests/flavors/lib/main.dart
new file mode 100644
index 0000000..45ee469
--- /dev/null
+++ b/dev/integration_tests/flavors/lib/main.dart
@@ -0,0 +1,34 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_driver/driver_extension.dart';
+
+void main() {
+  enableFlutterDriverExtension();
+  runApp(new Center(child: new Flavor()));
+}
+
+class Flavor extends StatefulWidget {
+  @override
+  _FlavorState createState() => new _FlavorState();
+}
+
+class _FlavorState extends State<Flavor> {
+  String _flavor;
+
+  @override
+  void initState() {
+    super.initState();
+    new MethodChannel('flavor').invokeMethod('getFlavor').then((String flavor) {
+      setState(() {
+        _flavor = flavor;
+      });
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return _flavor == null 
+      ? new Text('Awaiting flavor...')
+      : new Text(_flavor, key: const ValueKey<String>('flavor'));
+  }
+}
diff --git a/dev/integration_tests/flavors/pubspec.yaml b/dev/integration_tests/flavors/pubspec.yaml
new file mode 100644
index 0000000..60cc16a
--- /dev/null
+++ b/dev/integration_tests/flavors/pubspec.yaml
@@ -0,0 +1,10 @@
+name: flavors
+description: Integration test for build flavors.
+
+dependencies:
+  flutter:
+    sdk: flutter
+  flutter_driver:
+    sdk: flutter
+flutter:
+  uses-material-design: true
diff --git a/dev/integration_tests/flavors/test_driver/main_test.dart b/dev/integration_tests/flavors/test_driver/main_test.dart
new file mode 100644
index 0000000..548ebbf
--- /dev/null
+++ b/dev/integration_tests/flavors/test_driver/main_test.dart
@@ -0,0 +1,26 @@
+// Copyright 2017 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 'package:flutter_driver/flutter_driver.dart';
+import 'package:test/test.dart';
+
+void main() {
+  group('flavors suite', () {
+    FlutterDriver driver;
+
+    setUpAll(() async {
+      driver = await FlutterDriver.connect();
+    });
+
+    test('check flavor', () async {
+      final SerializableFinder flavorField = find.byValueKey('flavor');
+      final String flavor = await driver.getText(flavorField);
+      expect(flavor, 'paid');
+    });
+
+    tearDownAll(() async {
+      driver?.close();
+    });
+  });
+}
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart
index 6175d96..6f2d0ec 100644
--- a/packages/flutter_tools/lib/src/android/android_device.dart
+++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -338,8 +338,7 @@
 
   @override
   Future<LaunchResult> startApp(
-    ApplicationPackage package,
-    BuildMode mode, {
+    ApplicationPackage package, {
     String mainPath,
     String route,
     DebuggingOptions debuggingOptions,
@@ -352,7 +351,7 @@
     if (!await _checkForSupportedAdbVersion() || !await _checkForSupportedAndroidVersion())
       return new LaunchResult.failed();
 
-    if (await targetPlatform != TargetPlatform.android_arm && mode != BuildMode.debug) {
+    if (await targetPlatform != TargetPlatform.android_arm && !debuggingOptions.buildInfo.isDebug) {
       printError('Profile and release builds are only supported on ARM targets.');
       return new LaunchResult.failed();
     }
@@ -361,7 +360,7 @@
       printTrace('Building APK');
       await buildApk(
           target: mainPath,
-          buildMode: debuggingOptions.buildMode,
+          buildInfo: debuggingOptions.buildInfo,
           kernelPath: kernelPath,
       );
       // Package has been built, so we can get the updated application ID and
@@ -408,7 +407,7 @@
     if (debuggingOptions.enableSoftwareRendering)
       cmd.addAll(<String>['--ez', 'enable-software-rendering', 'true']);
     if (debuggingOptions.debuggingEnabled) {
-      if (debuggingOptions.buildMode == BuildMode.debug)
+      if (debuggingOptions.buildInfo.isDebug)
         cmd.addAll(<String>['--ez', 'enable-checked-mode', 'true']);
       if (debuggingOptions.startPaused)
         cmd.addAll(<String>['--ez', 'start-paused', 'true']);
@@ -435,13 +434,13 @@
     try {
       Uri observatoryUri, diagnosticUri;
 
-      if (debuggingOptions.buildMode == BuildMode.debug) {
+      if (debuggingOptions.buildInfo.isDebug) {
         final List<Uri> deviceUris = await Future.wait(
             <Future<Uri>>[observatoryDiscovery.uri, diagnosticDiscovery.uri]
         );
         observatoryUri = deviceUris[0];
         diagnosticUri = deviceUris[1];
-      } else if (debuggingOptions.buildMode == BuildMode.profile) {
+      } else if (debuggingOptions.buildInfo.isProfile) {
         observatoryUri = await observatoryDiscovery.uri;
       }
 
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart
index 25d4083..05d7c67 100644
--- a/packages/flutter_tools/lib/src/android/gradle.dart
+++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -23,8 +23,9 @@
 const String gradleAppOutV1 = 'android/app/build/outputs/apk/app-debug.apk';
 const String gradleAppOutDirV1 = 'android/app/build/outputs/apk';
 const String gradleVersion = '3.3';
+final RegExp _assembleTaskPattern = new RegExp(r'assemble([^:]+): task ');
 
-String _cachedGradleAppOutDirV2;
+GradleProject _cachedGradleProject;
 String _cachedGradleExecutable;
 
 enum FlutterPluginVersion {
@@ -58,6 +59,8 @@
   return FlutterPluginVersion.none;
 }
 
+/// Returns the path to the apk file created by [buildGradleProject], relative
+/// to current directory.
 Future<String> getGradleAppOut() async {
   switch (flutterPluginVersion) {
     case FlutterPluginVersion.none:
@@ -67,19 +70,19 @@
     case FlutterPluginVersion.managed:
       // Fall through. The managed plugin matches plugin v2 for now.
     case FlutterPluginVersion.v2:
-      return '${await _getGradleAppOutDirV2()}/app.apk';
+      return fs.path.relative(fs.path.join((await _gradleProject()).apkDirectory, 'app.apk'));
   }
   return null;
 }
 
-Future<String> _getGradleAppOutDirV2() async {
-  _cachedGradleAppOutDirV2 ??= await _calculateGradleAppOutDirV2();
-  return _cachedGradleAppOutDirV2;
+Future<GradleProject> _gradleProject() async {
+  _cachedGradleProject ??= await _readGradleProject();
+  return _cachedGradleProject;
 }
 
 // Note: Dependencies are resolved and possibly downloaded as a side-effect
 // of calculating the app properties using Gradle. This may take minutes.
-Future<String> _calculateGradleAppOutDirV2() async {
+Future<GradleProject> _readGradleProject() async {
   final String gradle = await _ensureGradle();
   updateLocalProperties();
   try {
@@ -90,28 +93,20 @@
       environment: _gradleEnv,
     );
     final String properties = runResult.stdout.trim();
-    String buildDir = properties
-        .split('\n')
-        .firstWhere((String s) => s.startsWith('buildDir: '))
-        .substring('buildDir: '.length)
-        .trim();
-    final String currentDirectory = fs.currentDirectory.path;
-    if (buildDir.startsWith(currentDirectory)) {
-      // Relativize path, snip current directory + separating '/'.
-      buildDir = buildDir.substring(currentDirectory.length + 1);
-    }
+    final GradleProject project = new GradleProject.fromAppProperties(properties);
     status.stop();
-    return '$buildDir/outputs/apk';
+    return project;
   } catch (e) {
     printError('Error running gradle: $e');
   }
   // Fall back to the default
-  return gradleAppOutDirV1;
+  return new GradleProject(<String>['debug', 'profile', 'release'], <String>[], gradleAppOutDirV1);
 }
 
 String _locateProjectGradlew({ bool ensureExecutable: true }) {
   final String path = fs.path.join(
-      'android', platform.isWindows ? 'gradlew.bat' : 'gradlew'
+    'android',
+    platform.isWindows ? 'gradlew.bat' : 'gradlew',
   );
 
   if (fs.isFileSync(path)) {
@@ -164,7 +159,7 @@
 }
 
 /// Create android/local.properties if needed, and update Flutter settings.
-void updateLocalProperties({String projectPath, String buildMode}) {
+void updateLocalProperties({String projectPath, BuildInfo buildInfo}) {
   final File localProperties = (projectPath == null)
       ? fs.file(fs.path.join('android', 'local.properties'))
       : fs.file(fs.path.join(projectPath, 'android', 'local.properties'));
@@ -183,8 +178,8 @@
     settings.values['flutter.sdk'] = escapedRoot;
     changed = true;
   }
-  if (buildMode != null && settings.values['flutter.buildMode'] != buildMode) {
-    settings.values['flutter.buildMode']  = buildMode;
+  if (buildInfo != null && settings.values['flutter.buildMode'] != buildInfo.modeName) {
+    settings.values['flutter.buildMode'] = buildInfo.modeName;
     changed = true;
   }
 
@@ -192,13 +187,12 @@
     settings.writeContents(localProperties);
 }
 
-Future<Null> buildGradleProject(BuildMode buildMode, String target, String kernelPath) async {
+Future<Null> buildGradleProject(BuildInfo buildInfo, String target, String kernelPath) async {
   // Update the local.properties file with the build mode.
   // FlutterPlugin v1 reads local.properties to determine build mode. Plugin v2
   // uses the standard Android way to determine what to build, but we still
   // update local.properties, in case we want to use it in the future.
-  final String buildModeName = getModeName(buildMode);
-  updateLocalProperties(buildMode: buildModeName);
+  updateLocalProperties(buildInfo: buildInfo);
 
   injectPlugins();
 
@@ -212,7 +206,7 @@
     case FlutterPluginVersion.managed:
       // Fall through. Managed plugin builds the same way as plugin v2.
     case FlutterPluginVersion.v2:
-      return _buildGradleProjectV2(gradle, buildModeName, target, kernelPath);
+      return _buildGradleProjectV2(gradle, buildInfo, target, kernelPath);
   }
 }
 
@@ -234,21 +228,25 @@
   printStatus('Built $gradleAppOutV1 (${getSizeAsMB(apkFile.lengthSync())}).');
 }
 
-File findApkFile(String buildDirectory, String buildModeName) {
-  final String apkFilename = 'app-$buildModeName.apk';
-  File apkFile = fs.file('$buildDirectory/$apkFilename');
-  if (apkFile.existsSync())
-    return apkFile;
-  apkFile = fs.file('$buildDirectory/$buildModeName/$apkFilename');
-  if (apkFile.existsSync())
-    return apkFile;
-  return null;
-}
-
-Future<Null> _buildGradleProjectV2(String gradle, String buildModeName, String target, String kernelPath) async {
-  final String assembleTask = "assemble${toTitleCase(buildModeName)}";
-
-  // Run 'gradlew assemble<BuildMode>'.
+Future<Null> _buildGradleProjectV2(String gradle, BuildInfo buildInfo, String target, String kernelPath) async {
+  final GradleProject project = await _gradleProject();
+  final String assembleTask = project.assembleTaskFor(buildInfo);
+  if (assembleTask == null) {
+    printError('');
+    printError('The Gradle project does not define a task suitable for the requested build.');
+    if (!project.buildTypes.contains(buildInfo.modeName)) {
+      printError('Review the android/app/build.gradle file and ensure it defines a ${buildInfo.modeName} build type.');
+    } else {
+      if (project.productFlavors.isEmpty) {
+        printError('The android/app/build.gradle file does not define any custom product flavors.');
+        printError('You cannot use the --flavor option.');
+      } else {
+        printError('The android/app/build.gradle file defines product flavors: ${project.productFlavors.join(', ')}');
+        printError('You must specify a --flavor option to select one of them.');
+      }
+      throwToolExit('Gradle build aborted.');
+    }
+  }
   final Status status = logger.startProgress('Running \'gradlew $assembleTask\'...', expectSlowOperation: true);
   final String gradlePath = fs.file(gradle).absolute.path;
   final List<String> command = <String>[gradlePath];
@@ -266,7 +264,7 @@
   if (kernelPath != null)
     command.add('-Pkernel=$kernelPath');
   command.add(assembleTask);
-  final int exitcode = await runCommandAndStreamOutput(
+  final int exitCode = await runCommandAndStreamOutput(
       command,
       workingDirectory: 'android',
       allowReentrantFlutter: true,
@@ -274,21 +272,33 @@
   );
   status.stop();
 
-  if (exitcode != 0)
-    throwToolExit('Gradle build failed: $exitcode', exitCode: exitcode);
+  if (exitCode != 0)
+    throwToolExit('Gradle build failed: $exitCode', exitCode: exitCode);
 
-  final String buildDirectory = await _getGradleAppOutDirV2();
-  final File apkFile = findApkFile(buildDirectory, buildModeName);
+  final File apkFile = _findApkFile(project, buildInfo);
   if (apkFile == null)
     throwToolExit('Gradle build failed to produce an Android package.');
   // Copy the APK to app.apk, so `flutter run`, `flutter install`, etc. can find it.
-  apkFile.copySync('$buildDirectory/app.apk');
+  apkFile.copySync(fs.path.join(project.apkDirectory, 'app.apk'));
 
-  printTrace('calculateSha: $buildDirectory/app.apk');
-  final File apkShaFile = fs.file('$buildDirectory/app.apk.sha1');
+  printTrace('calculateSha: ${project.apkDirectory}/app.apk');
+  final File apkShaFile = fs.file(fs.path.join(project.apkDirectory, 'app.apk.sha1'));
   apkShaFile.writeAsStringSync(calculateSha(apkFile));
 
-  printStatus('Built ${apkFile.path} (${getSizeAsMB(apkFile.lengthSync())}).');
+  printStatus('Built ${fs.path.relative(apkFile.path)} (${getSizeAsMB(apkFile.lengthSync())}).');
+}
+
+File _findApkFile(GradleProject project, BuildInfo buildInfo) {
+  final String apkFileName = project.apkFileFor(buildInfo);
+  if (apkFileName == null)
+    return null;
+  File apkFile = fs.file(fs.path.join(project.apkDirectory, apkFileName));
+  if (apkFile.existsSync())
+    return apkFile;
+  apkFile = fs.file(fs.path.join(project.apkDirectory, buildInfo.modeName, apkFileName));
+  if (apkFile.existsSync())
+    return apkFile;
+  return null;
 }
 
 Map<String, String> get _gradleEnv {
@@ -299,3 +309,83 @@
   }
   return env;
 }
+
+class GradleProject {
+  GradleProject(this.buildTypes, this.productFlavors, this.apkDirectory);
+
+  factory GradleProject.fromAppProperties(String properties) {
+    // Extract build directory.
+    final String buildDir = properties
+        .split('\n')
+        .firstWhere((String s) => s.startsWith('buildDir: '))
+        .substring('buildDir: '.length)
+        .trim();
+
+    // Extract build types and product flavors.
+    final Set<String> variants = new Set<String>();
+    properties.split('\n').forEach((String s) {
+      final Match match = _assembleTaskPattern.matchAsPrefix(s);
+      if (match != null) {
+        final String variant = match.group(1).toLowerCase();
+        if (!variant.endsWith('test'))
+          variants.add(variant);
+      }
+    });
+    final Set<String> buildTypes = new Set<String>();
+    final Set<String> productFlavors = new Set<String>();
+    for (final String variant1 in variants) {
+      for (final String variant2 in variants) {
+        if (variant2.startsWith(variant1) && variant2 != variant1) {
+          final String buildType = variant2.substring(variant1.length);
+          if (variants.contains(buildType)) {
+            buildTypes.add(buildType);
+            productFlavors.add(variant1);
+          }
+        }
+      }
+    }
+    if (productFlavors.isEmpty)
+      buildTypes.addAll(variants);
+    return new GradleProject(
+      buildTypes.toList(),
+      productFlavors.toList(),
+      fs.path.normalize(fs.path.join(buildDir, 'outputs', 'apk')),
+    );
+  }
+
+  final List<String> buildTypes;
+  final List<String> productFlavors;
+  final String apkDirectory;
+
+  String _buildTypeFor(BuildInfo buildInfo) {
+    if (buildTypes.contains(buildInfo.modeName))
+      return buildInfo.modeName;
+    return null;
+  }
+
+  String _productFlavorFor(BuildInfo buildInfo) {
+    if (buildInfo.flavor == null)
+      return productFlavors.isEmpty ? '' : null;
+    else if (productFlavors.contains(buildInfo.flavor.toLowerCase()))
+      return buildInfo.flavor.toLowerCase();
+    else
+      return null;
+  }
+
+  String assembleTaskFor(BuildInfo buildInfo) {
+    final String buildType = _buildTypeFor(buildInfo);
+    final String productFlavor = _productFlavorFor(buildInfo);
+    if (buildType == null || productFlavor == null)
+      return null;
+    return 'assemble${toTitleCase(productFlavor)}${toTitleCase(buildType)}';
+  }
+
+  String apkFileFor(BuildInfo buildInfo) {
+    final String buildType = _buildTypeFor(buildInfo);
+    final String productFlavor = _productFlavorFor(buildInfo);
+    if (buildType == null || productFlavor == null)
+      return null;
+    final String flavorString = productFlavor.isEmpty ? '' : '-' + productFlavor;
+    return 'app$flavorString-$buildType.apk';
+  }
+}
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
index a931d36..f348a40 100644
--- a/packages/flutter_tools/lib/src/application_package.dart
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -228,7 +228,7 @@
   bool get isSwift => buildSettings?.containsKey('SWIFT_VERSION');
 
   String _buildAppPath(String type) {
-    return fs.path.join(getIosBuildDirectory(), 'Release-$type', kBundleName);
+    return fs.path.join(getIosBuildDirectory(), type, kBundleName);
   }
 }
 
diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart
index 52dc5cf..c9a5662 100644
--- a/packages/flutter_tools/lib/src/build_info.dart
+++ b/packages/flutter_tools/lib/src/build_info.dart
@@ -8,10 +8,42 @@
 import 'base/utils.dart';
 import 'globals.dart';
 
-enum BuildType {
-  prebuilt,
-  release,
-  debug,
+/// Information about a build to be performed or used.
+class BuildInfo {
+  const BuildInfo(this.mode, this.flavor);
+
+  final BuildMode mode;
+  /// Represents a custom Android product flavor or an Xcode scheme, null for
+  /// using the default.
+  ///
+  /// If not null, the Gradle build task will be `assembleFlavorMode` (e.g.
+  /// `assemblePaidRelease`), and the Xcode build configuration will be
+  /// Mode-Flavor (e.g. Release-Paid).
+  final String flavor;
+
+  static const BuildInfo debug = const BuildInfo(BuildMode.debug, null);
+  static const BuildInfo profile = const BuildInfo(BuildMode.profile, null);
+  static const BuildInfo release = const BuildInfo(BuildMode.release, null);
+
+  /// Returns whether a debug build is requested.
+  ///
+  /// Exactly one of [isDebug], [isProfile], or [isRelease] is true.
+  bool get isDebug => mode == BuildMode.debug;
+
+  /// Returns whether a profile build is requested.
+  ///
+  /// Exactly one of [isDebug], [isProfile], or [isRelease] is true.
+  bool get isProfile => mode == BuildMode.profile;
+
+  /// Returns whether a release build is requested.
+  ///
+  /// Exactly one of [isDebug], [isProfile], or [isRelease] is true.
+  bool get isRelease => mode == BuildMode.release;
+
+  bool get usesAot => isAotBuildMode(mode);
+  bool get supportsEmulator => isEmulatorBuildMode(mode);
+  bool get supportsSimulator => isEmulatorBuildMode(mode);
+  String get modeName => getModeName(mode);
 }
 
 /// The type of build - `debug`, `profile`, or `release`.
diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart
index 21e5f5b..791b1cc 100644
--- a/packages/flutter_tools/lib/src/commands/build_apk.dart
+++ b/packages/flutter_tools/lib/src/commands/build_apk.dart
@@ -35,6 +35,7 @@
   BuildApkCommand() {
     usesTargetOption();
     addBuildModeFlags();
+    usesFlavorOption();
     usesPubOption();
   }
 
@@ -51,14 +52,14 @@
   Future<Null> runCommand() async {
     await super.runCommand();
 
-    final BuildMode buildMode = getBuildMode();
-    await buildApk(buildMode: buildMode, target: targetFile);
+    final BuildInfo buildInfo = getBuildInfo();
+    await buildApk(buildInfo: buildInfo, target: targetFile);
   }
 }
 
 Future<Null> buildApk({
   String target,
-  BuildMode buildMode: BuildMode.debug,
+  BuildInfo buildInfo: BuildInfo.debug,
   String kernelPath,
 }) async {
   if (!isProjectUsingGradle()) {
@@ -80,5 +81,5 @@
     throwToolExit('Try re-installing or updating your Android SDK.');
   }
 
-  return buildGradleProject(buildMode, target, kernelPath);
+  return buildGradleProject(buildInfo, target, kernelPath);
 }
diff --git a/packages/flutter_tools/lib/src/commands/build_ios.dart b/packages/flutter_tools/lib/src/commands/build_ios.dart
index cfc0992..2724865 100644
--- a/packages/flutter_tools/lib/src/commands/build_ios.dart
+++ b/packages/flutter_tools/lib/src/commands/build_ios.dart
@@ -15,6 +15,7 @@
 class BuildIOSCommand extends BuildSubCommand {
   BuildIOSCommand() {
     usesTargetOption();
+    usesFlavorOption();
     usesPubOption();
     argParser.addFlag('debug',
       negatable: false,
@@ -56,17 +57,17 @@
       printStatus('Warning: Building for device with codesigning disabled. You will '
         'have to manually codesign before deploying to device.');
     }
-
-    if (forSimulator && !isEmulatorBuildMode(getBuildMode()))
-      throwToolExit('${toTitleCase(getModeName(getBuildMode()))} mode is not supported for emulators.');
+    final BuildInfo buildInfo = getBuildInfo();
+    if (forSimulator && !buildInfo.supportsSimulator)
+      throwToolExit('${toTitleCase(buildInfo.modeName)} mode is not supported for simulators.');
 
     final String logTarget = forSimulator ? 'simulator' : 'device';
 
-    final String typeName = artifacts.getEngineType(TargetPlatform.ios, getBuildMode());
+    final String typeName = artifacts.getEngineType(TargetPlatform.ios, buildInfo.mode);
     printStatus('Building $app for $logTarget ($typeName)...');
     final XcodeBuildResult result = await buildXcodeProject(
       app: app,
-      mode: getBuildMode(),
+      buildInfo: buildInfo,
       target: targetFile,
       buildForDevice: !forSimulator,
       codesign: shouldCodesign
diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index 0b6f67c..75f4795 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -182,7 +182,7 @@
 
     updateXcodeGeneratedProperties(
       projectPath: appPath,
-      mode: BuildMode.debug,
+      buildInfo: BuildInfo.debug,
       target: flx.defaultMainPath,
       hasPlugins: generatePlugin,
     );
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index cb069f1..f5d997f 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -302,6 +302,7 @@
     final bool useTestFonts = _getBoolArg(args, 'useTestFonts') ?? false;
     final String route = _getStringArg(args, 'route');
     final String mode = _getStringArg(args, 'mode');
+    final String flavor = _getStringArg(args, 'flavor');
     final String target = _getStringArg(args, 'target');
     final bool enableHotReload = _getBoolArg(args, 'hot') ?? kHotReloadDefault;
 
@@ -312,13 +313,13 @@
     if (!fs.isDirectorySync(projectDirectory))
       throw "'$projectDirectory' does not exist";
 
-    final BuildMode buildMode = getBuildModeForName(mode) ?? BuildMode.debug;
+    final BuildInfo buildInfo = new BuildInfo(getBuildModeForName(mode) ?? BuildMode.debug, flavor);
     DebuggingOptions options;
-    if (buildMode == BuildMode.release) {
-      options = new DebuggingOptions.disabled(buildMode);
+    if (buildInfo.isRelease) {
+      options = new DebuggingOptions.disabled(buildInfo);
     } else {
       options = new DebuggingOptions.enabled(
-        buildMode,
+        buildInfo,
         startPaused: startPaused,
         useTestFonts: useTestFonts,
       );
@@ -349,8 +350,8 @@
     String packagesFilePath,
     String projectAssets,
   }) async {
-    if (await device.isLocalEmulator && !isEmulatorBuildMode(options.buildMode))
-      throw '${toTitleCase(getModeName(options.buildMode))} mode is not supported for emulators.';
+    if (await device.isLocalEmulator && !options.buildInfo.supportsEmulator)
+      throw '${toTitleCase(options.buildInfo.modeName)} mode is not supported for emulators.';
 
     // We change the current working directory for the duration of the `start` command.
     final Directory cwd = fs.currentDirectory;
diff --git a/packages/flutter_tools/lib/src/commands/drive.dart b/packages/flutter_tools/lib/src/commands/drive.dart
index e9ad417..d465092 100644
--- a/packages/flutter_tools/lib/src/commands/drive.dart
+++ b/packages/flutter_tools/lib/src/commands/drive.dart
@@ -8,7 +8,6 @@
 import '../base/common.dart';
 import '../base/file_system.dart';
 import '../base/process.dart';
-import '../build_info.dart';
 import '../cache.dart';
 import '../dart/package_map.dart';
 import '../dart/sdk.dart';
@@ -28,7 +27,7 @@
 /// as the `--target` option (defaults to `lib/main.dart`). It then looks for a
 /// corresponding test file within the `test_driver` directory. The test file is
 /// expected to have the same name but contain the `_test.dart` suffix. The
-/// `_test.dart` file would generall be a Dart program that uses
+/// `_test.dart` file would generally be a Dart program that uses
 /// `package:flutter_driver` and exercises your application. Most commonly it
 /// is a test written using `package:test`, but you are free to use something
 /// else.
@@ -113,7 +112,7 @@
     if (argResults['use-existing-app'] == null) {
       printStatus('Starting application: $targetFile');
 
-      if (getBuildMode() == BuildMode.release) {
+      if (getBuildInfo().isRelease) {
         // This is because we need VM service to be able to drive the app.
         throwToolExit(
           'Flutter Driver does not support running in release mode.\n'
@@ -267,11 +266,10 @@
 
   final LaunchResult result = await command.device.startApp(
     package,
-    command.getBuildMode(),
     mainPath: mainPath,
     route: command.route,
     debuggingOptions: new DebuggingOptions.enabled(
-      command.getBuildMode(),
+      command.getBuildInfo(),
       startPaused: true,
       observatoryPort: command.observatoryPort,
       diagnosticPort: command.diagnosticPort,
diff --git a/packages/flutter_tools/lib/src/commands/fuchsia_reload.dart b/packages/flutter_tools/lib/src/commands/fuchsia_reload.dart
index 118b418..399618d 100644
--- a/packages/flutter_tools/lib/src/commands/fuchsia_reload.dart
+++ b/packages/flutter_tools/lib/src/commands/fuchsia_reload.dart
@@ -122,7 +122,7 @@
     flutterDevice.observatoryUris = observatoryUris;
     final HotRunner hotRunner = new HotRunner(
       <FlutterDevice>[flutterDevice],
-      debuggingOptions: new DebuggingOptions.enabled(getBuildMode()),
+      debuggingOptions: new DebuggingOptions.enabled(getBuildInfo()),
       target: _target,
       projectRootPath: _fuchsiaProjectPath,
       packagesFilePath: _dotPackagesPath
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index 02a8441..7ca63a0 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -23,6 +23,7 @@
   // Used by run and drive commands.
   RunCommandBase() {
     addBuildModeFlags(defaultToRelease: false);
+    usesFlavorOption();
     argParser.addFlag('trace-startup',
         negatable: true,
         defaultsTo: false,
@@ -208,7 +209,7 @@
   bool shouldUseHotMode() {
     final bool hotArg = argResults['hot'] ?? false;
     final bool shouldUseHotMode = hotArg;
-    return (getBuildMode() == BuildMode.debug) && shouldUseHotMode;
+    return getBuildInfo().isDebug && shouldUseHotMode;
   }
 
   bool get runningWithPrebuiltApplication =>
@@ -228,11 +229,12 @@
   }
 
   DebuggingOptions _createDebuggingOptions() {
-    if (getBuildMode() == BuildMode.release) {
-      return new DebuggingOptions.disabled(getBuildMode());
+    final BuildInfo buildInfo = getBuildInfo();
+    if (buildInfo.isRelease) {
+      return new DebuggingOptions.disabled(buildInfo);
     } else {
       return new DebuggingOptions.enabled(
-        getBuildMode(),
+        buildInfo,
         startPaused: argResults['start-paused'],
         useTestFonts: argResults['use-test-fonts'],
         enableSoftwareRendering: argResults['enable-software-rendering'],
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index e46519a..52434aa 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -235,8 +235,7 @@
   /// for iOS device deployment. Set to false if stdin cannot be read from while
   /// attempting to start the app.
   Future<LaunchResult> startApp(
-    ApplicationPackage package,
-    BuildMode mode, {
+    ApplicationPackage package, {
     String mainPath,
     String route,
     DebuggingOptions debuggingOptions,
@@ -316,7 +315,7 @@
 }
 
 class DebuggingOptions {
-  DebuggingOptions.enabled(this.buildMode, {
+  DebuggingOptions.enabled(this.buildInfo, {
     this.startPaused: false,
     this.enableSoftwareRendering: false,
     this.useTestFonts: false,
@@ -324,7 +323,7 @@
     this.diagnosticPort
    }) : debuggingEnabled = true;
 
-  DebuggingOptions.disabled(this.buildMode) :
+  DebuggingOptions.disabled(this.buildInfo) :
     debuggingEnabled = false,
     useTestFonts = false,
     startPaused = false,
@@ -334,7 +333,7 @@
 
   final bool debuggingEnabled;
 
-  final BuildMode buildMode;
+  final BuildInfo buildInfo;
   final bool startPaused;
   final bool enableSoftwareRendering;
   final bool useTestFonts;
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
index 9f8a8e0..1587577 100644
--- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
+++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
@@ -59,8 +59,7 @@
 
   @override
   Future<LaunchResult> startApp(
-    ApplicationPackage app,
-    BuildMode mode, {
+    ApplicationPackage app, {
     String mainPath,
     String route,
     DebuggingOptions debuggingOptions,
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index f97ef42..5db8514 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -164,8 +164,7 @@
 
   @override
   Future<LaunchResult> startApp(
-    ApplicationPackage app,
-    BuildMode mode, {
+    ApplicationPackage app, {
     String mainPath,
     String route,
     DebuggingOptions debuggingOptions,
@@ -182,7 +181,7 @@
       // Step 1: Build the precompiled/DBC application if necessary.
       final XcodeBuildResult buildResult = await buildXcodeProject(
           app: app,
-          mode: mode,
+          buildInfo: debuggingOptions.buildInfo,
           target: mainPath,
           buildForDevice: true,
           usesTerminalUi: usesTerminalUi,
@@ -267,7 +266,7 @@
 
       final Future<Uri> forwardObservatoryUri = observatoryDiscovery.uri;
       Future<Uri> forwardDiagnosticUri;
-      if (debuggingOptions.buildMode == BuildMode.debug) {
+      if (debuggingOptions.buildInfo.isDebug) {
         forwardDiagnosticUri = diagnosticDiscovery.uri;
       } else {
         forwardDiagnosticUri = new Future<Uri>.value(null);
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index 87c9f15..3885332 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -220,7 +220,7 @@
 
 Future<XcodeBuildResult> buildXcodeProject({
   BuildableIOSApp app,
-  BuildMode mode,
+  BuildInfo buildInfo,
   String target: flx.defaultMainPath,
   bool buildForDevice,
   bool codesign: true,
@@ -234,6 +234,35 @@
     return new XcodeBuildResult(success: false);
   }
 
+  final XcodeProjectInfo projectInfo = new XcodeProjectInfo.fromProjectSync(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:');
+    printError('  open ios/Runner.xcworkspace');
+    return new XcodeBuildResult(success: false);
+  }
+  final String scheme = projectInfo.schemeFor(buildInfo);
+  if (scheme == null) {
+    printError('');
+    if (projectInfo.definesCustomSchemes) {
+      printError('The Xcode project defines schemes: ${projectInfo.schemes.join(', ')}');
+      printError('You must specify a --flavor option to select one of them.');
+    } else {
+      printError('The Xcode project does not define custom schemes.');
+      printError('You cannot use the --flavor option.');
+    }
+    return new XcodeBuildResult(success: false);
+  }
+  final String configuration = projectInfo.buildConfigurationFor(buildInfo, scheme);
+  if (configuration == null) {
+    printError('');
+    printError('The Xcode project defines build configurations: ${projectInfo.buildConfigurations.join(', ')}');
+    printError('Flutter expects a build configuration named ${XcodeProjectInfo.expectedBuildConfigurationFor(buildInfo, scheme)} or similar.');
+    printError('Open Xcode to fix the problem:');
+    printError('  open ios/Runner.xcworkspace');
+    return new XcodeBuildResult(success: false);
+  }
+
   String developmentTeam;
   if (codesign && buildForDevice)
     developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app, usesTerminalUi: usesTerminalUi);
@@ -247,13 +276,13 @@
   if (hasFlutterPlugins)
     await cocoaPods.processPods(
       appIosDir: appDirectory,
-      iosEngineDir: flutterFrameworkDir(mode),
+      iosEngineDir: flutterFrameworkDir(buildInfo.mode),
       isSwift: app.isSwift,
     );
 
   updateXcodeGeneratedProperties(
     projectPath: fs.currentDirectory.path,
-    mode: mode,
+    buildInfo: buildInfo,
     target: target,
     hasPlugins: hasFlutterPlugins
   );
@@ -264,7 +293,8 @@
     'xcodebuild',
     'clean',
     'build',
-    '-configuration', 'Release',
+    '-configuration', configuration,
+    '-scheme', scheme,
     'ONLY_ACTIVE_ARCH=YES',
   ];
 
@@ -276,7 +306,6 @@
     if (fs.path.extension(entity.path) == '.xcworkspace') {
       commands.addAll(<String>[
         '-workspace', fs.path.basename(entity.path),
-        '-scheme', fs.path.basenameWithoutExtension(entity.path),
         "BUILD_DIR=${fs.path.absolute(getIosBuildDirectory())}",
       ]);
       break;
@@ -306,7 +335,6 @@
     allowReentrantFlutter: true
   );
   status.stop();
-
   if (result.exitCode != 0) {
     printStatus('Failed to build iOS app');
     if (result.stderr.isNotEmpty) {
@@ -328,13 +356,18 @@
       ),
     );
   } else {
-    // Look for 'clean build/Release-iphoneos/Runner.app'.
-    final RegExp regexp = new RegExp(r' clean (\S*\.app)$', multiLine: true);
+    // Look for 'clean build/<configuration>-<sdk>/Runner.app'.
+    final RegExp regexp = new RegExp(r' clean (.*\.app)$', multiLine: true);
     final Match match = regexp.firstMatch(result.stdout);
     String outputDir;
-    if (match != null)
-      outputDir = fs.path.join(app.appDirectory, match.group(1));
-    return new XcodeBuildResult(success:true, output: outputDir);
+    if (match != null) {
+      final String actualOutputDir = match.group(1).replaceAll('\\ ', ' ');
+      // Copy app folder to a place where other tools can find it without knowing
+      // the BuildInfo.
+      outputDir = actualOutputDir.replaceFirst('/$configuration-', '/');
+      copyDirectorySync(fs.directory(actualOutputDir), fs.directory(outputDir));
+    }
+    return new XcodeBuildResult(success: true, output: outputDir);
   }
 }
 
@@ -356,7 +389,9 @@
     printError(noDevelopmentTeamInstruction, emphasis: true);
     return;
   }
-  if (app.id?.contains('com.yourcompany') ?? false) {
+  if (result.xcodeBuildExecution != null &&
+      result.xcodeBuildExecution.buildForPhysicalDevice &&
+      app.id?.contains('com.yourcompany') ?? false) {
     printError('');
     printError('It appears that your application still contains the default signing identifier.');
     printError("Try replacing 'com.yourcompany' with your signing id in Xcode:");
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index 910d16d..03c73e4 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -306,8 +306,7 @@
 
   @override
   Future<LaunchResult> startApp(
-    ApplicationPackage app,
-    BuildMode mode, {
+    ApplicationPackage app, {
     String mainPath,
     String route,
     DebuggingOptions debuggingOptions,
@@ -321,7 +320,7 @@
       printTrace('Building ${app.name} for $id.');
 
       try {
-        await _setupUpdatedApplicationBundle(app);
+        await _setupUpdatedApplicationBundle(app, debuggingOptions.buildInfo.flavor);
       } on ToolExit catch (e) {
         printError(e.message);
         return new LaunchResult.failed();
@@ -343,7 +342,7 @@
     }
 
     if (debuggingOptions.debuggingEnabled) {
-      if (debuggingOptions.buildMode == BuildMode.debug)
+      if (debuggingOptions.buildInfo.isDebug)
         args.add('--enable-checked-mode');
       if (debuggingOptions.startPaused)
         args.add('--start-paused');
@@ -395,17 +394,17 @@
     return criteria.reduce((bool a, bool b) => a && b);
   }
 
-  Future<Null> _setupUpdatedApplicationBundle(ApplicationPackage app) async {
+  Future<Null> _setupUpdatedApplicationBundle(ApplicationPackage app, String flavor) async {
     await _sideloadUpdatedAssetsForInstalledApplicationBundle(app);
 
     if (!await _applicationIsInstalledAndRunning(app))
-      return _buildAndInstallApplicationBundle(app);
+      return _buildAndInstallApplicationBundle(app, flavor);
   }
 
-  Future<Null> _buildAndInstallApplicationBundle(ApplicationPackage app) async {
+  Future<Null> _buildAndInstallApplicationBundle(ApplicationPackage app, String flavor) async {
     // Step 1: Build the Xcode project.
     // The build mode for the simulator is always debug.
-    final XcodeBuildResult buildResult = await buildXcodeProject(app: app, mode: BuildMode.debug, buildForDevice: false);
+    final XcodeBuildResult buildResult = await buildXcodeProject(app: app, buildInfo: new BuildInfo(BuildMode.debug, flavor), buildForDevice: false);
     if (!buildResult.success)
       throwToolExit('Could not build the application for the simulator.');
 
diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
index 6b66067..fb65f40 100644
--- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart
+++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
@@ -7,6 +7,7 @@
 import '../artifacts.dart';
 import '../base/file_system.dart';
 import '../base/process.dart';
+import '../base/utils.dart';
 import '../build_info.dart';
 import '../cache.dart';
 import '../globals.dart';
@@ -20,7 +21,7 @@
 
 void updateXcodeGeneratedProperties({
   @required String projectPath,
-  @required BuildMode mode,
+  @required BuildInfo buildInfo,
   @required String target,
   @required bool hasPlugins,
 }) {
@@ -38,21 +39,21 @@
   localsBuffer.writeln('FLUTTER_TARGET=$target');
 
   // The runtime mode for the current build.
-  localsBuffer.writeln('FLUTTER_BUILD_MODE=${getModeName(mode)}');
+  localsBuffer.writeln('FLUTTER_BUILD_MODE=${buildInfo.modeName}');
 
   // The build outputs directory, relative to FLUTTER_APPLICATION_PATH.
   localsBuffer.writeln('FLUTTER_BUILD_DIR=${getBuildDirectory()}');
 
   localsBuffer.writeln('SYMROOT=\${SOURCE_ROOT}/../${getIosBuildDirectory()}');
 
-  localsBuffer.writeln('FLUTTER_FRAMEWORK_DIR=${flutterFrameworkDir(mode)}');
+  localsBuffer.writeln('FLUTTER_FRAMEWORK_DIR=${flutterFrameworkDir(buildInfo.mode)}');
 
   if (artifacts is LocalEngineArtifacts) {
     final LocalEngineArtifacts localEngineArtifacts = artifacts;
     localsBuffer.writeln('LOCAL_ENGINE=${localEngineArtifacts.engineOutPath}');
   }
 
-  // Add dependency to CocoaPods' generated project only if plugns are used.
+  // 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"');
 
@@ -74,7 +75,6 @@
   return settings;
 }
 
-
 /// Substitutes variables in [str] with their values from the specified Xcode
 /// project and target.
 String substituteXcodeVariables(String str, Map<String, String> xcodeBuildSettings) {
@@ -84,3 +84,109 @@
 
   return str.replaceAllMapped(_varExpr, (Match m) => xcodeBuildSettings[m[1]] ?? m[0]);
 }
+
+/// Information about an Xcode project.
+///
+/// Represents the output of `xcodebuild -list`.
+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>[];
+    final List<String> schemes = <String>[];
+    List<String> collector;
+    for (String line in output.split('\n')) {
+      if (line.isEmpty) {
+        collector = null;
+        continue;
+      } else if (line.endsWith('Targets:')) {
+        collector = targets;
+        continue;
+      } else if (line.endsWith('Build Configurations:')) {
+        collector = buildConfigurations;
+        continue;
+      } else if (line.endsWith('Schemes:')) {
+        collector = schemes;
+        continue;
+      }
+      collector?.add(line.trim());
+    }
+    return new XcodeProjectInfo(targets, buildConfigurations, schemes);
+  }
+
+  final List<String> targets;
+  final List<String> buildConfigurations;
+  final List<String> schemes;
+
+  bool get definesCustomTargets => !(targets.contains('Runner') && targets.length == 1);
+  bool get definesCustomSchemes => !(schemes.contains('Runner') && schemes.length == 1);
+  bool get definesCustomBuildConfigurations {
+    return !(buildConfigurations.contains('Debug') &&
+        buildConfigurations.contains('Release') &&
+        buildConfigurations.length == 2);
+  }
+
+  /// The expected scheme for [buildInfo].
+  static String expectedSchemeFor(BuildInfo buildInfo) {
+    return toTitleCase(buildInfo.flavor ?? 'runner');
+  }
+
+  /// The expected build configuration for [buildInfo] and [scheme].
+  static String expectedBuildConfigurationFor(BuildInfo buildInfo, String scheme) {
+    final String baseConfiguration = _baseConfigurationFor(buildInfo);
+    if (buildInfo.flavor == null)
+      return baseConfiguration;
+    else
+      return baseConfiguration + '-$scheme';
+  }
+
+  /// Returns unique scheme matching [buildInfo], or null, if there is no unique
+  /// best match.
+  String schemeFor(BuildInfo buildInfo) {
+    final String expectedScheme = expectedSchemeFor(buildInfo);
+    if (schemes.contains(expectedScheme))
+      return expectedScheme;
+    return _uniqueMatch(schemes, (String candidate) {
+      return candidate.toLowerCase() == expectedScheme.toLowerCase();
+    });
+  }
+
+  /// Returns unique build configuration matching [buildInfo] and [scheme], or
+  /// null, if there is no unique best match.
+  String buildConfigurationFor(BuildInfo buildInfo, String scheme) {
+    final String expectedConfiguration = expectedBuildConfigurationFor(buildInfo, scheme);
+    if (buildConfigurations.contains(expectedConfiguration))
+      return expectedConfiguration;
+    final String baseConfiguration = _baseConfigurationFor(buildInfo);
+    return _uniqueMatch(buildConfigurations, (String candidate) {
+      candidate = candidate.toLowerCase();
+      if (buildInfo.flavor == null)
+        return candidate == expectedConfiguration.toLowerCase();
+      else
+        return candidate.contains(baseConfiguration.toLowerCase()) && candidate.contains(scheme.toLowerCase());
+    });
+  }
+
+  static String _baseConfigurationFor(BuildInfo buildInfo) => buildInfo.isDebug ? 'Debug' : 'Release';
+
+  static String _uniqueMatch(Iterable<String> strings, bool matches(String s)) {
+    final List<String> options = strings.where(matches).toList();
+    if (options.length == 1)
+      return options.first;
+    else
+      return null;
+  }
+
+  @override
+  String toString() {
+    return 'XcodeProjectInfo($targets, $buildConfigurations, $schemes)';
+  }
+}
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index e2dfd13..ecb404c 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -208,7 +208,7 @@
     bool shouldBuild,
   }) async {
     final bool prebuiltMode = hotRunner.applicationBinary != null;
-    final String modeName = getModeName(hotRunner.debuggingOptions.buildMode);
+    final String modeName = hotRunner.debuggingOptions.buildInfo.modeName;
     printStatus('Launching ${getDisplayPath(hotRunner.mainPath)} on ${device.name} in $modeName mode...');
 
     final TargetPlatform targetPlatform = await device.targetPlatform;
@@ -234,7 +234,6 @@
     final bool hasDirtyDependencies = hotRunner.hasDirtyDependencies(this);
     final Future<LaunchResult> futureResult = device.startApp(
       package,
-      hotRunner.debuggingOptions.buildMode,
       mainPath: hotRunner.mainPath,
       debuggingOptions: hotRunner.debuggingOptions,
       platformArgs: platformArgs,
@@ -268,7 +267,7 @@
       applicationBinary: coldRunner.applicationBinary
     );
 
-    final String modeName = getModeName(coldRunner.debuggingOptions.buildMode);
+    final String modeName = coldRunner.debuggingOptions.buildInfo.modeName;
     final bool prebuiltMode = coldRunner.applicationBinary != null;
     if (coldRunner.mainPath == null) {
       assert(prebuiltMode);
@@ -295,7 +294,6 @@
     final bool hasDirtyDependencies = coldRunner.hasDirtyDependencies(this);
     final LaunchResult result = await device.startApp(
       package,
-      coldRunner.debuggingOptions.buildMode,
       mainPath: coldRunner.mainPath,
       debuggingOptions: coldRunner.debuggingOptions,
       platformArgs: platformArgs,
@@ -378,9 +376,9 @@
   AssetBundle _assetBundle;
   AssetBundle get assetBundle => _assetBundle;
 
-  bool get isRunningDebug => debuggingOptions.buildMode == BuildMode.debug;
-  bool get isRunningProfile => debuggingOptions.buildMode == BuildMode.profile;
-  bool get isRunningRelease => debuggingOptions.buildMode == BuildMode.release;
+  bool get isRunningDebug => debuggingOptions.buildInfo.isDebug;
+  bool get isRunningProfile => debuggingOptions.buildInfo.isProfile;
+  bool get isRunningRelease => debuggingOptions.buildInfo.isRelease;
   bool get supportsServiceProtocol => isRunningDebug || isRunningProfile;
 
   /// Start the app and keep the process running during its lifetime.
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index 56d2bd9..6cecc1f 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -133,6 +133,19 @@
     return _defaultBuildMode;
   }
 
+  void usesFlavorOption() {
+    argParser.addOption(
+      'flavor',
+      help: 'Build a custom app flavor as defined by platform-specific build setup.\n'
+        'Supports the use of product flavors in Android Gradle scripts.\n'
+        'Supports the use of custom Xcode schemes.'
+    );
+  }
+
+  BuildInfo getBuildInfo() {
+    return new BuildInfo(getBuildMode(), argResults['flavor']);
+  }
+
   void setupApplicationPackages() {
     applicationPackages ??= new ApplicationPackageStore();
   }
diff --git a/packages/flutter_tools/test/android/gradle_test.dart b/packages/flutter_tools/test/android/gradle_test.dart
index 457ba81..daf399f 100644
--- a/packages/flutter_tools/test/android/gradle_test.dart
+++ b/packages/flutter_tools/test/android/gradle_test.dart
@@ -2,33 +2,89 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'package:file/file.dart';
-import 'package:file/memory.dart';
 import 'package:flutter_tools/src/android/gradle.dart';
+import 'package:flutter_tools/src/base/file_system.dart';
+import 'package:flutter_tools/src/build_info.dart';
 import 'package:test/test.dart';
 
-import '../src/context.dart';
 
 const String _kBuildDirectory = '/build/app/outputs';
 
 void main() {
-  FileSystem fs;
+  group('gradle project', () {
+    GradleProject projectFrom(String properties) => new GradleProject.fromAppProperties(properties);
 
-  setUp(() {
-    fs = new MemoryFileSystem();
-    fs.directory('$_kBuildDirectory/release').createSync(recursive: true);
-    fs.file('$_kBuildDirectory/app-debug.apk').createSync();
-    fs.file('$_kBuildDirectory/release/app-release.apk').createSync();
-  });
-
-  group('gradle', () {
-    testUsingContext('findApkFile', () {
-      expect(findApkFile(_kBuildDirectory, 'debug').path,
-             '/build/app/outputs/app-debug.apk');
-      expect(findApkFile(_kBuildDirectory, 'release').path,
-             '/build/app/outputs/release/app-release.apk');
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fs,
+    test('should extract build directory from app properties', () {
+      final GradleProject project = projectFrom('''
+someProperty: someValue
+buildDir: /Users/some/apps/hello/build/app
+someOtherProperty: someOtherValue
+      ''');
+      expect(project.apkDirectory, fs.path.normalize('/Users/some/apps/hello/build/app/outputs/apk'));
+    });
+    test('should extract default build variants from app properties', () {
+      final GradleProject project = projectFrom('''
+someProperty: someValue
+assemble: task ':app:assemble'
+assembleAndroidTest: task ':app:assembleAndroidTest'
+assembleDebug: task ':app:assembleDebug'
+assembleProfile: task ':app:assembleProfile'
+assembleRelease: task ':app:assembleRelease'
+buildDir: /Users/some/apps/hello/build/app
+someOtherProperty: someOtherValue
+      ''');
+      expect(project.buildTypes, <String>['debug', 'profile', 'release']);
+      expect(project.productFlavors, isEmpty);
+    });
+    test('should extract custom build variants from app properties', () {
+      final GradleProject project = projectFrom('''
+someProperty: someValue
+assemble: task ':app:assemble'
+assembleAndroidTest: task ':app:assembleAndroidTest'
+assembleDebug: task ':app:assembleDebug'
+assembleFree: task ':app:assembleFree'
+assembleFreeAndroidTest: task ':app:assembleFreeAndroidTest'
+assembleFreeDebug: task ':app:assembleFreeDebug'
+assembleFreeProfile: task ':app:assembleFreeProfile'
+assembleFreeRelease: task ':app:assembleFreeRelease'
+assemblePaid: task ':app:assemblePaid'
+assemblePaidAndroidTest: task ':app:assemblePaidAndroidTest'
+assemblePaidDebug: task ':app:assemblePaidDebug'
+assemblePaidProfile: task ':app:assemblePaidProfile'
+assemblePaidRelease: task ':app:assemblePaidRelease'
+assembleProfile: task ':app:assembleProfile'
+assembleRelease: task ':app:assembleRelease'
+buildDir: /Users/some/apps/hello/build/app
+someOtherProperty: someOtherValue
+      ''');
+      expect(project.buildTypes, <String>['debug', 'profile', 'release']);
+      expect(project.productFlavors, <String>['free', 'paid']);
+    });
+    test('should provide apk file name for default build types', () {
+      final GradleProject project = new GradleProject(<String>['debug', 'profile', 'release'], <String>[], '/some/dir');
+      expect(project.apkFileFor(BuildInfo.debug), 'app-debug.apk');
+      expect(project.apkFileFor(BuildInfo.profile), 'app-profile.apk');
+      expect(project.apkFileFor(BuildInfo.release), 'app-release.apk');
+      expect(project.apkFileFor(const BuildInfo(BuildMode.release, 'unknown')), isNull);
+    });
+    test('should provide apk file name for flavored build types', () {
+      final GradleProject project = new GradleProject(<String>['debug', 'profile', 'release'], <String>['free', 'paid'], '/some/dir');
+      expect(project.apkFileFor(const BuildInfo(BuildMode.debug, 'free')), 'app-free-debug.apk');
+      expect(project.apkFileFor(const BuildInfo(BuildMode.release, 'paid')), 'app-paid-release.apk');
+      expect(project.apkFileFor(const BuildInfo(BuildMode.release, 'unknown')), isNull);
+    });
+    test('should provide assemble task name for default build types', () {
+      final GradleProject project = new GradleProject(<String>['debug', 'profile', 'release'], <String>[], '/some/dir');
+      expect(project.assembleTaskFor(BuildInfo.debug), 'assembleDebug');
+      expect(project.assembleTaskFor(BuildInfo.profile), 'assembleProfile');
+      expect(project.assembleTaskFor(BuildInfo.release), 'assembleRelease');
+      expect(project.assembleTaskFor(const BuildInfo(BuildMode.release, 'unknown')), isNull);
+    });
+    test('should provide assemble task name for flavored build types', () {
+      final GradleProject project = new GradleProject(<String>['debug', 'profile', 'release'], <String>['free', 'paid'], '/some/dir');
+      expect(project.assembleTaskFor(const BuildInfo(BuildMode.debug, 'free')), 'assembleFreeDebug');
+      expect(project.assembleTaskFor(const BuildInfo(BuildMode.release, 'paid')), 'assemblePaidRelease');
+      expect(project.assembleTaskFor(const BuildInfo(BuildMode.release, 'unknown')), isNull);
     });
   });
 }
diff --git a/packages/flutter_tools/test/ios/xcodeproj_test.dart b/packages/flutter_tools/test/ios/xcodeproj_test.dart
new file mode 100644
index 0000000..a8eeb7f
--- /dev/null
+++ b/packages/flutter_tools/test/ios/xcodeproj_test.dart
@@ -0,0 +1,120 @@
+import 'package:test/test.dart';
+
+import 'package:flutter_tools/src/build_info.dart';
+import 'package:flutter_tools/src/ios/xcodeproj.dart';
+
+void main() {
+  group('Xcode project properties', () {
+    test('properties from default project can be parsed', () {
+      final String output = '''
+Information about project "Runner":
+    Targets:
+        Runner
+
+    Build Configurations:
+        Debug
+        Release
+
+    If no build configuration is specified and -scheme is not passed then "Release" is used.
+
+    Schemes:
+        Runner
+
+''';
+      final XcodeProjectInfo info = new XcodeProjectInfo.fromXcodeBuildOutput(output);
+      expect(info.targets, <String>['Runner']);
+      expect(info.schemes, <String>['Runner']);
+      expect(info.buildConfigurations, <String>['Debug', 'Release']);
+    });
+    test('properties from project with custom schemes can be parsed', () {
+      final String output = '''
+Information about project "Runner":
+    Targets:
+        Runner
+
+    Build Configurations:
+        Debug (Free)
+        Debug (Paid)
+        Release (Free)
+        Release (Paid)
+
+    If no build configuration is specified and -scheme is not passed then "Release (Free)" is used.
+
+    Schemes:
+        Free
+        Paid
+
+''';
+      final XcodeProjectInfo info = new XcodeProjectInfo.fromXcodeBuildOutput(output);
+      expect(info.targets, <String>['Runner']);
+      expect(info.schemes, <String>['Free', 'Paid']);
+      expect(info.buildConfigurations, <String>['Debug (Free)', 'Debug (Paid)', 'Release (Free)', 'Release (Paid)']);
+    });
+    test('expected scheme for non-flavored build is Runner', () {
+      expect(XcodeProjectInfo.expectedSchemeFor(BuildInfo.debug), 'Runner');
+      expect(XcodeProjectInfo.expectedSchemeFor(BuildInfo.profile), 'Runner');
+      expect(XcodeProjectInfo.expectedSchemeFor(BuildInfo.release), 'Runner');
+    });
+    test('expected build configuration for non-flavored build is derived from BuildMode', () {
+      expect(XcodeProjectInfo.expectedBuildConfigurationFor(BuildInfo.debug, 'Runner'), 'Debug');
+      expect(XcodeProjectInfo.expectedBuildConfigurationFor(BuildInfo.profile, 'Runner'), 'Release');
+      expect(XcodeProjectInfo.expectedBuildConfigurationFor(BuildInfo.release, 'Runner'), 'Release');
+    });
+    test('expected scheme for flavored build is the title-cased flavor', () {
+      expect(XcodeProjectInfo.expectedSchemeFor(const BuildInfo(BuildMode.debug, 'hello')), 'Hello');
+      expect(XcodeProjectInfo.expectedSchemeFor(const BuildInfo(BuildMode.profile, 'HELLO')), 'HELLO');
+      expect(XcodeProjectInfo.expectedSchemeFor(const BuildInfo(BuildMode.release, 'Hello')), 'Hello');
+    });
+    test('expected build configuration for flavored build is Mode-Flavor', () {
+      expect(XcodeProjectInfo.expectedBuildConfigurationFor(const BuildInfo(BuildMode.debug, 'hello'), 'Hello'), 'Debug-Hello');
+      expect(XcodeProjectInfo.expectedBuildConfigurationFor(const BuildInfo(BuildMode.profile, 'HELLO'), 'Hello'), 'Release-Hello');
+      expect(XcodeProjectInfo.expectedBuildConfigurationFor(const BuildInfo(BuildMode.release, 'Hello'), 'Hello'), 'Release-Hello');
+    });
+    test('scheme for default project is Runner', () {
+      final XcodeProjectInfo info = new XcodeProjectInfo(<String>['Runner'], <String>['Debug', 'Release'], <String>['Runner']);
+      expect(info.schemeFor(BuildInfo.debug), 'Runner');
+      expect(info.schemeFor(BuildInfo.profile), 'Runner');
+      expect(info.schemeFor(BuildInfo.release), 'Runner');
+      expect(info.schemeFor(const BuildInfo(BuildMode.debug, 'unknown')), isNull);
+    });
+    test('build configuration for default project is matched against BuildMode', () {
+      final XcodeProjectInfo info = new XcodeProjectInfo(<String>['Runner'], <String>['Debug', 'Release'], <String>['Runner']);
+      expect(info.buildConfigurationFor(BuildInfo.debug, 'Runner'), 'Debug');
+      expect(info.buildConfigurationFor(BuildInfo.profile, 'Runner'), 'Release');
+      expect(info.buildConfigurationFor(BuildInfo.release, 'Runner'), 'Release');
+    });
+    test('scheme for project with custom schemes is matched against flavor', () {
+      final XcodeProjectInfo info = new XcodeProjectInfo(
+        <String>['Runner'],
+        <String>['Debug (Free)', 'Debug (Paid)', 'Release (Free)', 'Release (Paid)'],
+        <String>['Free', 'Paid'],
+      );
+      expect(info.schemeFor(const BuildInfo(BuildMode.debug, 'free')), 'Free');
+      expect(info.schemeFor(const BuildInfo(BuildMode.profile, 'Free')), 'Free');
+      expect(info.schemeFor(const BuildInfo(BuildMode.release, 'paid')), 'Paid');
+      expect(info.schemeFor(const BuildInfo(BuildMode.debug, null)), isNull);
+      expect(info.schemeFor(const BuildInfo(BuildMode.debug, 'unknown')), isNull);
+    });
+    test('build configuration for project with custom schemes is matched against BuildMode and flavor', () {
+      final XcodeProjectInfo info = new XcodeProjectInfo(
+        <String>['Runner'],
+        <String>['debug (free)', 'Debug paid', 'release - Free', 'Release-Paid'],
+        <String>['Free', 'Paid'],
+      );
+      expect(info.buildConfigurationFor(const BuildInfo(BuildMode.debug, 'free'), 'Free'), 'debug (free)');
+      expect(info.buildConfigurationFor(const BuildInfo(BuildMode.debug, 'Paid'), 'Paid'), 'Debug paid');
+      expect(info.buildConfigurationFor(const BuildInfo(BuildMode.profile, 'FREE'), 'Free'), 'release - Free');
+      expect(info.buildConfigurationFor(const BuildInfo(BuildMode.release, 'paid'), 'Paid'), 'Release-Paid');
+    });
+    test('build configuration for project with inconsistent naming is null', () {
+      final XcodeProjectInfo info = new XcodeProjectInfo(
+        <String>['Runner'],
+        <String>['Debug-F', 'Dbg Paid', 'Rel Free', 'Release Full'],
+        <String>['Free', 'Paid'],
+      );
+      expect(info.buildConfigurationFor(const BuildInfo(BuildMode.debug, 'Free'), 'Free'), null);
+      expect(info.buildConfigurationFor(const BuildInfo(BuildMode.profile, 'Free'), 'Free'), null);
+      expect(info.buildConfigurationFor(const BuildInfo(BuildMode.release, 'Paid'), 'Paid'), null);
+    });
+  });
+}