diff --git a/dev/benchmarks/complex_layout/.gitignore b/dev/benchmarks/complex_layout/.gitignore
new file mode 100644
index 00000000000..14c7d4c3f73
--- /dev/null
+++ b/dev/benchmarks/complex_layout/.gitignore
@@ -0,0 +1,9 @@
+.DS_Store
+.atom/
+.idea
+.packages
+.pub/
+build/
+ios/.generated/
+packages
+pubspec.lock
diff --git a/dev/benchmarks/complex_layout/android/AndroidManifest.xml b/dev/benchmarks/complex_layout/android/AndroidManifest.xml
new file mode 100644
index 00000000000..794a13f5d7e
--- /dev/null
+++ b/dev/benchmarks/complex_layout/android/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/benchmarks/complex_layout/android/res/mipmap-hdpi/ic_launcher.png b/dev/benchmarks/complex_layout/android/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000000..f0a55cda78c
Binary files /dev/null and b/dev/benchmarks/complex_layout/android/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/dev/benchmarks/complex_layout/android/res/mipmap-mdpi/ic_launcher.png b/dev/benchmarks/complex_layout/android/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000000..eb98dc451df
Binary files /dev/null and b/dev/benchmarks/complex_layout/android/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/dev/benchmarks/complex_layout/android/res/mipmap-xhdpi/ic_launcher.png b/dev/benchmarks/complex_layout/android/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000000..f1783dbbf3c
Binary files /dev/null and b/dev/benchmarks/complex_layout/android/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/dev/benchmarks/complex_layout/android/res/mipmap-xxhdpi/ic_launcher.png b/dev/benchmarks/complex_layout/android/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000000..46828a290a7
Binary files /dev/null and b/dev/benchmarks/complex_layout/android/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/dev/benchmarks/complex_layout/android/res/mipmap-xxxhdpi/ic_launcher.png b/dev/benchmarks/complex_layout/android/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000000..a5276715d92
Binary files /dev/null and b/dev/benchmarks/complex_layout/android/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/dev/benchmarks/complex_layout/flutter.yaml b/dev/benchmarks/complex_layout/flutter.yaml
new file mode 100644
index 00000000000..16e08e13853
--- /dev/null
+++ b/dev/benchmarks/complex_layout/flutter.yaml
@@ -0,0 +1,6 @@
+name: complex_layout
+uses-material-design: true
+assets:
+ - packages/flutter_gallery_assets/ali_connors.png
+ - packages/flutter_gallery_assets/ali_connors_sml.png
+ - packages/flutter_gallery_assets/top_10_australian_beaches.png
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Contents.json b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000000..363d1c4ce4f
--- /dev/null
+++ b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,142 @@
+{
+ "images" : [
+ {
+ "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" : "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"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "icon_16x16.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "icon_16x16@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "icon_32x32.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "icon_32x32@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "icon_128x128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "icon_128x128@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "icon_256x256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "icon_256x256@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "icon_512x512.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "icon_512x512@2x.png",
+ "scale" : "2x"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
new file mode 100644
index 00000000000..9996f5e598a
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
new file mode 100644
index 00000000000..7a543edbdd4
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-76.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-76.png
new file mode 100644
index 00000000000..05a82681049
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-76.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
new file mode 100644
index 00000000000..bfbca28f5d5
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
new file mode 100644
index 00000000000..c924b8e497d
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png
new file mode 100644
index 00000000000..3006f1d348c
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png
new file mode 100644
index 00000000000..5f7d22d364d
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png
new file mode 100644
index 00000000000..9996f5e598a
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
new file mode 100644
index 00000000000..3433da11966
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
new file mode 100644
index 00000000000..f7f9f16d1b4
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
new file mode 100644
index 00000000000..e9c23601cf4
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128.png
new file mode 100644
index 00000000000..dd5fccb0a99
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png
new file mode 100644
index 00000000000..1909e275118
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16.png
new file mode 100644
index 00000000000..40a701b5146
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png
new file mode 100644
index 00000000000..06c1a805609
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256.png
new file mode 100644
index 00000000000..1909e275118
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png
new file mode 100644
index 00000000000..69c96ef1e27
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32.png
new file mode 100644
index 00000000000..06c1a805609
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png
new file mode 100644
index 00000000000..0f943714603
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512.png
new file mode 100644
index 00000000000..69c96ef1e27
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png
new file mode 100644
index 00000000000..1d6355f58c8
Binary files /dev/null and b/dev/benchmarks/complex_layout/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png differ
diff --git a/dev/benchmarks/complex_layout/ios/Info.plist b/dev/benchmarks/complex_layout/ios/Info.plist
new file mode 100644
index 00000000000..9c5e317f536
--- /dev/null
+++ b/dev/benchmarks/complex_layout/ios/Info.plist
@@ -0,0 +1,47 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ Runner
+ CFBundleIdentifier
+ com.yourcompany.complexLayout
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ complex_layout
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/dev/benchmarks/complex_layout/ios/LaunchScreen.storyboard b/dev/benchmarks/complex_layout/ios/LaunchScreen.storyboard
new file mode 100644
index 00000000000..78686cd0750
--- /dev/null
+++ b/dev/benchmarks/complex_layout/ios/LaunchScreen.storyboard
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/benchmarks/complex_layout/lib/main.dart b/dev/benchmarks/complex_layout/lib/main.dart
new file mode 100644
index 00000000000..91993a98f4d
--- /dev/null
+++ b/dev/benchmarks/complex_layout/lib/main.dart
@@ -0,0 +1,636 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart' show timeDilation;
+
+void main() {
+ runApp(
+ new ComplexLayoutApp()
+ );
+}
+
+class ComplexLayoutApp extends StatefulWidget {
+ @override
+ ComplexLayoutAppState createState() => new ComplexLayoutAppState();
+
+ static ComplexLayoutAppState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher());
+}
+
+class ComplexLayoutAppState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return new MaterialApp(
+ theme: lightTheme ? new ThemeData.light() : new ThemeData.dark(),
+ title: 'Advanced Layout',
+ routes: {
+ '/': (BuildContext context) => new ComplexLayout(),
+ }
+ );
+ }
+
+ bool _lightTheme = true;
+ bool get lightTheme => _lightTheme;
+ void set lightTheme(bool value) {
+ setState(() {
+ _lightTheme = value;
+ });
+ }
+}
+
+class ComplexLayout extends StatefulWidget {
+ ComplexLayout({ Key key }) : super(key: key);
+
+ @override
+ ComplexLayoutState createState() => new ComplexLayoutState();
+
+ static ComplexLayoutState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher());
+
+}
+
+class ComplexLayoutState extends State {
+ @override
+ Widget build(BuildContext context) {
+
+ return new Scaffold(
+ appBar: new AppBar(
+ title: new Text('Advanced Layout'),
+ actions: [
+ new IconButton(
+ icon: Icons.create,
+ tooltip: 'Search',
+ onPressed: () {
+ print("Pressed search");
+ }
+ ),
+ new TopBarMenu()
+ ]
+ ),
+ body: new Column(
+ children: [
+ new Flexible(
+ child: new ScrollableMixedWidgetList(
+ key: new Key("main-scroll"),
+ builder: (BuildContext context, int index) {
+ if (index % 2 == 0)
+ return new FancyImageItem(index, key: new Key("Item $index"));
+ else
+ return new FancyGalleryItem(index, key: new Key("Item $index"));
+ }
+ )
+ ),
+ new BottomBar()
+ ]
+ ),
+ drawer: new GalleryDrawer()
+ );
+ }
+}
+
+class TopBarMenu extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return new PopupMenuButton(
+ onSelected: (String value) { print("Selected: $value"); },
+ items: >[
+ new PopupMenuItem(
+ value: "Friends",
+ child: new MenuItemWithIcon(Icons.people, "Friends", "5 new")
+ ),
+ new PopupMenuItem(
+ value: "Events",
+ child: new MenuItemWithIcon(Icons.event, "Events", "12 upcoming")
+ ),
+ new PopupMenuItem(
+ value: "Events",
+ child: new MenuItemWithIcon(Icons.group, "Groups", "14")
+ ),
+ new PopupMenuItem(
+ value: "Events",
+ child: new MenuItemWithIcon(Icons.image, "Pictures", "12")
+ ),
+ new PopupMenuItem(
+ value: "Events",
+ child: new MenuItemWithIcon(Icons.near_me, "Nearby", "33")
+ ),
+ new PopupMenuItem(
+ value: "Friends",
+ child: new MenuItemWithIcon(Icons.people, "Friends", "5")
+ ),
+ new PopupMenuItem(
+ value: "Events",
+ child: new MenuItemWithIcon(Icons.event, "Events", "12")
+ ),
+ new PopupMenuItem(
+ value: "Events",
+ child: new MenuItemWithIcon(Icons.group, "Groups", "14")
+ ),
+ new PopupMenuItem(
+ value: "Events",
+ child: new MenuItemWithIcon(Icons.image, "Pictures", "12")
+ ),
+ new PopupMenuItem(
+ value: "Events",
+ child: new MenuItemWithIcon(Icons.near_me, "Nearby", "33")
+ )
+ ]
+ );
+ }
+}
+
+class MenuItemWithIcon extends StatelessWidget {
+ MenuItemWithIcon(this.icon, this.title, this.subtitle);
+
+ final IconData icon;
+ final String title;
+ final String subtitle;
+
+ @override
+ Widget build(BuildContext context) {
+ return new Row(
+ children: [
+ new Icon(icon: icon),
+ new Padding(
+ padding: new EdgeInsets.only(left: 8.0, right: 8.0),
+ child: new Text(title)
+ ),
+ new Text(subtitle, style: Theme.of(context).textTheme.caption)
+ ]
+ );
+ }
+}
+
+class FancyImageItem extends StatelessWidget {
+ FancyImageItem(this.index, {Key key}) : super(key: key);
+
+ final int index;
+
+ @override
+ Widget build(BuildContext context) {
+ return new BlockBody(
+ children: [
+ new UserHeader("Ali Connors $index"),
+ new ItemDescription(),
+ new ItemImageBox(),
+ new InfoBar(),
+ new Padding(
+ padding: new EdgeInsets.symmetric(horizontal: 8.0),
+ child: new Divider()
+ ),
+ new IconBar(),
+ new FatDivider()
+ ]
+ );
+ }
+}
+
+class FancyGalleryItem extends StatelessWidget {
+ FancyGalleryItem(this.index, {Key key}) : super(key: key);
+
+ final int index;
+ @override
+ Widget build(BuildContext context) {
+ return new BlockBody(
+ children: [
+ new UserHeader("Ali Connors"),
+ new ItemGalleryBox(index),
+ new InfoBar(),
+ new Padding(
+ padding: new EdgeInsets.symmetric(horizontal: 8.0),
+ child: new Divider()
+ ),
+ new IconBar(),
+ new FatDivider()
+ ]
+ );
+ }
+}
+
+class InfoBar extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return new Padding(
+ padding: new EdgeInsets.all(8.0),
+ child: new Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ new MiniIconWithText(Icons.thumb_up, "42"),
+ new Text("3 Comments", style: Theme.of(context).textTheme.caption)
+ ]
+ )
+ );
+ }
+}
+
+class IconBar extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return new Padding(
+ padding: new EdgeInsets.only(left: 16.0, right: 16.0),
+ child: new Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ new IconWithText(Icons.thumb_up, "Like"),
+ new IconWithText(Icons.comment, "Comment"),
+ new IconWithText(Icons.share, "Share"),
+ ]
+ )
+ );
+ }
+}
+
+class IconWithText extends StatelessWidget {
+ IconWithText(this.icon, this.title);
+
+ final IconData icon;
+ final String title;
+
+ @override
+ Widget build(BuildContext context) {
+ return new Row(
+ mainAxisAlignment: MainAxisAlignment.collapse,
+ children: [
+ new IconButton(icon: icon, onPressed: () { print("Pressed $title button"); } ),
+ new Text(title)
+ ]
+ );
+ }
+}
+
+class MiniIconWithText extends StatelessWidget {
+ MiniIconWithText(this.icon, this.title);
+
+ final IconData icon;
+ final String title;
+
+ @override
+ Widget build(BuildContext context) {
+ return new Row(
+ mainAxisAlignment: MainAxisAlignment.collapse,
+ children: [
+ new Padding(
+ padding: new EdgeInsets.only(right: 8.0),
+ child: new Container(
+ width: 16.0,
+ height: 16.0,
+ decoration: new BoxDecoration(
+ backgroundColor: Theme.of(context).primaryColor,
+ shape: BoxShape.circle
+ ),
+ child: new Icon(icon: icon, color: Colors.white, size: 12.0)
+ )
+ ),
+ new Text(title, style: Theme.of(context).textTheme.caption)
+ ]
+ );
+ }
+}
+
+class FatDivider extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return new Container(
+ height: 8.0,
+ decoration: new BoxDecoration(
+ backgroundColor: Theme.of(context).dividerColor
+ )
+ );
+ }
+}
+
+class UserHeader extends StatelessWidget {
+ UserHeader(this.userName);
+
+ final String userName;
+
+ @override
+ Widget build(BuildContext context) {
+ return new Padding(
+ padding: new EdgeInsets.all(8.0),
+ child: new Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ new Padding(
+ padding: new EdgeInsets.only(right: 8.0),
+ child: new AssetImage(
+ name: "packages/flutter_gallery_assets/ali_connors_sml.png",
+ width: 32.0,
+ height: 32.0
+ )
+ ),
+ new Flexible(
+ child: new Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ new RichText(text: new TextSpan(
+ style: Theme.of(context).textTheme.body1,
+ children: [
+ new TextSpan(text: userName, style: new TextStyle(fontWeight: FontWeight.bold)),
+ new TextSpan(text: " shared a new "),
+ new TextSpan(text: "photo", style: new TextStyle(fontWeight: FontWeight.bold))
+ ]
+ )),
+ new Row(
+ children: [
+ new Text("Yesterday at 11:55 • ", style: Theme.of(context).textTheme.caption),
+ new Icon(icon: Icons.people, size: 16.0, color: Theme.of(context).textTheme.caption.color)
+ ]
+ )
+ ]
+ )
+ ),
+ new TopBarMenu()
+ ]
+ )
+ );
+ }
+}
+
+class ItemDescription extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return new Padding(
+ padding: new EdgeInsets.all(8.0),
+ child: new Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.")
+ );
+ }
+}
+
+class ItemImageBox extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return new Padding(
+ padding: new EdgeInsets.all(8.0),
+ child: new Card(
+ child: new Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ new Stack(
+ children: [
+ new SizedBox(
+ height: 230.0,
+ child: new AssetImage(
+ name: "packages/flutter_gallery_assets/top_10_australian_beaches.png"
+ )
+ ),
+ new Theme(
+ data: new ThemeData.dark(),
+ child: new Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ new IconButton(icon: Icons.edit, onPressed: () { print("Pressed edit button"); }),
+ new IconButton(icon: Icons.zoom_in, onPressed: () { print("Pressed zoom button"); })
+ ]
+ )
+ ),
+ new Positioned(
+ bottom: 4.0,
+ left: 4.0,
+ child: new Container(
+ decoration: new BoxDecoration(
+ backgroundColor: Colors.black54,
+ borderRadius: 2.0
+ ),
+ padding: new EdgeInsets.all(4.0),
+ child: new RichText(
+ text: new TextSpan(
+ style: new TextStyle(color: Colors.white),
+ children: [
+ new TextSpan(
+ text: "Photo by "
+ ),
+ new TextSpan(
+ style: new TextStyle(fontWeight: FontWeight.bold),
+ text: "Magic Mike"
+ )
+ ]
+ )
+ )
+ )
+ )
+ ]
+ )
+ ,
+ new Padding(
+ padding: new EdgeInsets.all(8.0),
+ child: new Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ new Text("Where can you find that amazing sunset?", style: Theme.of(context).textTheme.body2),
+ new Text("The sun sets over stinson beach", style: Theme.of(context).textTheme.body1),
+ new Text("flutter.io/amazingsunsets", style: Theme.of(context).textTheme.caption)
+ ]
+ )
+ )
+ ]
+ )
+ )
+ );
+ }
+}
+
+class ItemGalleryBox extends StatelessWidget {
+ ItemGalleryBox(this.index);
+
+ final int index;
+
+ @override
+ Widget build(BuildContext context) {
+ List tabNames = [
+ "A", "B", "C", "D"
+ ];
+
+ return new SizedBox(
+ height: 200.0,
+ child: new TabBarSelection(
+ values: tabNames,
+ child: new Column(
+ children: [
+ new Flexible(
+ child: new TabBarView(
+ children: tabNames.map((String tabName) {
+ return new Container(
+ key: new Key("Tab $index - $tabName"),
+ child: new Padding(
+ padding: new EdgeInsets.all(8.0),
+ child: new Card(
+ child: new Column(
+ children: [
+ new Flexible(
+ child: new Container(
+ decoration: new BoxDecoration(
+ backgroundColor: Theme.of(context).primaryColor
+ ),
+ child: new Center(
+ child: new Text(tabName, style: Theme.of(context).textTheme.headline.copyWith(color: Colors.white))
+ )
+ )
+ ),
+ new Row(
+ children: [
+ new IconButton(
+ icon: Icons.share,
+ onPressed: () { print("Pressed share"); }
+ ),
+ new IconButton(
+ icon: Icons.event,
+ onPressed: () { print("Pressed event"); }
+ ),
+ new Flexible(
+ child: new Padding(
+ padding: new EdgeInsets.only(left: 8.0),
+ child: new Text("This is item $tabName")
+ )
+ )
+ ]
+ )
+ ]
+ )
+ )
+ )
+ );
+ }).toList()
+ )
+ ),
+ new Container(
+ child: new TabPageSelector()
+ )
+ ]
+ )
+ )
+ );
+ }
+}
+
+class BottomBar extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return new Container(
+ decoration: new BoxDecoration(
+ border: new Border(
+ top: new BorderSide(
+ color: Theme.of(context).dividerColor,
+ width: 1.0
+ )
+ )
+ ),
+ child: new Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ new BottomBarButton(Icons.new_releases, "News"),
+ new BottomBarButton(Icons.people, "Requests"),
+ new BottomBarButton(Icons.chat, "Messenger"),
+ new BottomBarButton(Icons.bookmark, "Bookmark"),
+ new BottomBarButton(Icons.alarm, "Alarm")
+ ]
+ )
+ );
+ }
+}
+
+class BottomBarButton extends StatelessWidget {
+ BottomBarButton(this.icon, this.title);
+
+ final IconData icon;
+ final String title;
+
+ @override
+ Widget build(BuildContext context) {
+ return new Padding(
+ padding: new EdgeInsets.all(8.0),
+ child: new Column(
+ children: [
+ new IconButton(
+ icon: icon,
+ onPressed: () { print("Pressed: $title"); }
+ ),
+ new Text(title, style: Theme.of(context).textTheme.caption)
+ ]
+ )
+ );
+ }
+}
+
+class GalleryDrawer extends StatelessWidget {
+ GalleryDrawer({ Key key }) : super(key: key);
+
+ void _changeTheme(BuildContext context, bool value) {
+ ComplexLayoutApp.of(context).lightTheme = value;
+ }
+
+ void _toggleAnimationSpeed(BuildContext context) {
+ ComplexLayoutApp.of(context).setState(() {
+ timeDilation = (timeDilation != 1.0) ? 1.0 : 5.0;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return new Drawer(
+ child: new Block(
+ children: [
+ new FancyDrawerHeader(),
+ new DrawerItem(
+ icon: Icons.brightness_5,
+ onPressed: () { _changeTheme(context, true); },
+ selected: ComplexLayoutApp.of(context).lightTheme,
+ child: new Row(
+ children: [
+ new Flexible(child: new Text('Light')),
+ new Radio(
+ value: true,
+ groupValue: ComplexLayoutApp.of(context).lightTheme,
+ onChanged: (bool value) { _changeTheme(context, value); }
+ )
+ ]
+ )
+ ),
+ new DrawerItem(
+ icon: Icons.brightness_7,
+ onPressed: () { _changeTheme(context, false); },
+ selected: !ComplexLayoutApp.of(context).lightTheme,
+ child: new Row(
+ children: [
+ new Flexible(child: new Text('Dark')),
+ new Radio(
+ value: false,
+ groupValue: ComplexLayoutApp.of(context).lightTheme,
+ onChanged: (bool value) { _changeTheme(context, value); }
+ )
+ ]
+ )
+ ),
+ new Divider(),
+ new DrawerItem(
+ icon: Icons.hourglass_empty,
+ selected: timeDilation != 1.0,
+ onPressed: () { _toggleAnimationSpeed(context); },
+ child: new Row(
+ children: [
+ new Flexible(child: new Text('Animate Slowly')),
+ new Checkbox(
+ value: timeDilation != 1.0,
+ onChanged: (bool value) { _toggleAnimationSpeed(context); }
+ )
+ ]
+ )
+ )
+ ]
+ )
+ );
+ }
+}
+
+class FancyDrawerHeader extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return new Container(
+ decoration: new BoxDecoration(
+ backgroundColor: Colors.purple[500]
+ ),
+ height: 200.0
+ );
+ }
+}
diff --git a/dev/benchmarks/complex_layout/pubspec.yaml b/dev/benchmarks/complex_layout/pubspec.yaml
new file mode 100644
index 00000000000..db5f9bf1780
--- /dev/null
+++ b/dev/benchmarks/complex_layout/pubspec.yaml
@@ -0,0 +1,8 @@
+name: complex_layout
+description: A new flutter project.
+dependencies:
+ flutter:
+ path: ../../../packages/flutter
+ flutter_driver:
+ path: ../../../packages/flutter_driver
+ flutter_gallery_assets: '0.0.14'
diff --git a/dev/benchmarks/complex_layout/test_driver/scroll_perf.dart b/dev/benchmarks/complex_layout/test_driver/scroll_perf.dart
new file mode 100644
index 00000000000..c8a016b41fb
--- /dev/null
+++ b/dev/benchmarks/complex_layout/test_driver/scroll_perf.dart
@@ -0,0 +1,11 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter_driver/driver_extension.dart';
+import 'package:complex_layout/main.dart' as app;
+
+void main() {
+ enableFlutterDriverExtension();
+ app.main();
+}
diff --git a/dev/benchmarks/complex_layout/test_driver/scroll_perf_test.dart b/dev/benchmarks/complex_layout/test_driver/scroll_perf_test.dart
new file mode 100644
index 00000000000..5aca2114c83
--- /dev/null
+++ b/dev/benchmarks/complex_layout/test_driver/scroll_perf_test.dart
@@ -0,0 +1,47 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'package:flutter_driver/flutter_driver.dart';
+import 'package:test/test.dart';
+
+void main() {
+ group('scrolling performance test', () {
+ FlutterDriver driver;
+
+ setUpAll(() async {
+ driver = await FlutterDriver.connect();
+ });
+
+ tearDownAll(() async {
+ if (driver != null)
+ driver.close();
+ });
+
+ test('measure', () async {
+ Map profileJson = await driver.traceAction(() async {
+ // Find the scrollable stock list
+ ObjectRef stockList = await driver.findByValueKey('main-scroll');
+ expect(stockList, isNotNull);
+
+ // Scroll down
+ for (int i = 0; i < 5; i++) {
+ await driver.scroll(stockList, 0.0, -300.0, new Duration(milliseconds: 300));
+ await new Future.delayed(new Duration(milliseconds: 500));
+ }
+
+ // Scroll up
+ for (int i = 0; i < 5; i++) {
+ await driver.scroll(stockList, 0.0, 300.0, new Duration(milliseconds: 300));
+ await new Future.delayed(new Duration(milliseconds: 500));
+ }
+ });
+
+ expect(profileJson, isNotNull);
+ await new File("build/profile.json").writeAsString(JSON.encode(profileJson));
+ });
+ });
+}