Move stocks example app into dev/benchmarks/test_apps (#49559)
The stocks example app is outdated and deprecated, but we still use it for some benchmark tests. Moving it into the benchmarks directory to indicate its status.
diff --git a/dev/benchmarks/microbenchmarks/pubspec.yaml b/dev/benchmarks/microbenchmarks/pubspec.yaml
index 98b3cc1..fee9a0e 100644
--- a/dev/benchmarks/microbenchmarks/pubspec.yaml
+++ b/dev/benchmarks/microbenchmarks/pubspec.yaml
@@ -12,7 +12,7 @@
flutter_test:
sdk: flutter
stocks:
- path: ../../../examples/stocks
+ path: ../test_apps/stocks
test: 1.9.4
_fe_analyzer_shared: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
diff --git a/dev/benchmarks/test_apps/stocks/README.md b/dev/benchmarks/test_apps/stocks/README.md
new file mode 100644
index 0000000..d8d0785
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/README.md
@@ -0,0 +1,44 @@
+# Stocks
+
+Demo app for the material design widgets and other features provided by Flutter.
+
+## Building
+
+You can follow these instructions to build the stocks app
+and install it onto your device.
+
+### Prerequisites
+
+If you are new to Flutter, please first follow
+the [Flutter Setup](https://flutter.dev/setup/) guide.
+
+### Building and installing the stocks demo app
+
+* `cd $FLUTTER_ROOT/examples/stocks`
+* `flutter pub get`
+* `flutter run --release`
+
+The `flutter run --release` command both builds and installs the Flutter app.
+
+## Internationalization
+
+This app has been internationalized (just enough to show how it's
+done). It's an example of how one can do so with the
+[Dart intl package](https://pub.dev/packages/intl).
+
+The [Flutter Internationalization Tutorial](https://flutter.dev/tutorials/internationalization/)
+covers Flutter app internationalization in general.
+
+See [regenerate.md](lib/i18n/regenerate.md) for an explanation
+of how the Dart internationalization tools, like
+`intl_translation:generate_from_arb`, were used to generate
+localizations for this app.
+
+## Icon
+
+Icon was created using Android Asset Studio:
+https://romannurik.github.io/AndroidAssetStudio/icons-launcher.html#foreground.type=image&foreground.space.trim=0&foreground.space.pad=0&foreColor=607d8b%2C0&crop=0&backgroundShape=square&backColor=fff%2C100&effects=none
+
+From this clipart:
+https://openclipart.org/detail/30403/tango-go-up
+Which is public domain.
diff --git a/dev/benchmarks/test_apps/stocks/android/app/build.gradle b/dev/benchmarks/test_apps/stocks/android/app/build.gradle
new file mode 100644
index 0000000..160ac2d
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/android/app/build.gradle
@@ -0,0 +1,64 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+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.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion 28
+
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+
+ defaultConfig {
+ applicationId "io.flutter.examples.stocks"
+ minSdkVersion 16
+ targetSdkVersion 28
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test:runner:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
+}
diff --git a/dev/benchmarks/test_apps/stocks/android/app/src/main/AndroidManifest.xml b/dev/benchmarks/test_apps/stocks/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..e779929
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<!-- Copyright 2014 The Flutter Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file. -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="io.flutter.examples.stocks">
+
+ <!-- The INTERNET permission is required for development. Specifically,
+ flutter needs it to communicate with the running application
+ to allow setting breakpoints, to provide hot reload, etc.
+ -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+
+ <application android:label="Stocks" android:icon="@mipmap/ic_launcher">
+ <activity android:name="io.flutter.embedding.android.FlutterActivity"
+ android:theme="@android:style/Theme.Black.NoTitleBar"
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
+ 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>
+ <!-- Don't delete the meta-data below.
+ This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+ <meta-data
+ android:name="flutterEmbedding"
+ android:value="2" />
+ </application>
+</manifest>
diff --git a/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..f244429
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..ac6f2d0
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..d0c9cd2
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d907cf4
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..3643033
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/android/build.gradle b/dev/benchmarks/test_apps/stocks/android/build.gradle
new file mode 100644
index 0000000..5df71fc
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/android/build.gradle
@@ -0,0 +1,33 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.5.0'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/dev/benchmarks/test_apps/stocks/android/gradle.properties b/dev/benchmarks/test_apps/stocks/android/gradle.properties
new file mode 100644
index 0000000..a673820
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/android/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
+android.enableR8=true
diff --git a/dev/benchmarks/test_apps/stocks/android/gradle/wrapper/gradle-wrapper.properties b/dev/benchmarks/test_apps/stocks/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000..296b146
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/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-5.6.2-all.zip
diff --git a/dev/benchmarks/test_apps/stocks/android/settings.gradle b/dev/benchmarks/test_apps/stocks/android/settings.gradle
new file mode 100644
index 0000000..dbc3b58
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/android/settings.gradle
@@ -0,0 +1,19 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+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/benchmarks/test_apps/stocks/fuchsia/meta/stocks.cmx b/dev/benchmarks/test_apps/stocks/fuchsia/meta/stocks.cmx
new file mode 100644
index 0000000..6a1acee
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/fuchsia/meta/stocks.cmx
@@ -0,0 +1,22 @@
+{
+ "program": {
+ "data": "data/stocks"
+ },
+ "sandbox": {
+ "services": [
+ "fuchsia.cobalt.LoggerFactory",
+ "fuchsia.fonts.Provider",
+ "fuchsia.logger.LogSink",
+ "fuchsia.modular.Clipboard",
+ "fuchsia.modular.ContextWriter",
+ "fuchsia.modular.DeviceMap",
+ "fuchsia.modular.ModuleContext",
+ "fuchsia.sys.Environment",
+ "fuchsia.sys.Launcher",
+ "fuchsia.testing.runner.TestRunner",
+ "fuchsia.ui.input.ImeService",
+ "fuchsia.ui.policy.Presenter",
+ "fuchsia.ui.scenic.Scenic"
+ ]
+ }
+}
diff --git a/dev/benchmarks/test_apps/stocks/ios/Flutter/AppFrameworkInfo.plist b/dev/benchmarks/test_apps/stocks/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..6b4c0f7
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+<?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>$(DEVELOPMENT_LANGUAGE)</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>MinimumOSVersion</key>
+ <string>8.0</string>
+</dict>
+</plist>
diff --git a/dev/benchmarks/test_apps/stocks/ios/Flutter/Debug.xcconfig b/dev/benchmarks/test_apps/stocks/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/dev/benchmarks/test_apps/stocks/ios/Flutter/Release.xcconfig b/dev/benchmarks/test_apps/stocks/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner.xcodeproj/project.pbxproj b/dev/benchmarks/test_apps/stocks/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..83c8e59
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,510 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 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, ); }; };
+ 74970F6E1EDC1810000507F3 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 74970F6D1EDC1810000507F3 /* GeneratedPluginRegistrant.m */; };
+ 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, ); }; };
+ 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 */
+ 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>"; };
+ 74970F6C1EDC1810000507F3 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
+ 74970F6D1EDC1810000507F3 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; 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>"; };
+ 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 = (
+ 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 */,
+ CF3B75C9A7D2FA2A4C99F110 /* Frameworks */,
+ );
+ sourceTree = "<group>";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 74970F6C1EDC1810000507F3 /* GeneratedPluginRegistrant.h */,
+ 74970F6D1EDC1810000507F3 /* GeneratedPluginRegistrant.m */,
+ 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
+ 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 97C146F11CF9000F007C117D /* Supporting Files */,
+ );
+ path = Runner;
+ sourceTree = "<group>";
+ };
+ 97C146F11CF9000F007C117D /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146F21CF9000F007C117D /* main.m */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ CF3B75C9A7D2FA2A4C99F110 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ 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 = 1020;
+ ORGANIZATIONNAME = "The Flutter Authors";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = en;
+ 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 = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist 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 */,
+ 74970F6E1EDC1810000507F3 /* 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 */
+ 2445AC6C21828DE2009C639E /* Profile */ = {
+ 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_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ 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;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 2445AC6D21828DE2009C639E /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ 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 = io.flutter.examples.stocks;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ 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_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ 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;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ 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_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ 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;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ 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 = io.flutter.examples.stocks;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ 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 = io.flutter.examples.stocks;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ 2445AC6C21828DE2009C639E /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ 2445AC6D21828DE2009C639E /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/dev/benchmarks/test_apps/stocks/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/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/benchmarks/test_apps/stocks/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/dev/benchmarks/test_apps/stocks/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?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>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/dev/benchmarks/test_apps/stocks/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..a28140c
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1020"
+ 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"
+ 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"
+ 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 = "Profile"
+ 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">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner.xcworkspace/contents.xcworkspacedata b/dev/benchmarks/test_apps/stocks/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/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/benchmarks/test_apps/stocks/ios/Runner/AppDelegate.h b/dev/benchmarks/test_apps/stocks/ios/Runner/AppDelegate.h
new file mode 100644
index 0000000..a78a945
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/AppDelegate.h
@@ -0,0 +1,10 @@
+// Copyright 2014 The Flutter 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 <UIKit/UIKit.h>
+#import <Flutter/Flutter.h>
+
+@interface AppDelegate : FlutterAppDelegate
+
+@end
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/AppDelegate.m b/dev/benchmarks/test_apps/stocks/ios/Runner/AppDelegate.m
new file mode 100644
index 0000000..d41031a
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/AppDelegate.m
@@ -0,0 +1,15 @@
+// Copyright 2014 The Flutter 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 "AppDelegate.h"
+#import "GeneratedPluginRegistrant.h"
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ [GeneratedPluginRegistrant registerWithRegistry:self];
+ // Override point for customization after application launch.
+ return [super application:application didFinishLaunchingWithOptions:launchOptions];
+}
+@end
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..7c0c222
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,106 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-Notification@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-Notification@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-Small@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-Small@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-Small-40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-Small-40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-Notification.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-Notification@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-Small.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-Small@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-Small-40.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-Small-40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-76.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-83.5@2x.png",
+ "scale" : "2x"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
new file mode 100644
index 0000000..bf55f17
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
new file mode 100644
index 0000000..062b37a
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76.png b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76.png
new file mode 100644
index 0000000..ddbe932
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
new file mode 100644
index 0000000..929edb5
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
new file mode 100644
index 0000000..1b8f3de
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification.png b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification.png
new file mode 100644
index 0000000..810eda2
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification@2x.png b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification@2x.png
new file mode 100644
index 0000000..e9a6b95
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification@2x.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification@3x.png b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification@3x.png
new file mode 100644
index 0000000..28db17e
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification@3x.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png
new file mode 100644
index 0000000..e9a6b95
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png
new file mode 100644
index 0000000..865962d
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png
new file mode 100644
index 0000000..bf55f17
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small.png b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
new file mode 100644
index 0000000..61342bc
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
new file mode 100644
index 0000000..a6cb12f
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
new file mode 100644
index 0000000..bc5f294
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
Binary files differ
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Base.lproj/LaunchScreen.storyboard b/dev/benchmarks/test_apps/stocks/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..ebf48f6
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,27 @@
+<?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" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
+ <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <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="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/dev/benchmarks/test_apps/stocks/ios/Runner/Base.lproj/Main.storyboard b/dev/benchmarks/test_apps/stocks/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/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/benchmarks/test_apps/stocks/ios/Runner/Info.plist b/dev/benchmarks/test_apps/stocks/ios/Runner/Info.plist
new file mode 100644
index 0000000..0a1d774
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/Info.plist
@@ -0,0 +1,45 @@
+<?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>$(DEVELOPMENT_LANGUAGE)</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>Stocks</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>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/benchmarks/test_apps/stocks/ios/Runner/main.m b/dev/benchmarks/test_apps/stocks/ios/Runner/main.m
new file mode 100644
index 0000000..8607072
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/ios/Runner/main.m
@@ -0,0 +1,14 @@
+// Copyright 2014 The Flutter 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 <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/benchmarks/test_apps/stocks/lib/i18n/.dartignore b/dev/benchmarks/test_apps/stocks/lib/i18n/.dartignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/i18n/.dartignore
diff --git a/dev/benchmarks/test_apps/stocks/lib/i18n/messages_all.dart b/dev/benchmarks/test_apps/stocks/lib/i18n/messages_all.dart
new file mode 100644
index 0000000..867afc7
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/i18n/messages_all.dart
@@ -0,0 +1,67 @@
+// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
+// This is a library that looks up messages for specific locales by
+// delegating to the appropriate library.
+
+// Ignore issues from commonly used lints in this file.
+// ignore_for_file:implementation_imports, file_names, unnecessary_new
+// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering
+// ignore_for_file:argument_type_not_assignable, invalid_assignment
+// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases
+// ignore_for_file:comment_references
+
+import 'dart:async';
+
+import 'package:intl/intl.dart';
+import 'package:intl/message_lookup_by_library.dart';
+import 'package:intl/src/intl_helpers.dart';
+
+import 'messages_en_US.dart' as messages_en_us;
+import 'messages_es_ES.dart' as messages_es_es;
+
+typedef Future<dynamic> LibraryLoader();
+Map<String, LibraryLoader> _deferredLibraries = {
+ 'en_US': () => new Future.value(null),
+ 'es_ES': () => new Future.value(null),
+};
+
+MessageLookupByLibrary _findExact(String localeName) {
+ switch (localeName) {
+ case 'en_US':
+ return messages_en_us.messages;
+ case 'es_ES':
+ return messages_es_es.messages;
+ default:
+ return null;
+ }
+}
+
+/// User programs should call this before using [localeName] for messages.
+Future<bool> initializeMessages(String localeName) async {
+ var availableLocale = Intl.verifiedLocale(
+ localeName,
+ (locale) => _deferredLibraries[locale] != null,
+ onFailure: (_) => null);
+ if (availableLocale == null) {
+ return new Future.value(false);
+ }
+ var lib = _deferredLibraries[availableLocale];
+ await (lib == null ? new Future.value(false) : lib());
+ initializeInternalMessageLookup(() => new CompositeMessageLookup());
+ messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
+ return new Future.value(true);
+}
+
+bool _messagesExistFor(String locale) {
+ try {
+ return _findExact(locale) != null;
+ } catch (e) {
+ return false;
+ }
+}
+
+MessageLookupByLibrary _findGeneratedMessagesFor(String locale) {
+ var actualLocale = Intl.verifiedLocale(locale, _messagesExistFor,
+ onFailure: (_) => null);
+ if (actualLocale == null) return null;
+ return _findExact(actualLocale);
+}
diff --git a/dev/benchmarks/test_apps/stocks/lib/i18n/messages_en_US.dart b/dev/benchmarks/test_apps/stocks/lib/i18n/messages_en_US.dart
new file mode 100644
index 0000000..fedb57c
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/i18n/messages_en_US.dart
@@ -0,0 +1,28 @@
+// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
+// This is a library that provides messages for a en_US locale. All the
+// messages from the main program should be duplicated here with the same
+// function name.
+
+// Ignore issues from commonly used lints in this file.
+// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
+// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
+// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
+// ignore_for_file:unused_import, file_names
+
+import 'package:intl/intl.dart';
+import 'package:intl/message_lookup_by_library.dart';
+
+final messages = new MessageLookup();
+
+typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
+
+class MessageLookup extends MessageLookupByLibrary {
+ String get localeName => 'en_US';
+
+ final messages = _notInlinedMessages(_notInlinedMessages);
+ static _notInlinedMessages(_) => <String, Function> {
+ "market" : MessageLookupByLibrary.simpleMessage("MARKET"),
+ "portfolio" : MessageLookupByLibrary.simpleMessage("PORTFOLIO"),
+ "title" : MessageLookupByLibrary.simpleMessage("Stocks")
+ };
+}
diff --git a/dev/benchmarks/test_apps/stocks/lib/i18n/messages_es_ES.dart b/dev/benchmarks/test_apps/stocks/lib/i18n/messages_es_ES.dart
new file mode 100644
index 0000000..b2c0fd3
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/i18n/messages_es_ES.dart
@@ -0,0 +1,28 @@
+// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
+// This is a library that provides messages for a es_ES locale. All the
+// messages from the main program should be duplicated here with the same
+// function name.
+
+// Ignore issues from commonly used lints in this file.
+// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
+// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
+// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
+// ignore_for_file:unused_import, file_names
+
+import 'package:intl/intl.dart';
+import 'package:intl/message_lookup_by_library.dart';
+
+final messages = new MessageLookup();
+
+typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
+
+class MessageLookup extends MessageLookupByLibrary {
+ String get localeName => 'es_ES';
+
+ final messages = _notInlinedMessages(_notInlinedMessages);
+ static _notInlinedMessages(_) => <String, Function> {
+ "market" : MessageLookupByLibrary.simpleMessage("MERCADO"),
+ "portfolio" : MessageLookupByLibrary.simpleMessage("CARTERA"),
+ "title" : MessageLookupByLibrary.simpleMessage("Acciones")
+ };
+}
diff --git a/dev/benchmarks/test_apps/stocks/lib/i18n/regenerate.md b/dev/benchmarks/test_apps/stocks/lib/i18n/regenerate.md
new file mode 100644
index 0000000..e75b945
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/i18n/regenerate.md
@@ -0,0 +1,26 @@
+# Regenerating the i18n files
+
+The files in this directory are used to generate `stock_strings.dart`, which
+is used by the stocks application to look up localized message strings. The
+stocks app uses the [Dart `intl` package](https://github.com/dart-lang/intl).
+
+Rebuilding everything requires two steps.
+
+1. Create or update the English and Spanish localizations, `stocks_en_US.arb`
+and `stocks_es_ES.arb`. See the [ARB specification](https://github.com/google/app-resource-bundle/wiki/ApplicationResourceBundleSpecification)
+for more info.
+
+2. With `examples/stocks` as the current directory, generate a
+`messages_<locale>.dart` for each `stocks_<locale>.arb` file,
+`messages_all.dart`, and `stock_strings.dart` with the following command:
+
+```dart
+dart ${FLUTTER_PATH}/dev/tools/localization/bin/gen_l10n.dart --arb-dir=lib/i18n \
+ --template-arb-file=stocks_en_US.arb --output-localization-file=stock_strings.dart \
+ --output-class=StockStrings
+```
+
+The `StockStrings` class uses the generated `initializeMessages()`function
+(`messages_all.dart`) to load the localized messages and `Intl.message()`
+to look them up. The generated class's API documentation explains how to add
+the new localizations delegate and supported locales to the Flutter application.
diff --git a/dev/benchmarks/test_apps/stocks/lib/i18n/stock_strings.dart b/dev/benchmarks/test_apps/stocks/lib/i18n/stock_strings.dart
new file mode 100644
index 0000000..8c12e5a
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/i18n/stock_strings.dart
@@ -0,0 +1,136 @@
+import 'dart:async';
+
+import 'package:flutter/widgets.dart';
+import 'package:flutter_localizations/flutter_localizations.dart';
+import 'package:intl/intl.dart';
+
+import 'messages_all.dart';
+
+/// Callers can lookup localized strings with an instance of StockStrings returned
+/// by `StockStrings.of(context)`.
+///
+/// Applications need to include `StockStrings.delegate()` in their app's
+/// localizationDelegates list, and the locales they support in the app's
+/// supportedLocales list. For example:
+///
+/// ```
+/// import 'i18n/stock_strings.dart';
+///
+/// return MaterialApp(
+/// localizationsDelegates: StockStrings.localizationsDelegates,
+/// supportedLocales: StockStrings.supportedLocales,
+/// home: MyApplicationHome(),
+/// );
+/// ```
+///
+/// ## Update pubspec.yaml
+///
+/// Please make sure to update your pubspec.yaml to include the following
+/// packages:
+///
+/// ```
+/// dependencies:
+/// # Internationalization support.
+/// flutter_localizations:
+/// sdk: flutter
+/// intl: 0.16.0
+/// intl_translation: 0.17.7
+///
+/// # rest of dependencies
+/// ```
+///
+/// ## iOS Applications
+///
+/// iOS applications define key application metadata, including supported
+/// locales, in an Info.plist file that is built into the application bundle.
+/// To configure the locales supported by your app, you’ll need to edit this
+/// file.
+///
+/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
+/// Then, in the Project Navigator, open the Info.plist file under the Runner
+/// project’s Runner folder.
+///
+/// Next, select the Information Property List item, select Add Item from the
+/// Editor menu, then select Localizations from the pop-up menu.
+///
+/// Select and expand the newly-created Localizations item then, for each
+/// locale your application supports, add a new item and select the locale
+/// you wish to add from the pop-up menu in the Value field. This list should
+/// be consistent with the languages listed in the StockStrings.supportedLocales
+/// property.
+class StockStrings {
+ StockStrings(Locale locale) : _localeName = Intl.canonicalizedLocale(locale.toString());
+
+ final String _localeName;
+
+ static Future<StockStrings> load(Locale locale) {
+ return initializeMessages(locale.toString())
+ .then<StockStrings>((_) => StockStrings(locale));
+ }
+
+ static StockStrings of(BuildContext context) {
+ return Localizations.of<StockStrings>(context, StockStrings);
+ }
+
+ static const LocalizationsDelegate<StockStrings> delegate = _StockStringsDelegate();
+
+ /// A list of this localizations delegate along with the default localizations
+ /// delegates.
+ ///
+ /// Returns a list of localizations delegates containing this delegate along with
+ /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
+ /// and GlobalWidgetsLocalizations.delegate.
+ static const List<LocalizationsDelegate<dynamic>> localizationsDelegates = <LocalizationsDelegate<dynamic>>[
+ delegate,
+ GlobalMaterialLocalizations.delegate,
+ GlobalCupertinoLocalizations.delegate,
+ GlobalWidgetsLocalizations.delegate,
+ ];
+
+ /// A list of this localizations delegate's supported locales.
+ static const List<Locale> supportedLocales = <Locale>[
+ Locale('en', 'US'),
+ Locale('es', 'ES'),
+ ];
+
+ String get market {
+ return Intl.message(
+ r'MARKET',
+ locale: _localeName,
+ name: 'market',
+ desc: r'Label for the Market tab'
+ );
+ }
+
+ String get portfolio {
+ return Intl.message(
+ r'PORTFOLIO',
+ locale: _localeName,
+ name: 'portfolio',
+ desc: r'Label for the Portfolio tab'
+ );
+ }
+
+ String get title {
+ return Intl.message(
+ r'Stocks',
+ locale: _localeName,
+ name: 'title',
+ desc: r'Title for the Stocks application'
+ );
+ }
+
+}
+
+class _StockStringsDelegate extends LocalizationsDelegate<StockStrings> {
+ const _StockStringsDelegate();
+
+ @override
+ Future<StockStrings> load(Locale locale) => StockStrings.load(locale);
+
+ @override
+ bool isSupported(Locale locale) => <String>['en', 'es'].contains(locale.languageCode);
+
+ @override
+ bool shouldReload(_StockStringsDelegate old) => false;
+}
diff --git a/dev/benchmarks/test_apps/stocks/lib/i18n/stocks_en_US.arb b/dev/benchmarks/test_apps/stocks/lib/i18n/stocks_en_US.arb
new file mode 100644
index 0000000..2d4b4f2
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/i18n/stocks_en_US.arb
@@ -0,0 +1,16 @@
+{
+ "title": "Stocks",
+ "@title": {
+ "description": "Title for the Stocks application"
+ },
+
+ "market": "MARKET",
+ "@market": {
+ "description": "Label for the Market tab"
+ },
+
+ "portfolio": "PORTFOLIO",
+ "@portfolio": {
+ "description": "Label for the Portfolio tab"
+ }
+}
diff --git a/dev/benchmarks/test_apps/stocks/lib/i18n/stocks_es_ES.arb b/dev/benchmarks/test_apps/stocks/lib/i18n/stocks_es_ES.arb
new file mode 100644
index 0000000..3d41a88
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/i18n/stocks_es_ES.arb
@@ -0,0 +1,5 @@
+{
+ "title": "Acciones",
+ "market": "MERCADO",
+ "portfolio": "CARTERA"
+}
diff --git a/dev/benchmarks/test_apps/stocks/lib/main.dart b/dev/benchmarks/test_apps/stocks/lib/main.dart
new file mode 100644
index 0000000..a066621
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/main.dart
@@ -0,0 +1,113 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library stocks;
+
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart' show
+ debugPaintSizeEnabled,
+ debugPaintBaselinesEnabled,
+ debugPaintLayerBordersEnabled,
+ debugPaintPointersEnabled,
+ debugRepaintRainbowEnabled;
+
+import 'i18n/stock_strings.dart';
+import 'stock_data.dart';
+import 'stock_home.dart';
+import 'stock_settings.dart';
+import 'stock_symbol_viewer.dart';
+import 'stock_types.dart';
+
+class StocksApp extends StatefulWidget {
+ @override
+ StocksAppState createState() => StocksAppState();
+}
+
+class StocksAppState extends State<StocksApp> {
+ StockData stocks;
+
+ StockConfiguration _configuration = StockConfiguration(
+ stockMode: StockMode.optimistic,
+ backupMode: BackupMode.enabled,
+ debugShowGrid: false,
+ debugShowSizes: false,
+ debugShowBaselines: false,
+ debugShowLayers: false,
+ debugShowPointers: false,
+ debugShowRainbow: false,
+ showPerformanceOverlay: false,
+ showSemanticsDebugger: false,
+ );
+
+ @override
+ void initState() {
+ super.initState();
+ stocks = StockData();
+ }
+
+ void configurationUpdater(StockConfiguration value) {
+ setState(() {
+ _configuration = value;
+ });
+ }
+
+ ThemeData get theme {
+ switch (_configuration.stockMode) {
+ case StockMode.optimistic:
+ return ThemeData(
+ brightness: Brightness.light,
+ primarySwatch: Colors.purple,
+ );
+ case StockMode.pessimistic:
+ return ThemeData(
+ brightness: Brightness.dark,
+ accentColor: Colors.redAccent,
+ );
+ }
+ assert(_configuration.stockMode != null);
+ return null;
+ }
+
+ Route<dynamic> _getRoute(RouteSettings settings) {
+ if (settings.name == '/stock') {
+ final String symbol = settings.arguments as String;
+ return MaterialPageRoute<void>(
+ settings: settings,
+ builder: (BuildContext context) => StockSymbolPage(symbol: symbol, stocks: stocks),
+ );
+ }
+ // The other paths we support are in the routes table.
+ return null;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ assert(() {
+ debugPaintSizeEnabled = _configuration.debugShowSizes;
+ debugPaintBaselinesEnabled = _configuration.debugShowBaselines;
+ debugPaintLayerBordersEnabled = _configuration.debugShowLayers;
+ debugPaintPointersEnabled = _configuration.debugShowPointers;
+ debugRepaintRainbowEnabled = _configuration.debugShowRainbow;
+ return true;
+ }());
+ return MaterialApp(
+ title: 'Stocks',
+ theme: theme,
+ localizationsDelegates: StockStrings.localizationsDelegates,
+ supportedLocales: StockStrings.supportedLocales,
+ debugShowMaterialGrid: _configuration.debugShowGrid,
+ showPerformanceOverlay: _configuration.showPerformanceOverlay,
+ showSemanticsDebugger: _configuration.showSemanticsDebugger,
+ routes: <String, WidgetBuilder>{
+ '/': (BuildContext context) => StockHome(stocks, _configuration, configurationUpdater),
+ '/settings': (BuildContext context) => StockSettings(_configuration, configurationUpdater),
+ },
+ onGenerateRoute: _getRoute,
+ );
+ }
+}
+
+void main() {
+ runApp(StocksApp());
+}
diff --git a/dev/benchmarks/test_apps/stocks/lib/stock_arrow.dart b/dev/benchmarks/test_apps/stocks/lib/stock_arrow.dart
new file mode 100644
index 0000000..e969edd
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/stock_arrow.dart
@@ -0,0 +1,87 @@
+// Copyright 2014 The Flutter 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:math' as math;
+
+import 'package:flutter/material.dart';
+
+class StockArrowPainter extends CustomPainter {
+ StockArrowPainter({ this.color, this.percentChange });
+
+ final Color color;
+ final double percentChange;
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ final Paint paint = Paint()..color = color;
+ paint.strokeWidth = 1.0;
+ const double padding = 2.0;
+ assert(padding > paint.strokeWidth / 2.0); // make sure the circle remains inside the box
+ final double r = (size.shortestSide - padding) / 2.0; // radius of the circle
+ final double centerX = padding + r;
+ final double centerY = padding + r;
+
+ // Draw the arrow.
+ const double w = 8.0;
+ double h = 5.0;
+ double arrowY;
+ if (percentChange < 0.0) {
+ h = -h;
+ arrowY = centerX + 1.0;
+ } else {
+ arrowY = centerX - 1.0;
+ }
+ final Path path = Path();
+ path.moveTo(centerX, arrowY - h); // top of the arrow
+ path.lineTo(centerX + w, arrowY + h);
+ path.lineTo(centerX - w, arrowY + h);
+ path.close();
+ paint.style = PaintingStyle.fill;
+ canvas.drawPath(path, paint);
+
+ // Draw a circle that circumscribes the arrow.
+ paint.style = PaintingStyle.stroke;
+ canvas.drawCircle(Offset(centerX, centerY), r, paint);
+ }
+
+ @override
+ bool shouldRepaint(StockArrowPainter oldDelegate) {
+ return oldDelegate.color != color
+ || oldDelegate.percentChange != percentChange;
+ }
+}
+
+class StockArrow extends StatelessWidget {
+ const StockArrow({ Key key, this.percentChange }) : super(key: key);
+
+ final double percentChange;
+
+ int _colorIndexForPercentChange(double percentChange) {
+ const double maxPercent = 10.0;
+ final double normalizedPercentChange = math.min(percentChange.abs(), maxPercent) / maxPercent;
+ return 100 + (normalizedPercentChange * 8.0).floor() * 100;
+ }
+
+ Color _colorForPercentChange(double percentChange) {
+ if (percentChange > 0)
+ return Colors.green[_colorIndexForPercentChange(percentChange)];
+ return Colors.red[_colorIndexForPercentChange(percentChange)];
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ width: 40.0,
+ height: 40.0,
+ margin: const EdgeInsets.symmetric(horizontal: 5.0),
+ child: CustomPaint(
+ painter: StockArrowPainter(
+ // TODO(jackson): This should change colors with the theme
+ color: _colorForPercentChange(percentChange),
+ percentChange: percentChange,
+ ),
+ ),
+ );
+ }
+}
diff --git a/dev/benchmarks/test_apps/stocks/lib/stock_data.dart b/dev/benchmarks/test_apps/stocks/lib/stock_data.dart
new file mode 100644
index 0000000..35a79ca
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/stock_data.dart
@@ -0,0 +1,101 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Snapshot from http://www.nasdaq.com/screening/company-list.aspx
+// Fetched 2/23/2014.
+// "Symbol","Name","LastSale","MarketCap","IPOyear","Sector","industry","Summary Quote",
+// Data in stock_data.json
+
+import 'dart:convert';
+import 'dart:math' as math;
+
+import 'package:flutter/foundation.dart';
+import 'package:http/http.dart' as http;
+
+final math.Random _rng = math.Random();
+
+class Stock {
+ Stock(this.symbol, this.name, this.lastSale, this.marketCap, this.percentChange);
+
+ Stock.fromFields(List<String> fields) {
+ // FIXME: This class should only have static data, not lastSale, etc.
+ // "Symbol","Name","LastSale","MarketCap","IPOyear","Sector","industry","Summary Quote",
+ lastSale = 0.0;
+ try {
+ lastSale = double.parse(fields[2]);
+ } catch (_) { }
+ symbol = fields[0];
+ name = fields[1];
+ marketCap = fields[4];
+ percentChange = (_rng.nextDouble() * 20) - 10;
+ }
+
+ String symbol;
+ String name;
+ double lastSale;
+ String marketCap;
+ double percentChange;
+}
+
+class StockData extends ChangeNotifier {
+ StockData() {
+ if (actuallyFetchData) {
+ _httpClient = http.Client();
+ _fetchNextChunk();
+ }
+ }
+
+ final List<String> _symbols = <String>[];
+ final Map<String, Stock> _stocks = <String, Stock>{};
+
+ List<String> get allSymbols => _symbols;
+
+ Stock operator [](String symbol) => _stocks[symbol];
+
+ bool get loading => _httpClient != null;
+
+ void add(List<dynamic> data) {
+ for (final List<dynamic> fields in data.cast<List<dynamic>>()) {
+ final Stock stock = Stock.fromFields(fields.cast<String>());
+ _symbols.add(stock.symbol);
+ _stocks[stock.symbol] = stock;
+ }
+ _symbols.sort();
+ notifyListeners();
+ }
+
+ static const int _chunkCount = 30;
+ int _nextChunk = 0;
+
+ String _urlToFetch(int chunk) {
+ return 'https://domokit.github.io/examples/stocks/data/stock_data_$chunk.json';
+ }
+
+ http.Client _httpClient;
+
+ static bool actuallyFetchData = true;
+
+ void _fetchNextChunk() {
+ _httpClient.get(_urlToFetch(_nextChunk++)).then<void>((http.Response response) {
+ final String json = response.body;
+ if (json == null) {
+ debugPrint('Failed to load stock data chunk ${_nextChunk - 1}');
+ _end();
+ return;
+ }
+ const JsonDecoder decoder = JsonDecoder();
+ add(decoder.convert(json) as List<dynamic>);
+ if (_nextChunk < _chunkCount) {
+ _fetchNextChunk();
+ } else {
+ _end();
+ }
+ });
+ }
+
+ void _end() {
+ _httpClient?.close();
+ _httpClient = null;
+ }
+}
diff --git a/dev/benchmarks/test_apps/stocks/lib/stock_home.dart b/dev/benchmarks/test_apps/stocks/lib/stock_home.dart
new file mode 100644
index 0000000..48046cd
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/stock_home.dart
@@ -0,0 +1,357 @@
+// Copyright 2014 The Flutter 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/material.dart';
+import 'package:flutter/rendering.dart' show debugDumpRenderTree, debugDumpLayerTree, debugDumpSemanticsTree, DebugSemanticsDumpOrder;
+import 'package:flutter/scheduler.dart' show timeDilation;
+import 'package:flutter/gestures.dart' show DragStartBehavior;
+
+import 'i18n/stock_strings.dart';
+import 'stock_data.dart';
+import 'stock_list.dart';
+import 'stock_symbol_viewer.dart';
+import 'stock_types.dart';
+
+typedef ModeUpdater = void Function(StockMode mode);
+
+enum _StockMenuItem { autorefresh, refresh, speedUp, speedDown }
+enum StockHomeTab { market, portfolio }
+
+class _NotImplementedDialog extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return AlertDialog(
+ title: const Text('Not Implemented'),
+ content: const Text('This feature has not yet been implemented.'),
+ actions: <Widget>[
+ FlatButton(
+ onPressed: debugDumpApp,
+ child: Row(
+ children: <Widget>[
+ const Icon(
+ Icons.dvr,
+ size: 18.0,
+ ),
+ Container(
+ width: 8.0,
+ ),
+ const Text('DUMP APP TO CONSOLE'),
+ ],
+ ),
+ ),
+ FlatButton(
+ onPressed: () {
+ Navigator.pop(context, false);
+ },
+ child: const Text('OH WELL'),
+ ),
+ ],
+ );
+ }
+}
+
+class StockHome extends StatefulWidget {
+ const StockHome(this.stocks, this.configuration, this.updater);
+
+ final StockData stocks;
+ final StockConfiguration configuration;
+ final ValueChanged<StockConfiguration> updater;
+
+ @override
+ StockHomeState createState() => StockHomeState();
+}
+
+class StockHomeState extends State<StockHome> {
+ final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
+ final TextEditingController _searchQuery = TextEditingController();
+ bool _isSearching = false;
+ bool _autorefresh = false;
+
+ void _handleSearchBegin() {
+ ModalRoute.of(context).addLocalHistoryEntry(LocalHistoryEntry(
+ onRemove: () {
+ setState(() {
+ _isSearching = false;
+ _searchQuery.clear();
+ });
+ },
+ ));
+ setState(() {
+ _isSearching = true;
+ });
+ }
+
+ void _handleStockModeChange(StockMode value) {
+ if (widget.updater != null)
+ widget.updater(widget.configuration.copyWith(stockMode: value));
+ }
+
+ void _handleStockMenu(BuildContext context, _StockMenuItem value) {
+ switch (value) {
+ case _StockMenuItem.autorefresh:
+ setState(() {
+ _autorefresh = !_autorefresh;
+ });
+ break;
+ case _StockMenuItem.refresh:
+ showDialog<void>(
+ context: context,
+ builder: (BuildContext context) => _NotImplementedDialog(),
+ );
+ break;
+ case _StockMenuItem.speedUp:
+ timeDilation /= 5.0;
+ break;
+ case _StockMenuItem.speedDown:
+ timeDilation *= 5.0;
+ break;
+ }
+ }
+
+ Widget _buildDrawer(BuildContext context) {
+ return Drawer(
+ child: ListView(
+ dragStartBehavior: DragStartBehavior.down,
+ children: <Widget>[
+ const DrawerHeader(child: Center(child: Text('Stocks'))),
+ const ListTile(
+ leading: Icon(Icons.assessment),
+ title: Text('Stock List'),
+ selected: true,
+ ),
+ const ListTile(
+ leading: Icon(Icons.account_balance),
+ title: Text('Account Balance'),
+ enabled: false,
+ ),
+ ListTile(
+ leading: const Icon(Icons.dvr),
+ title: const Text('Dump App to Console'),
+ onTap: () {
+ try {
+ debugDumpApp();
+ debugDumpRenderTree();
+ debugDumpLayerTree();
+ debugDumpSemanticsTree(DebugSemanticsDumpOrder.traversalOrder);
+ } catch (e, stack) {
+ debugPrint('Exception while dumping app:\n$e\n$stack');
+ }
+ },
+ ),
+ const Divider(),
+ ListTile(
+ leading: const Icon(Icons.thumb_up),
+ title: const Text('Optimistic'),
+ trailing: Radio<StockMode>(
+ value: StockMode.optimistic,
+ groupValue: widget.configuration.stockMode,
+ onChanged: _handleStockModeChange,
+ ),
+ onTap: () {
+ _handleStockModeChange(StockMode.optimistic);
+ },
+ ),
+ ListTile(
+ leading: const Icon(Icons.thumb_down),
+ title: const Text('Pessimistic'),
+ trailing: Radio<StockMode>(
+ value: StockMode.pessimistic,
+ groupValue: widget.configuration.stockMode,
+ onChanged: _handleStockModeChange,
+ ),
+ onTap: () {
+ _handleStockModeChange(StockMode.pessimistic);
+ },
+ ),
+ const Divider(),
+ ListTile(
+ leading: const Icon(Icons.settings),
+ title: const Text('Settings'),
+ onTap: _handleShowSettings,
+ ),
+ ListTile(
+ leading: const Icon(Icons.help),
+ title: const Text('About'),
+ onTap: _handleShowAbout,
+ ),
+ ],
+ ),
+ );
+ }
+
+ void _handleShowSettings() {
+ Navigator.popAndPushNamed(context, '/settings');
+ }
+
+ void _handleShowAbout() {
+ showAboutDialog(context: context);
+ }
+
+ AppBar buildAppBar() {
+ return AppBar(
+ elevation: 0.0,
+ title: Text(StockStrings.of(context).title),
+ actions: <Widget>[
+ IconButton(
+ icon: const Icon(Icons.search),
+ onPressed: _handleSearchBegin,
+ tooltip: 'Search',
+ ),
+ PopupMenuButton<_StockMenuItem>(
+ onSelected: (_StockMenuItem value) { _handleStockMenu(context, value); },
+ itemBuilder: (BuildContext context) => <PopupMenuItem<_StockMenuItem>>[
+ CheckedPopupMenuItem<_StockMenuItem>(
+ value: _StockMenuItem.autorefresh,
+ checked: _autorefresh,
+ child: const Text('Autorefresh'),
+ ),
+ const PopupMenuItem<_StockMenuItem>(
+ value: _StockMenuItem.refresh,
+ child: Text('Refresh'),
+ ),
+ const PopupMenuItem<_StockMenuItem>(
+ value: _StockMenuItem.speedUp,
+ child: Text('Increase animation speed'),
+ ),
+ const PopupMenuItem<_StockMenuItem>(
+ value: _StockMenuItem.speedDown,
+ child: Text('Decrease animation speed'),
+ ),
+ ],
+ ),
+ ],
+ bottom: TabBar(
+ tabs: <Widget>[
+ Tab(text: StockStrings.of(context).market),
+ Tab(text: StockStrings.of(context).portfolio),
+ ],
+ ),
+ );
+ }
+
+ static Iterable<Stock> _getStockList(StockData stocks, Iterable<String> symbols) {
+ return symbols.map<Stock>((String symbol) => stocks[symbol])
+ .where((Stock stock) => stock != null);
+ }
+
+ Iterable<Stock> _filterBySearchQuery(Iterable<Stock> stocks) {
+ if (_searchQuery.text.isEmpty)
+ return stocks;
+ final RegExp regexp = RegExp(_searchQuery.text, caseSensitive: false);
+ return stocks.where((Stock stock) => stock.symbol.contains(regexp));
+ }
+
+ void _buyStock(Stock stock) {
+ setState(() {
+ stock.percentChange = 100.0 * (1.0 / stock.lastSale);
+ stock.lastSale += 1.0;
+ });
+ _scaffoldKey.currentState.showSnackBar(SnackBar(
+ content: Text('Purchased ${stock.symbol} for ${stock.lastSale}'),
+ action: SnackBarAction(
+ label: 'BUY MORE',
+ onPressed: () {
+ _buyStock(stock);
+ },
+ ),
+ ));
+ }
+
+ Widget _buildStockList(BuildContext context, Iterable<Stock> stocks, StockHomeTab tab) {
+ return StockList(
+ stocks: stocks.toList(),
+ onAction: _buyStock,
+ onOpen: (Stock stock) {
+ Navigator.pushNamed(context, '/stock', arguments: stock.symbol);
+ },
+ onShow: (Stock stock) {
+ _scaffoldKey.currentState.showBottomSheet<void>((BuildContext context) => StockSymbolBottomSheet(stock: stock));
+ },
+ );
+ }
+
+ Widget _buildStockTab(BuildContext context, StockHomeTab tab, List<String> stockSymbols) {
+ return AnimatedBuilder(
+ key: ValueKey<StockHomeTab>(tab),
+ animation: Listenable.merge(<Listenable>[_searchQuery, widget.stocks]),
+ builder: (BuildContext context, Widget child) {
+ return _buildStockList(context, _filterBySearchQuery(_getStockList(widget.stocks, stockSymbols)).toList(), tab);
+ },
+ );
+ }
+
+ static const List<String> portfolioSymbols = <String>['AAPL','FIZZ', 'FIVE', 'FLAT', 'ZINC', 'ZNGA'];
+
+ AppBar buildSearchBar() {
+ return AppBar(
+ leading: BackButton(
+ color: Theme.of(context).accentColor,
+ ),
+ title: TextField(
+ controller: _searchQuery,
+ autofocus: true,
+ decoration: const InputDecoration(
+ hintText: 'Search stocks',
+ ),
+ ),
+ backgroundColor: Theme.of(context).canvasColor,
+ );
+ }
+
+ void _handleCreateCompany() {
+ showModalBottomSheet<void>(
+ context: context,
+ builder: (BuildContext context) => _CreateCompanySheet(),
+ );
+ }
+
+ Widget buildFloatingActionButton() {
+ return FloatingActionButton(
+ tooltip: 'Create company',
+ child: const Icon(Icons.add),
+ backgroundColor: Theme.of(context).accentColor,
+ onPressed: _handleCreateCompany,
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return DefaultTabController(
+ length: 2,
+ child: Scaffold(
+ drawerDragStartBehavior: DragStartBehavior.down,
+ key: _scaffoldKey,
+ appBar: _isSearching ? buildSearchBar() : buildAppBar(),
+ floatingActionButton: buildFloatingActionButton(),
+ drawer: _buildDrawer(context),
+ body: TabBarView(
+ dragStartBehavior: DragStartBehavior.down,
+ children: <Widget>[
+ _buildStockTab(context, StockHomeTab.market, widget.stocks.allSymbols),
+ _buildStockTab(context, StockHomeTab.portfolio, portfolioSymbols),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+class _CreateCompanySheet extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ children: const <Widget>[
+ TextField(
+ autofocus: true,
+ decoration: InputDecoration(
+ hintText: 'Company Name',
+ ),
+ ),
+ Text('(This demo is not yet complete.)'),
+ // For example, we could add a button that actually updates the list
+ // and then contacts the server, etc.
+ ],
+ );
+ }
+}
diff --git a/dev/benchmarks/test_apps/stocks/lib/stock_list.dart b/dev/benchmarks/test_apps/stocks/lib/stock_list.dart
new file mode 100644
index 0000000..17c87ea
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/stock_list.dart
@@ -0,0 +1,34 @@
+// Copyright 2014 The Flutter 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/material.dart';
+
+import 'stock_data.dart';
+import 'stock_row.dart';
+
+class StockList extends StatelessWidget {
+ const StockList({ Key key, this.stocks, this.onOpen, this.onShow, this.onAction }) : super(key: key);
+
+ final List<Stock> stocks;
+ final StockRowActionCallback onOpen;
+ final StockRowActionCallback onShow;
+ final StockRowActionCallback onAction;
+
+ @override
+ Widget build(BuildContext context) {
+ return ListView.builder(
+ key: const ValueKey<String>('stock-list'),
+ itemExtent: StockRow.kHeight,
+ itemCount: stocks.length,
+ itemBuilder: (BuildContext context, int index) {
+ return StockRow(
+ stock: stocks[index],
+ onPressed: onOpen,
+ onDoubleTap: onShow,
+ onLongPressed: onAction,
+ );
+ },
+ );
+ }
+}
diff --git a/dev/benchmarks/test_apps/stocks/lib/stock_row.dart b/dev/benchmarks/test_apps/stocks/lib/stock_row.dart
new file mode 100644
index 0000000..aeb1e09
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/stock_row.dart
@@ -0,0 +1,89 @@
+// Copyright 2014 The Flutter 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/material.dart';
+
+import 'stock_arrow.dart';
+import 'stock_data.dart';
+
+typedef StockRowActionCallback = void Function(Stock stock);
+
+class StockRow extends StatelessWidget {
+ StockRow({
+ this.stock,
+ this.onPressed,
+ this.onDoubleTap,
+ this.onLongPressed,
+ }) : super(key: ObjectKey(stock));
+
+ final Stock stock;
+ final StockRowActionCallback onPressed;
+ final StockRowActionCallback onDoubleTap;
+ final StockRowActionCallback onLongPressed;
+
+ static const double kHeight = 79.0;
+
+ GestureTapCallback _getHandler(StockRowActionCallback callback) {
+ return callback == null ? null : () => callback(stock);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final String lastSale = '\$${stock.lastSale.toStringAsFixed(2)}';
+ String changeInPrice = '${stock.percentChange.toStringAsFixed(2)}%';
+ if (stock.percentChange > 0)
+ changeInPrice = '+' + changeInPrice;
+ return InkWell(
+ key: ValueKey<String>(stock.symbol),
+ onTap: _getHandler(onPressed),
+ onDoubleTap: _getHandler(onDoubleTap),
+ onLongPress: _getHandler(onLongPressed),
+ child: Container(
+ padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 20.0),
+ decoration: BoxDecoration(
+ border: Border(
+ bottom: BorderSide(color: Theme.of(context).dividerColor)
+ )
+ ),
+ child: Row(
+ children: <Widget>[
+ Container(
+ margin: const EdgeInsets.only(right: 5.0),
+ child: Hero(
+ tag: stock,
+ child: StockArrow(percentChange: stock.percentChange),
+ ),
+ ),
+ Expanded(
+ child: Row(
+ children: <Widget>[
+ Expanded(
+ flex: 2,
+ child: Text(
+ stock.symbol
+ ),
+ ),
+ Expanded(
+ child: Text(
+ lastSale,
+ textAlign: TextAlign.right,
+ ),
+ ),
+ Expanded(
+ child: Text(
+ changeInPrice,
+ textAlign: TextAlign.right,
+ ),
+ ),
+ ],
+ crossAxisAlignment: CrossAxisAlignment.baseline,
+ textBaseline: DefaultTextStyle.of(context).style.textBaseline,
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/dev/benchmarks/test_apps/stocks/lib/stock_settings.dart b/dev/benchmarks/test_apps/stocks/lib/stock_settings.dart
new file mode 100644
index 0000000..aa95ad8
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/stock_settings.dart
@@ -0,0 +1,218 @@
+// Copyright 2014 The Flutter 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/material.dart';
+
+import 'stock_types.dart';
+
+class StockSettings extends StatefulWidget {
+ const StockSettings(this.configuration, this.updater);
+
+ final StockConfiguration configuration;
+ final ValueChanged<StockConfiguration> updater;
+
+ @override
+ StockSettingsState createState() => StockSettingsState();
+}
+
+class StockSettingsState extends State<StockSettings> {
+ void _handleOptimismChanged(bool value) {
+ value ??= false;
+ sendUpdates(widget.configuration.copyWith(stockMode: value ? StockMode.optimistic : StockMode.pessimistic));
+ }
+
+ void _handleBackupChanged(bool value) {
+ sendUpdates(widget.configuration.copyWith(backupMode: value ? BackupMode.enabled : BackupMode.disabled));
+ }
+
+ void _handleShowGridChanged(bool value) {
+ sendUpdates(widget.configuration.copyWith(debugShowGrid: value));
+ }
+
+ void _handleShowSizesChanged(bool value) {
+ sendUpdates(widget.configuration.copyWith(debugShowSizes: value));
+ }
+
+ void _handleShowBaselinesChanged(bool value) {
+ sendUpdates(widget.configuration.copyWith(debugShowBaselines: value));
+ }
+
+ void _handleShowLayersChanged(bool value) {
+ sendUpdates(widget.configuration.copyWith(debugShowLayers: value));
+ }
+
+ void _handleShowPointersChanged(bool value) {
+ sendUpdates(widget.configuration.copyWith(debugShowPointers: value));
+ }
+
+ void _handleShowRainbowChanged(bool value) {
+ sendUpdates(widget.configuration.copyWith(debugShowRainbow: value));
+ }
+
+
+ void _handleShowPerformanceOverlayChanged(bool value) {
+ sendUpdates(widget.configuration.copyWith(showPerformanceOverlay: value));
+ }
+
+ void _handleShowSemanticsDebuggerChanged(bool value) {
+ sendUpdates(widget.configuration.copyWith(showSemanticsDebugger: value));
+ }
+
+ void _confirmOptimismChange() {
+ switch (widget.configuration.stockMode) {
+ case StockMode.optimistic:
+ _handleOptimismChanged(false);
+ break;
+ case StockMode.pessimistic:
+ showDialog<bool>(
+ context: context,
+ builder: (BuildContext context) {
+ return AlertDialog(
+ title: const Text('Change mode?'),
+ content: const Text('Optimistic mode means everything is awesome. Are you sure you can handle that?'),
+ actions: <Widget>[
+ FlatButton(
+ child: const Text('NO THANKS'),
+ onPressed: () {
+ Navigator.pop(context, false);
+ },
+ ),
+ FlatButton(
+ child: const Text('AGREE'),
+ onPressed: () {
+ Navigator.pop(context, true);
+ },
+ ),
+ ],
+ );
+ },
+ ).then<void>(_handleOptimismChanged);
+ break;
+ }
+ }
+
+ void sendUpdates(StockConfiguration value) {
+ if (widget.updater != null)
+ widget.updater(value);
+ }
+
+ AppBar buildAppBar(BuildContext context) {
+ return AppBar(
+ title: const Text('Settings'),
+ );
+ }
+
+ Widget buildSettingsPane(BuildContext context) {
+ final List<Widget> rows = <Widget>[
+ ListTile(
+ leading: const Icon(Icons.thumb_up),
+ title: const Text('Everything is awesome'),
+ onTap: _confirmOptimismChange,
+ trailing: Checkbox(
+ value: widget.configuration.stockMode == StockMode.optimistic,
+ onChanged: (bool value) => _confirmOptimismChange(),
+ ),
+ ),
+ ListTile(
+ leading: const Icon(Icons.backup),
+ title: const Text('Back up stock list to the cloud'),
+ onTap: () { _handleBackupChanged(!(widget.configuration.backupMode == BackupMode.enabled)); },
+ trailing: Switch(
+ value: widget.configuration.backupMode == BackupMode.enabled,
+ onChanged: _handleBackupChanged,
+ ),
+ ),
+ ListTile(
+ leading: const Icon(Icons.picture_in_picture),
+ title: const Text('Show rendering performance overlay'),
+ onTap: () { _handleShowPerformanceOverlayChanged(!widget.configuration.showPerformanceOverlay); },
+ trailing: Switch(
+ value: widget.configuration.showPerformanceOverlay,
+ onChanged: _handleShowPerformanceOverlayChanged,
+ ),
+ ),
+ ListTile(
+ leading: const Icon(Icons.accessibility),
+ title: const Text('Show semantics overlay'),
+ onTap: () { _handleShowSemanticsDebuggerChanged(!widget.configuration.showSemanticsDebugger); },
+ trailing: Switch(
+ value: widget.configuration.showSemanticsDebugger,
+ onChanged: _handleShowSemanticsDebuggerChanged,
+ ),
+ ),
+ ];
+ assert(() {
+ // material grid and size construction lines are only available in checked mode
+ rows.addAll(<Widget>[
+ ListTile(
+ leading: const Icon(Icons.border_clear),
+ title: const Text('Show material grid (for debugging)'),
+ onTap: () { _handleShowGridChanged(!widget.configuration.debugShowGrid); },
+ trailing: Switch(
+ value: widget.configuration.debugShowGrid,
+ onChanged: _handleShowGridChanged,
+ ),
+ ),
+ ListTile(
+ leading: const Icon(Icons.border_all),
+ title: const Text('Show construction lines (for debugging)'),
+ onTap: () { _handleShowSizesChanged(!widget.configuration.debugShowSizes); },
+ trailing: Switch(
+ value: widget.configuration.debugShowSizes,
+ onChanged: _handleShowSizesChanged,
+ ),
+ ),
+ ListTile(
+ leading: const Icon(Icons.format_color_text),
+ title: const Text('Show baselines (for debugging)'),
+ onTap: () { _handleShowBaselinesChanged(!widget.configuration.debugShowBaselines); },
+ trailing: Switch(
+ value: widget.configuration.debugShowBaselines,
+ onChanged: _handleShowBaselinesChanged,
+ ),
+ ),
+ ListTile(
+ leading: const Icon(Icons.filter_none),
+ title: const Text('Show layer boundaries (for debugging)'),
+ onTap: () { _handleShowLayersChanged(!widget.configuration.debugShowLayers); },
+ trailing: Switch(
+ value: widget.configuration.debugShowLayers,
+ onChanged: _handleShowLayersChanged,
+ ),
+ ),
+ ListTile(
+ leading: const Icon(Icons.mouse),
+ title: const Text('Show pointer hit-testing (for debugging)'),
+ onTap: () { _handleShowPointersChanged(!widget.configuration.debugShowPointers); },
+ trailing: Switch(
+ value: widget.configuration.debugShowPointers,
+ onChanged: _handleShowPointersChanged,
+ ),
+ ),
+ ListTile(
+ leading: const Icon(Icons.gradient),
+ title: const Text('Show repaint rainbow (for debugging)'),
+ onTap: () { _handleShowRainbowChanged(!widget.configuration.debugShowRainbow); },
+ trailing: Switch(
+ value: widget.configuration.debugShowRainbow,
+ onChanged: _handleShowRainbowChanged,
+ ),
+ ),
+ ]);
+ return true;
+ }());
+ return ListView(
+ padding: const EdgeInsets.symmetric(vertical: 20.0),
+ children: rows,
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: buildAppBar(context),
+ body: buildSettingsPane(context),
+ );
+ }
+}
diff --git a/dev/benchmarks/test_apps/stocks/lib/stock_symbol_viewer.dart b/dev/benchmarks/test_apps/stocks/lib/stock_symbol_viewer.dart
new file mode 100644
index 0000000..e375ace
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/stock_symbol_viewer.dart
@@ -0,0 +1,133 @@
+// Copyright 2014 The Flutter 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/material.dart';
+
+import 'stock_arrow.dart';
+import 'stock_data.dart';
+
+class _StockSymbolView extends StatelessWidget {
+ const _StockSymbolView({ this.stock, this.arrow });
+
+ final Stock stock;
+ final Widget arrow;
+
+ @override
+ Widget build(BuildContext context) {
+ assert(stock != null);
+ final String lastSale = '\$${stock.lastSale.toStringAsFixed(2)}';
+ String changeInPrice = '${stock.percentChange.toStringAsFixed(2)}%';
+ if (stock.percentChange > 0)
+ changeInPrice = '+' + changeInPrice;
+
+ final TextStyle headings = Theme.of(context).textTheme.bodyText1;
+ return Container(
+ padding: const EdgeInsets.all(20.0),
+ child: Column(
+ children: <Widget>[
+ Row(
+ children: <Widget>[
+ Text(
+ '${stock.symbol}',
+ key: ValueKey<String>('${stock.symbol}_symbol_name'),
+ style: Theme.of(context).textTheme.headline3,
+ ),
+ arrow,
+ ],
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ ),
+ Text('Last Sale', style: headings),
+ Text('$lastSale ($changeInPrice)'),
+ Container(
+ height: 8.0
+ ),
+ Text('Market Cap', style: headings),
+ Text('${stock.marketCap}'),
+ Container(
+ height: 8.0
+ ),
+ RichText(
+ text: TextSpan(
+ style: DefaultTextStyle.of(context).style.merge(const TextStyle(fontSize: 8.0)),
+ text: 'Prices may be delayed by ',
+ children: const <TextSpan>[
+ TextSpan(text: 'several', style: TextStyle(fontStyle: FontStyle.italic)),
+ TextSpan(text: ' years.'),
+ ],
+ ),
+ ),
+ ],
+ mainAxisSize: MainAxisSize.min,
+ ),
+ );
+ }
+}
+
+class StockSymbolPage extends StatelessWidget {
+ const StockSymbolPage({ this.symbol, this.stocks });
+
+ final String symbol;
+ final StockData stocks;
+
+ @override
+ Widget build(BuildContext context) {
+ return AnimatedBuilder(
+ animation: stocks,
+ builder: (BuildContext context, Widget child) {
+ final Stock stock = stocks[symbol];
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(stock?.name ?? symbol),
+ ),
+ body: SingleChildScrollView(
+ child: Container(
+ margin: const EdgeInsets.all(20.0),
+ child: Card(
+ child: AnimatedCrossFade(
+ duration: const Duration(milliseconds: 300),
+ firstChild: const Padding(
+ padding: EdgeInsets.all(20.0),
+ child: Center(child: CircularProgressIndicator()),
+ ),
+ secondChild: stock != null
+ ? _StockSymbolView(
+ stock: stock,
+ arrow: Hero(
+ tag: stock,
+ child: StockArrow(percentChange: stock.percentChange),
+ ),
+ ) : Padding(
+ padding: const EdgeInsets.all(20.0),
+ child: Center(child: Text('$symbol not found')),
+ ),
+ crossFadeState: stock == null && stocks.loading ? CrossFadeState.showFirst : CrossFadeState.showSecond,
+ ),
+ ),
+ ),
+ ),
+ );
+ },
+ );
+ }
+}
+
+class StockSymbolBottomSheet extends StatelessWidget {
+ const StockSymbolBottomSheet({ this.stock });
+
+ final Stock stock;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ padding: const EdgeInsets.all(10.0),
+ decoration: const BoxDecoration(
+ border: Border(top: BorderSide(color: Colors.black26))
+ ),
+ child: _StockSymbolView(
+ stock: stock,
+ arrow: StockArrow(percentChange: stock.percentChange),
+ ),
+ );
+ }
+}
diff --git a/dev/benchmarks/test_apps/stocks/lib/stock_types.dart b/dev/benchmarks/test_apps/stocks/lib/stock_types.dart
new file mode 100644
index 0000000..132bc46
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/lib/stock_types.dart
@@ -0,0 +1,69 @@
+// Copyright 2014 The Flutter 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/foundation.dart';
+
+enum StockMode { optimistic, pessimistic }
+enum BackupMode { enabled, disabled }
+
+class StockConfiguration {
+ StockConfiguration({
+ @required this.stockMode,
+ @required this.backupMode,
+ @required this.debugShowGrid,
+ @required this.debugShowSizes,
+ @required this.debugShowBaselines,
+ @required this.debugShowLayers,
+ @required this.debugShowPointers,
+ @required this.debugShowRainbow,
+ @required this.showPerformanceOverlay,
+ @required this.showSemanticsDebugger,
+ }) : assert(stockMode != null),
+ assert(backupMode != null),
+ assert(debugShowGrid != null),
+ assert(debugShowSizes != null),
+ assert(debugShowBaselines != null),
+ assert(debugShowLayers != null),
+ assert(debugShowPointers != null),
+ assert(debugShowRainbow != null),
+ assert(showPerformanceOverlay != null),
+ assert(showSemanticsDebugger != null);
+
+ final StockMode stockMode;
+ final BackupMode backupMode;
+ final bool debugShowGrid;
+ final bool debugShowSizes;
+ final bool debugShowBaselines;
+ final bool debugShowLayers;
+ final bool debugShowPointers;
+ final bool debugShowRainbow;
+ final bool showPerformanceOverlay;
+ final bool showSemanticsDebugger;
+
+ StockConfiguration copyWith({
+ StockMode stockMode,
+ BackupMode backupMode,
+ bool debugShowGrid,
+ bool debugShowSizes,
+ bool debugShowBaselines,
+ bool debugShowLayers,
+ bool debugShowPointers,
+ bool debugShowRainbow,
+ bool showPerformanceOverlay,
+ bool showSemanticsDebugger,
+ }) {
+ return StockConfiguration(
+ stockMode: stockMode ?? this.stockMode,
+ backupMode: backupMode ?? this.backupMode,
+ debugShowGrid: debugShowGrid ?? this.debugShowGrid,
+ debugShowSizes: debugShowSizes ?? this.debugShowSizes,
+ debugShowBaselines: debugShowBaselines ?? this.debugShowBaselines,
+ debugShowLayers: debugShowLayers ?? this.debugShowLayers,
+ debugShowPointers: debugShowPointers ?? this.debugShowPointers,
+ debugShowRainbow: debugShowRainbow ?? this.debugShowRainbow,
+ showPerformanceOverlay: showPerformanceOverlay ?? this.showPerformanceOverlay,
+ showSemanticsDebugger: showSemanticsDebugger ?? this.showSemanticsDebugger,
+ );
+ }
+}
diff --git a/dev/benchmarks/test_apps/stocks/pubspec.yaml b/dev/benchmarks/test_apps/stocks/pubspec.yaml
new file mode 100644
index 0000000..ec51a59
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/pubspec.yaml
@@ -0,0 +1,90 @@
+name: stocks
+
+environment:
+ # The pub client defaults to an <2.0.0 sdk constraint which we need to explicitly overwrite.
+ sdk: ">=2.0.0-dev.68.0 <3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ flutter_localizations:
+ sdk: flutter
+ intl: 0.16.1
+ intl_translation: 0.17.9
+ http: 0.12.0+4
+ isolate: 2.0.2
+
+ _fe_analyzer_shared: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ analyzer: 0.39.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ async: 2.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ dart_style: 1.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ glob: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ html: 0.14.0+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ http_parser: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ js: 0.6.1+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ meta: 1.1.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ node_interop: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ node_io: 1.0.1+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ package_config: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ path: 1.6.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ pedantic: 1.8.0+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ petitparser: 2.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ pub_semver: 1.4.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ source_span: 1.5.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ string_scanner: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ term_glyph: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ typed_data: 1.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ vector_math: 2.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ watcher: 0.9.7+13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ yaml: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ flutter_driver:
+ sdk: flutter
+ test: 1.9.4
+
+ archive: 2.0.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ coverage: 0.13.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ http_multi_server: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ image: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ io: 0.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ json_rpc_2: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ logging: 0.11.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ matcher: 0.12.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ mime: 0.9.6+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ multi_server_socket: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ node_preamble: 1.4.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ package_resolver: 1.0.10 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ pool: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ quiver: 2.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ shelf: 0.7.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ shelf_packages_handler: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ shelf_static: 0.2.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ shelf_web_socket: 0.2.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ source_map_stack_trace: 1.1.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ source_maps: 0.10.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ stack_trace: 1.9.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ stream_channel: 2.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ sync_http: 0.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ test_api: 0.2.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ test_core: 0.2.15 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ vm_service: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ vm_service_client: 0.2.6+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ web_socket_channel: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ webdriver: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+ xml: 3.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+
+flutter:
+ uses-material-design: true
+
+# PUBSPEC CHECKSUM: 9b6f
diff --git a/dev/benchmarks/test_apps/stocks/test/icon_color_test.dart b/dev/benchmarks/test_apps/stocks/test/icon_color_test.dart
new file mode 100644
index 0000000..9e965f2
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/test/icon_color_test.dart
@@ -0,0 +1,91 @@
+// Copyright 2014 The Flutter 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:ui' as ui show window;
+
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:stocks/main.dart' as stocks;
+import 'package:stocks/stock_data.dart' as stock_data;
+
+Element findElementOfExactWidgetTypeGoingDown(Element node, Type targetType) {
+ void walker(Element child) {
+ if (child.widget.runtimeType == targetType)
+ throw child;
+ child.visitChildElements(walker);
+ }
+ try {
+ walker(node);
+ } on Element catch (result) {
+ return result;
+ }
+ return null;
+}
+
+Element findElementOfExactWidgetTypeGoingUp(Element node, Type targetType) {
+ Element result;
+ bool walker(Element ancestor) {
+ if (ancestor.widget.runtimeType == targetType)
+ result = ancestor;
+ return result == null;
+ }
+ node.visitAncestorElements(walker);
+ return result;
+}
+
+final RegExp materialIconAssetNameColorExtractor = RegExp(r'[^/]+/ic_.+_(white|black)_[0-9]+dp\.png');
+
+void checkIconColor(WidgetTester tester, String label, Color color) {
+ final Element listTile = findElementOfExactWidgetTypeGoingUp(tester.element(find.text(label)), ListTile);
+ expect(listTile, isNotNull);
+ final Element asset = findElementOfExactWidgetTypeGoingDown(listTile, RichText);
+ final RichText richText = asset.widget as RichText;
+ expect(richText.text.style.color, equals(color));
+}
+
+void main() {
+ stock_data.StockData.actuallyFetchData = false;
+
+ testWidgets('Icon colors', (WidgetTester tester) async {
+ stocks.main(); // builds the app and schedules a frame but doesn't trigger one
+ await tester.pump(); // see https://github.com/flutter/flutter/issues/1865
+ await tester.pump(); // triggers a frame
+
+ // sanity check
+ expect(find.text('MARKET'), findsOneWidget);
+ expect(find.text('Account Balance'), findsNothing);
+ await tester.pump(const Duration(seconds: 2));
+ expect(find.text('MARKET'), findsOneWidget);
+ expect(find.text('Account Balance'), findsNothing);
+
+ // drag the drawer out
+ final Offset left = Offset(0.0, (ui.window.physicalSize / ui.window.devicePixelRatio).height / 2.0);
+ final Offset right = Offset((ui.window.physicalSize / ui.window.devicePixelRatio).width, left.dy);
+ final TestGesture gesture = await tester.startGesture(left);
+ await tester.pump();
+ await gesture.moveTo(right);
+ await tester.pump();
+ await gesture.up();
+ await tester.pump();
+ expect(find.text('MARKET'), findsOneWidget);
+ expect(find.text('Account Balance'), findsOneWidget);
+
+ // check the color of the icon - light mode
+ checkIconColor(tester, 'Stock List', Colors.purple); // theme primary color
+ checkIconColor(tester, 'Account Balance', Colors.black38); // disabled
+ checkIconColor(tester, 'About', Colors.black45); // enabled
+
+ // switch to dark mode
+ await tester.tap(find.text('Pessimistic'));
+ await tester.pump(); // get the tap and send the notification that the theme has changed
+ await tester.pump(); // start the theme transition
+ await tester.pump(const Duration(seconds: 5)); // end the transition
+
+ // check the color of the icon - dark mode
+ checkIconColor(tester, 'Stock List', Colors.redAccent); // theme accent color
+ checkIconColor(tester, 'Account Balance', Colors.white38); // disabled
+ checkIconColor(tester, 'About', Colors.white); // enabled
+ });
+}
diff --git a/dev/benchmarks/test_apps/stocks/test/locale_test.dart b/dev/benchmarks/test_apps/stocks/test/locale_test.dart
new file mode 100644
index 0000000..a17e925
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/test/locale_test.dart
@@ -0,0 +1,32 @@
+// Copyright 2014 The Flutter 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_test/flutter_test.dart';
+import 'package:stocks/main.dart' as stocks;
+import 'package:stocks/stock_data.dart' as stock_data;
+
+void main() {
+ stock_data.StockData.actuallyFetchData = false;
+
+ testWidgets('Changing locale', (WidgetTester tester) async {
+ stocks.main();
+ await tester.idle(); // see https://github.com/flutter/flutter/issues/1865
+ await tester.pump();
+ // The initial test app's locale is "_", so we're seeing the fallback translation here.
+ expect(find.text('MARKET'), findsOneWidget);
+ await tester.binding.setLocale('es', 'US');
+ await tester.idle();
+
+ // The Localizations widget has been built with the new locale. The
+ // new locale's strings are loaded asynchronously, so we're still
+ // displaying the previous locale's strings.
+ await tester.pump();
+ expect(find.text('MARKET'), findsOneWidget);
+
+ // The localized strings have finished loading and dependent
+ // widgets have been updated.
+ await tester.pump();
+ expect(find.text('MERCADO'), findsOneWidget);
+ });
+}
diff --git a/dev/benchmarks/test_apps/stocks/test/search_test.dart b/dev/benchmarks/test_apps/stocks/test/search_test.dart
new file mode 100644
index 0000000..f9c2388
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/test/search_test.dart
@@ -0,0 +1,53 @@
+// Copyright 2014 The Flutter 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/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:stocks/main.dart' as stocks;
+import 'package:stocks/stock_data.dart' as stock_data;
+
+void main() {
+ stock_data.StockData.actuallyFetchData = false;
+
+ testWidgets('Search', (WidgetTester tester) async {
+ stocks.main(); // builds the app and schedules a frame but doesn't trigger one
+ await tester.pump(); // see https://github.com/flutter/flutter/issues/1865
+ await tester.pump(); // triggers a frame
+
+ expect(find.text('AAPL'), findsNothing);
+ expect(find.text('BANA'), findsNothing);
+
+ final stocks.StocksAppState app = tester.state<stocks.StocksAppState>(find.byType(stocks.StocksApp));
+ app.stocks.add(<List<String>>[
+ // "Symbol","Name","LastSale","MarketCap","IPOyear","Sector","industry","Summary Quote"
+ <String>['AAPL', 'Apple', '', '', '', '', '', ''],
+ <String>['BANA', 'Banana', '', '', '', '', '', ''],
+ ]);
+ await tester.pump();
+
+ expect(find.text('AAPL'), findsOneWidget);
+ expect(find.text('BANA'), findsOneWidget);
+
+ await tester.tap(find.byTooltip('Search'));
+ // We skip a minute at a time so that each phase of the animation
+ // is done in two frames, the start frame and the end frame.
+ // There are two phases currently, so that results in three frames.
+ expect(await tester.pumpAndSettle(const Duration(minutes: 1)), 3);
+
+ expect(find.text('AAPL'), findsOneWidget);
+ expect(find.text('BANA'), findsOneWidget);
+
+ await tester.enterText(find.byType(EditableText), 'B');
+ await tester.pump();
+
+ expect(find.text('AAPL'), findsNothing);
+ expect(find.text('BANA'), findsOneWidget);
+
+ await tester.enterText(find.byType(EditableText), 'X');
+ await tester.pump();
+
+ expect(find.text('AAPL'), findsNothing);
+ expect(find.text('BANA'), findsNothing);
+ });
+}
diff --git a/dev/benchmarks/test_apps/stocks/test_driver/scroll_perf.dart b/dev/benchmarks/test_apps/stocks/test_driver/scroll_perf.dart
new file mode 100644
index 0000000..332e06f
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/test_driver/scroll_perf.dart
@@ -0,0 +1,11 @@
+// Copyright 2014 The Flutter 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/driver_extension.dart';
+import 'package:stocks/main.dart' as app;
+
+void main() {
+ enableFlutterDriverExtension();
+ app.main();
+}
diff --git a/dev/benchmarks/test_apps/stocks/test_driver/scroll_perf_test.dart b/dev/benchmarks/test_apps/stocks/test_driver/scroll_perf_test.dart
new file mode 100644
index 0000000..d9ec350
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/test_driver/scroll_perf_test.dart
@@ -0,0 +1,47 @@
+// Copyright 2014 The Flutter 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_driver/flutter_driver.dart';
+import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
+
+void main() {
+ group('scrolling performance test', () {
+ FlutterDriver driver;
+
+ setUpAll(() async {
+ driver = await FlutterDriver.connect();
+ });
+
+ tearDownAll(() async {
+ if (driver != null)
+ driver.close();
+ });
+
+ test('measure', () async {
+ final Timeline timeline = await driver.traceAction(() async {
+ // Find the scrollable stock list
+ final SerializableFinder stockList = find.byValueKey('stock-list');
+ expect(stockList, isNotNull);
+
+ // Scroll down
+ for (int i = 0; i < 5; i++) {
+ await driver.scroll(stockList, 0.0, -300.0, const Duration(milliseconds: 300));
+ await Future<void>.delayed(const Duration(milliseconds: 500));
+ }
+
+ // Scroll up
+ for (int i = 0; i < 5; i++) {
+ await driver.scroll(stockList, 0.0, 300.0, const Duration(milliseconds: 300));
+ await Future<void>.delayed(const Duration(milliseconds: 500));
+ }
+ });
+
+ final TimelineSummary summary = TimelineSummary.summarize(timeline);
+ summary.writeSummaryToFile('stocks_scroll_perf', pretty: true);
+ summary.writeTimelineToFile('stocks_scroll_perf', pretty: true);
+ });
+ });
+}
diff --git a/dev/benchmarks/test_apps/stocks/test_driver/stock_view.dart b/dev/benchmarks/test_apps/stocks/test_driver/stock_view.dart
new file mode 100644
index 0000000..332e06f
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/test_driver/stock_view.dart
@@ -0,0 +1,11 @@
+// Copyright 2014 The Flutter 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/driver_extension.dart';
+import 'package:stocks/main.dart' as app;
+
+void main() {
+ enableFlutterDriverExtension();
+ app.main();
+}
diff --git a/dev/benchmarks/test_apps/stocks/test_driver/stock_view_test.dart b/dev/benchmarks/test_apps/stocks/test_driver/stock_view_test.dart
new file mode 100644
index 0000000..28d8b9d
--- /dev/null
+++ b/dev/benchmarks/test_apps/stocks/test_driver/stock_view_test.dart
@@ -0,0 +1,39 @@
+// Copyright 2014 The Flutter 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_driver/flutter_driver.dart';
+import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
+
+void main() {
+ group('basic stock view test', () {
+ FlutterDriver driver;
+
+ setUpAll(() async {
+ driver = await FlutterDriver.connect();
+ });
+
+ tearDownAll(() async {
+ if (driver != null)
+ driver.close();
+ });
+
+ test('open AAPL stock', () async {
+ final SerializableFinder stockList = find.byValueKey('stock-list');
+ expect(stockList, isNotNull);
+
+ final SerializableFinder aaplStockRow = find.byValueKey('AAPL');
+ await driver.scrollUntilVisible(stockList, aaplStockRow);
+
+ await driver.tap(aaplStockRow);
+ await Future<void>.delayed(const Duration(milliseconds: 500));
+
+ final SerializableFinder stockOption = find.byValueKey('AAPL_symbol_name');
+ final String symbol = await driver.getText(stockOption, timeout: const Duration(milliseconds: 500));
+
+ expect(symbol, 'AAPL');
+ });
+ });
+}
diff --git a/dev/bots/analyze.dart b/dev/bots/analyze.dart
index 6c0f607..325666e 100644
--- a/dev/bots/analyze.dart
+++ b/dev/bots/analyze.dart
@@ -908,57 +908,57 @@
// STOCKS ICONS
- // examples/stocks/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
+ // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Hash256(0x74052AB5241D4418, 0x7085180608BC3114, 0xD12493C50CD8BBC7, 0x56DED186C37ACE84),
- // examples/stocks/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
+ // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Hash256(0xE37947332E3491CB, 0x82920EE86A086FEA, 0xE1E0A70B3700A7DA, 0xDCAFBDD8F40E2E19),
- // examples/stocks/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
+ // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Hash256(0xE608CDFC0C8579FB, 0xE38873BAAF7BC944, 0x9C9D2EE3685A4FAE, 0x671EF0C8BC41D17C),
- // examples/stocks/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
+ // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Hash256(0xBD53D86977DF9C54, 0xF605743C5ABA114C, 0x9D51D1A8BB917E1A, 0x14CAA26C335CAEBD),
- // examples/stocks/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
+ // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Hash256(0x64E4D02262C4F3D0, 0xBB4FDC21CD0A816C, 0x4CD2A0194E00FB0F, 0x1C3AE4142FAC0D15),
- // examples/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
- // examples/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png
+ // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
+ // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png
Hash256(0x5BA3283A76918FC0, 0xEE127D0F22D7A0B6, 0xDF03DAED61669427, 0x93D89DDD87A08117),
- // examples/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
+ // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
Hash256(0xCD7F26ED31DEA42A, 0x535D155EC6261499, 0x34E6738255FDB2C4, 0xBD8D4BDDE9A99B05),
- // examples/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76.png
+ // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76.png
Hash256(0x3FA1225FC9A96A7E, 0xCD071BC42881AB0E, 0x7747EB72FFB72459, 0xA37971BBAD27EE24),
- // examples/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
+ // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
Hash256(0xCD867001ACD7BBDB, 0x25CDFD452AE89FA2, 0x8C2DC980CAF55F48, 0x0B16C246CFB389BC),
- // examples/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
+ // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
Hash256(0x848E9736E5C4915A, 0x7945BCF6B32FD56B, 0x1F1E7CDDD914352E, 0xC9681D38EF2A70DA),
- // examples/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification.png
+ // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification.png
Hash256(0x654BA7D6C4E05CA0, 0x7799878884EF8F11, 0xA383E1F24CEF5568, 0x3C47604A966983C8),
- // examples/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification@2x.png
- // examples/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png
+ // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification@2x.png
+ // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png
Hash256(0x743056FE7D83FE42, 0xA2990825B6AD0415, 0x1AF73D0D43B227AA, 0x07EBEA9B767381D9),
- // examples/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification@3x.png
+ // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification@3x.png
Hash256(0xA7E1570812D119CF, 0xEF4B602EF28DD0A4, 0x100D066E66F5B9B9, 0x881765DC9303343B),
- // examples/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png
+ // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png
Hash256(0xB4102839A1E41671, 0x62DACBDEFA471953, 0xB1EE89A0AB7594BE, 0x1D9AC1E67DC2B2CE),
- // examples/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
+ // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
Hash256(0x70AC6571B593A967, 0xF1CBAEC9BC02D02D, 0x93AD766D8290ADE6, 0x840139BF9F219019),
- // examples/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
+ // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
Hash256(0x5D87A78386DA2C43, 0xDDA8FEF2CA51438C, 0xE5A276FE28C6CF0A, 0xEBE89085B56665B6),
- // examples/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
+ // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
Hash256(0x4D9F5E81F668DA44, 0xB20A77F8BF7BA2E1, 0xF384533B5AD58F07, 0xB3A2F93F8635CD96),
diff --git a/dev/bots/test.dart b/dev/bots/test.dart
index ab4087e..b3b3b8b 100644
--- a/dev/bots/test.dart
+++ b/dev/bots/test.dart
@@ -453,7 +453,7 @@
await _runFlutterTest(path.join(flutterRoot, 'examples', 'catalog'), tableData: bigqueryApi?.tabledata);
await _runFlutterTest(path.join(flutterRoot, 'examples', 'hello_world'), tableData: bigqueryApi?.tabledata);
await _runFlutterTest(path.join(flutterRoot, 'examples', 'layers'), tableData: bigqueryApi?.tabledata);
- await _runFlutterTest(path.join(flutterRoot, 'examples', 'stocks'), tableData: bigqueryApi?.tabledata);
+ await _runFlutterTest(path.join(flutterRoot, 'dev', 'benchmarks', 'test_apps', 'stocks'), tableData: bigqueryApi?.tabledata);
await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_driver'), tableData: bigqueryApi?.tabledata, tests: <String>[path.join('test', 'src', 'real_tests')]);
await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_goldens'), tableData: bigqueryApi?.tabledata);
await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_localizations'), tableData: bigqueryApi?.tabledata);