Add Cupertino to gallery and add CupertinoButton and many yak friends (#8411)

* Add cupertino to gallery and add CupertinoButto

* Use single quotes

* Add disabled state

* Some review notes

* Make button animation more responsive and tweak timing

* Renamed things Cupertino

* Button with background, move cupertino demos, move material demos

* Move 2 level list too

* Refactor various demo route names

* Some review notes

* More reviews and add test

* Linter as

* Move private constant up
diff --git a/examples/flutter_gallery/lib/demo/all.dart b/examples/flutter_gallery/lib/demo/all.dart
index ed8618f..e574367 100644
--- a/examples/flutter_gallery/lib/demo/all.dart
+++ b/examples/flutter_gallery/lib/demo/all.dart
@@ -2,37 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-export 'bottom_navigation_demo.dart';
-export 'buttons_demo.dart';
 export 'calculator_demo.dart';
-export 'cards_demo.dart';
-export 'chip_demo.dart';
 export 'colors_demo.dart';
 export 'contacts_demo.dart';
-export 'data_table_demo.dart';
-export 'date_and_time_picker_demo.dart';
-export 'dialog_demo.dart';
-export 'drawer_demo.dart';
-export 'expansion_panels_demo.dart';
-export 'grid_list_demo.dart';
-export 'icons_demo.dart';
-export 'leave_behind_demo.dart';
-export 'list_demo.dart';
-export 'menu_demo.dart';
-export 'modal_bottom_sheet_demo.dart';
-export 'overscroll_demo.dart';
-export 'page_selector_demo.dart';
-export 'persistent_bottom_sheet_demo.dart';
 export 'pesto_demo.dart';
-export 'progress_indicator_demo.dart';
-export 'scrollable_tabs_demo.dart';
-export 'selection_controls_demo.dart';
 export 'shrine_demo.dart';
-export 'slider_demo.dart';
-export 'snack_bar_demo.dart';
-export 'tabs_demo.dart';
-export 'tabs_fab_demo.dart';
-export 'text_field_demo.dart';
-export 'tooltip_demo.dart';
-export 'two_level_list_demo.dart';
 export 'typography_demo.dart';
+export 'cupertino/cupertino.dart';
+export 'material/material.dart';
diff --git a/examples/flutter_gallery/lib/demo/cupertino/cupertino.dart b/examples/flutter_gallery/lib/demo/cupertino/cupertino.dart
new file mode 100644
index 0000000..b7cbea5
--- /dev/null
+++ b/examples/flutter_gallery/lib/demo/cupertino/cupertino.dart
@@ -0,0 +1,9 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+export 'cupertino_activity_indicator_demo.dart';
+export 'cupertino_buttons_demo.dart';
+export 'cupertino_dialog_demo.dart';
+export 'cupertino_slider_demo.dart';
+export 'cupertino_switch_demo.dart';
diff --git a/examples/flutter_gallery/lib/demo/cupertino/cupertino_activity_indicator_demo.dart b/examples/flutter_gallery/lib/demo/cupertino/cupertino_activity_indicator_demo.dart
new file mode 100644
index 0000000..28d0b93
--- /dev/null
+++ b/examples/flutter_gallery/lib/demo/cupertino/cupertino_activity_indicator_demo.dart
@@ -0,0 +1,22 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+class CupertinoProgressIndicatorDemo extends StatelessWidget {
+  static const String routeName = '/cupertino/progress_indicator';
+
+  @override
+  Widget build(BuildContext context) {
+    return new Scaffold(
+      appBar: new AppBar(
+        title: new Text('Cupertino Activity Indicator'),
+      ),
+      body: new Center(
+        child: new CupertinoActivityIndicator(),
+      ),
+    );
+  }
+}
diff --git a/examples/flutter_gallery/lib/demo/cupertino/cupertino_buttons_demo.dart b/examples/flutter_gallery/lib/demo/cupertino/cupertino_buttons_demo.dart
new file mode 100644
index 0000000..99525f8
--- /dev/null
+++ b/examples/flutter_gallery/lib/demo/cupertino/cupertino_buttons_demo.dart
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+class CupertinoButtonsDemo extends StatefulWidget {
+  static const String routeName = '/cupertino/buttons';
+
+  @override
+  _CupertinoButtonDemoState createState() => new _CupertinoButtonDemoState();
+}
+
+class _CupertinoButtonDemoState extends State<CupertinoButtonsDemo> {
+  int _pressedCount = 0;
+
+  @override
+  Widget build(BuildContext context) {
+    return new Scaffold(
+      appBar: new AppBar(
+        title: new Text('Cupertino Buttons'),
+      ),
+      body: new Column(
+        children: <Widget> [
+          new Padding(
+            padding: const EdgeInsets.all(16.0),
+            child: new Text('iOS themed buttons are flat. They can have borders or backgrounds but '
+                'only when necessary.'),
+          ),
+          new Expanded(
+            child: new Column(
+              mainAxisAlignment: MainAxisAlignment.center,
+              children: <Widget> [
+                new Text(_pressedCount > 0 ? "Button pressed $_pressedCount times" : " "),
+                new Padding(padding: const EdgeInsets.all(12.0)),
+                new Align(
+                  alignment: const FractionalOffset(0.5, 0.4),
+                  child: new Row(
+                    mainAxisSize: MainAxisSize.min,
+                    children: <Widget>[
+                      new CupertinoButton(
+                        child: new Text('Cupertino Button'),
+                        onPressed: () {
+                          setState(() {_pressedCount++;});
+                        }
+                      ),
+                      new CupertinoButton(
+                        child: new Text('Disabled'),
+                        onPressed: null,
+                      ),
+                    ],
+                  ),
+                ),
+                new Padding(padding: const EdgeInsets.all(12.0)),
+                new CupertinoButton(
+                  child: new Text('With Background'),
+                  color: CupertinoButton.kBlue,
+                  onPressed: () {
+                    setState(() {_pressedCount++;});
+                  }
+                ),
+                new Padding(padding: const EdgeInsets.all(12.0)),
+                new CupertinoButton(
+                  child: new Text('Disabled'),
+                  color: CupertinoButton.kBlue,
+                  onPressed: null,
+                ),
+              ],
+            )
+          ),
+        ],
+      )
+    );
+  }
+}
diff --git a/examples/flutter_gallery/lib/demo/cupertino/cupertino_dialog_demo.dart b/examples/flutter_gallery/lib/demo/cupertino/cupertino_dialog_demo.dart
new file mode 100644
index 0000000..a73fa89
--- /dev/null
+++ b/examples/flutter_gallery/lib/demo/cupertino/cupertino_dialog_demo.dart
@@ -0,0 +1,99 @@
+// 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/cupertino.dart';
+import 'package:flutter/material.dart';
+
+class CupertinoDialogDemo extends StatefulWidget {
+  static const String routeName = '/cupertino/dialog';
+
+  @override
+  _CupertinoDialogDemoState createState() => new _CupertinoDialogDemoState();
+}
+
+class _CupertinoDialogDemoState extends State<CupertinoDialogDemo> {
+  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
+
+  void showDemoDialog<T>({ BuildContext context, Widget child }) {
+    showDialog<T>(
+      context: context,
+      child: child,
+      barrierDismissable: false,
+    )
+    .then<Null>((T value) { // The value passed to Navigator.pop() or null.
+      if (value != null) {
+        _scaffoldKey.currentState.showSnackBar(new SnackBar(
+          content: new Text('You selected: $value')
+        ));
+      }
+    });
+  }
+
+
+
+  @override
+  Widget build(BuildContext context) {
+    return new Scaffold(
+      key: _scaffoldKey,
+      appBar: new AppBar(
+        title: new Text('Cupertino Dialogs'),
+      ),
+      body: new ListView(
+        padding: const EdgeInsets.symmetric(vertical: 24.0, horizontal: 72.0),
+        children: <Widget> [
+          new CupertinoButton(
+            child: new Text('Alert'),
+            color: CupertinoButton.kBlue,
+            onPressed: () {
+              showDemoDialog<String>(
+                context: context,
+                child: new CupertinoAlertDialog(
+                  content: new Text('Discard draft?'),
+                  actions: <Widget>[
+                    new CupertinoDialogAction(
+                      child: new Text('Discard'),
+                      isDestructive: true,
+                      onPressed: () { Navigator.pop(context, 'OK'); }
+                    ),
+                    new CupertinoDialogAction(
+                      child: new Text('Cancel', style: new TextStyle(fontWeight: FontWeight.w600)),
+                      onPressed: () { Navigator.pop(context, 'Cancel'); }
+                    ),
+                  ]
+                ),
+              );
+            },
+          ),
+          new Padding(padding: const EdgeInsets.all(8.0)),
+          new CupertinoButton(
+            child: new Text('Alert with Title'),
+            color: CupertinoButton.kBlue,
+            onPressed: () {
+              showDemoDialog<String>(
+                context: context,
+                child: new CupertinoAlertDialog(
+                  title: new Text('Allow "Maps" to access your location while you use the app?'),
+                  content: new Text(
+                    'Your current location will be displayed on the map and used for directions, '
+                    'nearby search results, and estimated travel times.'
+                  ),
+                  actions: <Widget>[
+                    new CupertinoDialogAction(
+                      child: new Text('Don\'t Allow'),
+                      onPressed: () { Navigator.pop(context, 'Disallow'); }
+                    ),
+                    new CupertinoDialogAction(
+                      child: new Text('Allow'),
+                      onPressed: () { Navigator.pop(context, 'Allow'); }
+                    ),
+                  ]
+                ),
+              );
+            },
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/examples/flutter_gallery/lib/demo/slider_demo.dart b/examples/flutter_gallery/lib/demo/cupertino/cupertino_slider_demo.dart
similarity index 61%
copy from examples/flutter_gallery/lib/demo/slider_demo.dart
copy to examples/flutter_gallery/lib/demo/cupertino/cupertino_slider_demo.dart
index dd55070..13971cd 100644
--- a/examples/flutter_gallery/lib/demo/slider_demo.dart
+++ b/examples/flutter_gallery/lib/demo/cupertino/cupertino_slider_demo.dart
@@ -1,24 +1,27 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 
-class SliderDemo extends StatefulWidget {
-  static const String routeName = '/slider';
+class CupertinoSliderDemo extends StatefulWidget {
+  static const String routeName = '/cupertino/slider';
 
   @override
-  _SliderDemoState createState() => new _SliderDemoState();
+  _CupertinoSliderDemoState createState() => new _CupertinoSliderDemoState();
 }
 
-class _SliderDemoState extends State<SliderDemo> {
+class _CupertinoSliderDemoState extends State<CupertinoSliderDemo> {
   double _value = 25.0;
   double _discreteValue = 20.0;
 
   @override
   Widget build(BuildContext context) {
     return new Scaffold(
-      appBar: new AppBar(title: new Text('Sliders')),
+      appBar: new AppBar(
+        title: new Text('Cupertino Sliders'),
+      ),
       body: new Center(
         child: new Column(
           mainAxisAlignment: MainAxisAlignment.spaceAround,
@@ -26,45 +29,35 @@
             new Column(
               mainAxisSize: MainAxisSize.min,
               children: <Widget> [
-                new Slider(
+                new CupertinoSlider(
                   value: _value,
                   min: 0.0,
                   max: 100.0,
-                  thumbOpenAtMin: true,
                   onChanged: (double value) {
                     setState(() {
                       _value = value;
                     });
                   }
                 ),
-                new Text('Continuous'),
+                new Text('Cupertino Continuous'),
               ]
             ),
             new Column(
               mainAxisSize: MainAxisSize.min,
               children: <Widget> [
-                new Slider(value: 0.25, thumbOpenAtMin: true, onChanged: null),
-                new Text('Disabled'),
-              ]
-            ),
-            new Column(
-              mainAxisSize: MainAxisSize.min,
-              children: <Widget> [
-                new Slider(
+                new CupertinoSlider(
                   value: _discreteValue,
                   min: 0.0,
                   max: 100.0,
                   divisions: 5,
-                  label: '${_discreteValue.round()}',
-                  thumbOpenAtMin: true,
                   onChanged: (double value) {
                     setState(() {
                       _discreteValue = value;
                     });
                   }
                 ),
-                new Text('Discrete'),
-              ],
+                new Text('Cupertino Discrete'),
+              ]
             ),
           ],
         ),
diff --git a/examples/flutter_gallery/lib/demo/cupertino/cupertino_switch_demo.dart b/examples/flutter_gallery/lib/demo/cupertino/cupertino_switch_demo.dart
new file mode 100644
index 0000000..dd2df03
--- /dev/null
+++ b/examples/flutter_gallery/lib/demo/cupertino/cupertino_switch_demo.dart
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+class CupertinoSwitchDemo extends StatefulWidget {
+  static const String routeName = '/cupertino/switch';
+
+  @override
+  _CupertinoSwitchDemoState createState() => new _CupertinoSwitchDemoState();
+}
+
+class _CupertinoSwitchDemoState extends State<CupertinoSwitchDemo> {
+
+  bool _switchValue = false;
+
+  @override
+  Widget build(BuildContext context) {
+    return new Scaffold(
+      appBar: new AppBar(
+        title: new Text('Cupertino Switch'),
+      ),
+      body: new Center(
+        child:  new CupertinoSwitch(
+          value: _switchValue,
+          onChanged: (bool value) {
+            setState(() {
+              _switchValue = value;
+            });
+          }
+        ),
+      ),
+    );
+  }
+}
diff --git a/examples/flutter_gallery/lib/demo/bottom_navigation_demo.dart b/examples/flutter_gallery/lib/demo/material/bottom_navigation_demo.dart
similarity index 98%
rename from examples/flutter_gallery/lib/demo/bottom_navigation_demo.dart
rename to examples/flutter_gallery/lib/demo/material/bottom_navigation_demo.dart
index f1d4a1e..4560277 100644
--- a/examples/flutter_gallery/lib/demo/bottom_navigation_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/bottom_navigation_demo.dart
@@ -79,7 +79,7 @@
 }
 
 class BottomNavigationDemo extends StatefulWidget {
-  static const String routeName = '/bottom_navigation';
+  static const String routeName = '/material/bottom_navigation';
 
   @override
   _BottomNavigationDemoState createState() => new _BottomNavigationDemoState();
diff --git a/examples/flutter_gallery/lib/demo/buttons_demo.dart b/examples/flutter_gallery/lib/demo/material/buttons_demo.dart
similarity index 86%
rename from examples/flutter_gallery/lib/demo/buttons_demo.dart
rename to examples/flutter_gallery/lib/demo/material/buttons_demo.dart
index f12a2b7..3623c5c 100644
--- a/examples/flutter_gallery/lib/demo/buttons_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/buttons_demo.dart
@@ -4,43 +4,43 @@
 
 import 'package:flutter/material.dart';
 
-import '../gallery/demo.dart';
+import '../../gallery/demo.dart';
 
 const String _raisedText =
-    "Raised buttons add dimension to mostly flat layouts. They emphasize "
-    "functions on busy or wide spaces.";
+    'Raised buttons add dimension to mostly flat layouts. They emphasize '
+    'functions on busy or wide spaces.';
 
 const String _raisedCode = 'buttons_raised';
 
-const String _flatText = "A flat button displays an ink splash on press "
-    "but does not lift. Use flat buttons on toolbars, in dialogs and "
-    "inline with padding";
+const String _flatText = 'A flat button displays an ink splash on press '
+    'but does not lift. Use flat buttons on toolbars, in dialogs and '
+    'inline with padding';
 
 const String _flatCode = 'buttons_flat';
 
 const String _dropdownText =
-    "A dropdown button displays a menu that's used to select a value from a "
-    "small set of values. The button displays the current value and a down "
-    "arrow.";
+    'A dropdown button displays a menu that\'s used to select a value from a '
+    'small set of values. The button displays the current value and a down '
+    'arrow.';
 
 const String _dropdownCode = 'buttons_dropdown';
 
 const String _iconText =
-    "IconButtons are appropriate for toggle buttons that allow a single choice "
-    "to be selected or deselected, such as adding or removing an item's star.";
+    'IconButtons are appropriate for toggle buttons that allow a single choice '
+    'to be selected or deselected, such as adding or removing an item\'s star.';
 
 const String _iconCode = 'buttons_icon';
 
 const String _actionText =
-    "Floating action buttons are used for a promoted action. They are "
-    "distinguished by a circled icon floating above the UI and can have motion "
-    "behaviors that include morphing, launching, and a transferring anchor "
-    "point.";
+    'Floating action buttons are used for a promoted action. They are '
+    'distinguished by a circled icon floating above the UI and can have motion '
+    'behaviors that include morphing, launching, and a transferring anchor '
+    'point.';
 
 const String _actionCode = 'buttons_action';
 
 class ButtonsDemo extends StatefulWidget {
-  static const String routeName = '/buttons';
+  static const String routeName = '/material//buttons';
 
   @override
   _ButtonsDemoState createState() => new _ButtonsDemoState();
diff --git a/examples/flutter_gallery/lib/demo/cards_demo.dart b/examples/flutter_gallery/lib/demo/material/cards_demo.dart
similarity index 98%
rename from examples/flutter_gallery/lib/demo/cards_demo.dart
rename to examples/flutter_gallery/lib/demo/material/cards_demo.dart
index fb1e9e9..cba2d80 100644
--- a/examples/flutter_gallery/lib/demo/cards_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/cards_demo.dart
@@ -126,7 +126,7 @@
 }
 
 class CardsDemo extends StatelessWidget {
-  static const String routeName = '/cards';
+  static const String routeName = '/material/cards';
 
   @override
   Widget build(BuildContext context) {
diff --git a/examples/flutter_gallery/lib/demo/chip_demo.dart b/examples/flutter_gallery/lib/demo/material/chip_demo.dart
similarity index 95%
rename from examples/flutter_gallery/lib/demo/chip_demo.dart
rename to examples/flutter_gallery/lib/demo/material/chip_demo.dart
index 162f5c9..11b6e04 100644
--- a/examples/flutter_gallery/lib/demo/chip_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/chip_demo.dart
@@ -5,7 +5,7 @@
 import 'package:flutter/material.dart';
 
 class ChipDemo extends StatefulWidget {
-  static const String routeName = '/chip';
+  static const String routeName = '/material/chip';
 
   @override
   _ChipDemoState createState() => new _ChipDemoState();
diff --git a/examples/flutter_gallery/lib/demo/data_table_demo.dart b/examples/flutter_gallery/lib/demo/material/data_table_demo.dart
similarity index 99%
rename from examples/flutter_gallery/lib/demo/data_table_demo.dart
rename to examples/flutter_gallery/lib/demo/material/data_table_demo.dart
index b17bb6d..2e83248 100644
--- a/examples/flutter_gallery/lib/demo/data_table_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/data_table_demo.dart
@@ -141,7 +141,7 @@
 }
 
 class DataTableDemo extends StatefulWidget {
-  static const String routeName = '/data-table';
+  static const String routeName = '/material/data-table';
 
   @override
   _DataTableDemoState createState() => new _DataTableDemoState();
diff --git a/examples/flutter_gallery/lib/demo/date_and_time_picker_demo.dart b/examples/flutter_gallery/lib/demo/material/date_and_time_picker_demo.dart
similarity index 98%
rename from examples/flutter_gallery/lib/demo/date_and_time_picker_demo.dart
rename to examples/flutter_gallery/lib/demo/material/date_and_time_picker_demo.dart
index 9d71e2a..8ec92ad 100644
--- a/examples/flutter_gallery/lib/demo/date_and_time_picker_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/date_and_time_picker_demo.dart
@@ -110,7 +110,7 @@
 }
 
 class DateAndTimePickerDemo extends StatefulWidget {
-  static const String routeName = '/date-and-time-pickers';
+  static const String routeName = '/material/date-and-time-pickers';
 
   @override
   _DateAndTimePickerDemoState createState() => new _DateAndTimePickerDemoState();
diff --git a/examples/flutter_gallery/lib/demo/dialog_demo.dart b/examples/flutter_gallery/lib/demo/material/dialog_demo.dart
similarity index 98%
rename from examples/flutter_gallery/lib/demo/dialog_demo.dart
rename to examples/flutter_gallery/lib/demo/material/dialog_demo.dart
index 9c5b761..5f80b34 100644
--- a/examples/flutter_gallery/lib/demo/dialog_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/dialog_demo.dart
@@ -47,7 +47,7 @@
 }
 
 class DialogDemo extends StatefulWidget {
-  static const String routeName = '/dialog';
+  static const String routeName = '/material/dialog';
 
   @override
   DialogDemoState createState() => new DialogDemoState();
@@ -68,7 +68,7 @@
   void showDemoDialog<T>({ BuildContext context, Widget child }) {
     showDialog<T>(
       context: context,
-      child: child
+      child: child,
     )
     .then<Null>((T value) { // The value passed to Navigator.pop() or null.
       if (value != null) {
@@ -195,7 +195,7 @@
                 builder: (BuildContext context) => new FullScreenDialogDemo()
               ));
             }
-          )
+          ),
         ]
         // Add a little space between the buttons
         .map((Widget button) {
diff --git a/examples/flutter_gallery/lib/demo/drawer_demo.dart b/examples/flutter_gallery/lib/demo/material/drawer_demo.dart
similarity index 98%
rename from examples/flutter_gallery/lib/demo/drawer_demo.dart
rename to examples/flutter_gallery/lib/demo/material/drawer_demo.dart
index 25987b6..389dd61 100644
--- a/examples/flutter_gallery/lib/demo/drawer_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/drawer_demo.dart
@@ -9,7 +9,7 @@
 const String _kAsset2 = 'packages/flutter_gallery_assets/shrine/vendors/sandra-adams.jpg';
 
 class DrawerDemo extends StatefulWidget {
-  static const String routeName = '/drawer';
+  static const String routeName = '/material/drawer';
 
   @override
   _DrawerDemoState createState() => new _DrawerDemoState();
diff --git a/examples/flutter_gallery/lib/demo/expansion_panels_demo.dart b/examples/flutter_gallery/lib/demo/material/expansion_panels_demo.dart
similarity index 99%
rename from examples/flutter_gallery/lib/demo/expansion_panels_demo.dart
rename to examples/flutter_gallery/lib/demo/material/expansion_panels_demo.dart
index b5c004c..16837b7 100644
--- a/examples/flutter_gallery/lib/demo/expansion_panels_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/expansion_panels_demo.dart
@@ -170,7 +170,7 @@
 }
 
 class ExpasionPanelsDemo extends StatefulWidget {
-  static const String routeName = '/expansion_panels';
+  static const String routeName = '/material/expansion_panels';
 
   @override
   _ExpansionPanelsDemoState createState() => new _ExpansionPanelsDemoState();
diff --git a/examples/flutter_gallery/lib/demo/full_screen_dialog_demo.dart b/examples/flutter_gallery/lib/demo/material/full_screen_dialog_demo.dart
similarity index 100%
rename from examples/flutter_gallery/lib/demo/full_screen_dialog_demo.dart
rename to examples/flutter_gallery/lib/demo/material/full_screen_dialog_demo.dart
diff --git a/examples/flutter_gallery/lib/demo/grid_list_demo.dart b/examples/flutter_gallery/lib/demo/material/grid_list_demo.dart
similarity index 99%
rename from examples/flutter_gallery/lib/demo/grid_list_demo.dart
rename to examples/flutter_gallery/lib/demo/material/grid_list_demo.dart
index 4f50bc2..cf61413 100644
--- a/examples/flutter_gallery/lib/demo/grid_list_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/grid_list_demo.dart
@@ -232,7 +232,7 @@
 class GridListDemo extends StatefulWidget {
   GridListDemo({ Key key }) : super(key: key);
 
-  static const String routeName = '/grid-list';
+  static const String routeName = '/material/grid-list';
 
   @override
   GridListDemoState createState() => new GridListDemoState();
diff --git a/examples/flutter_gallery/lib/demo/icons_demo.dart b/examples/flutter_gallery/lib/demo/material/icons_demo.dart
similarity index 98%
rename from examples/flutter_gallery/lib/demo/icons_demo.dart
rename to examples/flutter_gallery/lib/demo/material/icons_demo.dart
index a609adc..d223ced 100644
--- a/examples/flutter_gallery/lib/demo/icons_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/icons_demo.dart
@@ -5,7 +5,7 @@
 import 'package:flutter/material.dart';
 
 class IconsDemo extends StatefulWidget {
-  static const String routeName = '/icons';
+  static const String routeName = '/material/icons';
 
   @override
   IconsDemoState createState() => new IconsDemoState();
diff --git a/examples/flutter_gallery/lib/demo/leave_behind_demo.dart b/examples/flutter_gallery/lib/demo/material/leave_behind_demo.dart
similarity index 98%
rename from examples/flutter_gallery/lib/demo/leave_behind_demo.dart
rename to examples/flutter_gallery/lib/demo/material/leave_behind_demo.dart
index df8e61e..a229f7a 100644
--- a/examples/flutter_gallery/lib/demo/leave_behind_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/leave_behind_demo.dart
@@ -31,7 +31,7 @@
 class LeaveBehindDemo extends StatefulWidget {
   LeaveBehindDemo({ Key key }) : super(key: key);
 
-  static const String routeName = '/leave-behind';
+  static const String routeName = '/material/leave-behind';
 
   @override
   LeaveBehindDemoState createState() => new LeaveBehindDemoState();
diff --git a/examples/flutter_gallery/lib/demo/list_demo.dart b/examples/flutter_gallery/lib/demo/material/list_demo.dart
similarity index 98%
rename from examples/flutter_gallery/lib/demo/list_demo.dart
rename to examples/flutter_gallery/lib/demo/material/list_demo.dart
index ddf9578..646770d 100644
--- a/examples/flutter_gallery/lib/demo/list_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/list_demo.dart
@@ -7,7 +7,7 @@
 class ListDemo extends StatefulWidget {
   ListDemo({ Key key }) : super(key: key);
 
-  static const String routeName = '/list';
+  static const String routeName = '/material/list';
 
   @override
   _ListDemoState createState() => new _ListDemoState();
diff --git a/examples/flutter_gallery/lib/demo/material/material.dart b/examples/flutter_gallery/lib/demo/material/material.dart
new file mode 100644
index 0000000..96078a8
--- /dev/null
+++ b/examples/flutter_gallery/lib/demo/material/material.dart
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+export 'bottom_navigation_demo.dart';
+export 'buttons_demo.dart';
+export 'cards_demo.dart';
+export 'chip_demo.dart';
+export 'data_table_demo.dart';
+export 'date_and_time_picker_demo.dart';
+export 'dialog_demo.dart';
+export 'drawer_demo.dart';
+export 'expansion_panels_demo.dart';
+export 'grid_list_demo.dart';
+export 'icons_demo.dart';
+export 'leave_behind_demo.dart';
+export 'list_demo.dart';
+export 'menu_demo.dart';
+export 'modal_bottom_sheet_demo.dart';
+export 'overscroll_demo.dart';
+export 'page_selector_demo.dart';
+export 'persistent_bottom_sheet_demo.dart';
+export 'progress_indicator_demo.dart';
+export 'scrollable_tabs_demo.dart';
+export 'selection_controls_demo.dart';
+export 'slider_demo.dart';
+export 'snack_bar_demo.dart';
+export 'tabs_demo.dart';
+export 'tabs_fab_demo.dart';
+export 'text_field_demo.dart';
+export 'tooltip_demo.dart';
+export 'two_level_list_demo.dart';
diff --git a/examples/flutter_gallery/lib/demo/menu_demo.dart b/examples/flutter_gallery/lib/demo/material/menu_demo.dart
similarity index 99%
rename from examples/flutter_gallery/lib/demo/menu_demo.dart
rename to examples/flutter_gallery/lib/demo/material/menu_demo.dart
index 0822629..b355dff 100644
--- a/examples/flutter_gallery/lib/demo/menu_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/menu_demo.dart
@@ -7,7 +7,7 @@
 class MenuDemo extends StatefulWidget {
   MenuDemo({ Key key }) : super(key: key);
 
-  static const String routeName = '/menu';
+  static const String routeName = '/material/menu';
 
   @override
   MenuDemoState createState() => new MenuDemoState();
diff --git a/examples/flutter_gallery/lib/demo/modal_bottom_sheet_demo.dart b/examples/flutter_gallery/lib/demo/material/modal_bottom_sheet_demo.dart
similarity index 94%
rename from examples/flutter_gallery/lib/demo/modal_bottom_sheet_demo.dart
rename to examples/flutter_gallery/lib/demo/material/modal_bottom_sheet_demo.dart
index 24650f1..c0eee71 100644
--- a/examples/flutter_gallery/lib/demo/modal_bottom_sheet_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/modal_bottom_sheet_demo.dart
@@ -5,7 +5,7 @@
 import 'package:flutter/material.dart';
 
 class ModalBottomSheetDemo extends StatelessWidget {
-  static const String routeName = '/modal-bottom-sheet';
+  static const String routeName = '/material/modal-bottom-sheet';
 
   @override
   Widget build(BuildContext context) {
diff --git a/examples/flutter_gallery/lib/demo/overscroll_demo.dart b/examples/flutter_gallery/lib/demo/material/overscroll_demo.dart
similarity index 97%
rename from examples/flutter_gallery/lib/demo/overscroll_demo.dart
rename to examples/flutter_gallery/lib/demo/material/overscroll_demo.dart
index a8079ef..4e28e80 100644
--- a/examples/flutter_gallery/lib/demo/overscroll_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/overscroll_demo.dart
@@ -11,7 +11,7 @@
 class OverscrollDemo extends StatefulWidget {
   OverscrollDemo({ Key key }) : super(key: key);
 
-  static const String routeName = '/overscroll';
+  static const String routeName = '/material/overscroll';
 
   @override
   OverscrollDemoState createState() => new OverscrollDemoState();
diff --git a/examples/flutter_gallery/lib/demo/page_selector_demo.dart b/examples/flutter_gallery/lib/demo/material/page_selector_demo.dart
similarity index 97%
rename from examples/flutter_gallery/lib/demo/page_selector_demo.dart
rename to examples/flutter_gallery/lib/demo/material/page_selector_demo.dart
index ba0d04b..d4f0dc5 100644
--- a/examples/flutter_gallery/lib/demo/page_selector_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/page_selector_demo.dart
@@ -63,7 +63,7 @@
 }
 
 class PageSelectorDemo extends StatelessWidget {
-  static const String routeName = '/page-selector';
+  static const String routeName = '/material/page-selector';
   static final List<IconData> icons = <IconData>[
     Icons.event,
     Icons.home,
diff --git a/examples/flutter_gallery/lib/demo/persistent_bottom_sheet_demo.dart b/examples/flutter_gallery/lib/demo/material/persistent_bottom_sheet_demo.dart
similarity index 96%
rename from examples/flutter_gallery/lib/demo/persistent_bottom_sheet_demo.dart
rename to examples/flutter_gallery/lib/demo/material/persistent_bottom_sheet_demo.dart
index 5844eb5..4f0df21 100644
--- a/examples/flutter_gallery/lib/demo/persistent_bottom_sheet_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/persistent_bottom_sheet_demo.dart
@@ -5,7 +5,7 @@
 import 'package:flutter/material.dart';
 
 class PersistentBottomSheetDemo extends StatefulWidget {
-  static const String routeName = '/persistent-bottom-sheet';
+  static const String routeName = '/material/persistent-bottom-sheet';
 
   @override
   _PersistentBottomSheetDemoState createState() => new _PersistentBottomSheetDemoState();
diff --git a/examples/flutter_gallery/lib/demo/progress_indicator_demo.dart b/examples/flutter_gallery/lib/demo/material/progress_indicator_demo.dart
similarity index 97%
rename from examples/flutter_gallery/lib/demo/progress_indicator_demo.dart
rename to examples/flutter_gallery/lib/demo/material/progress_indicator_demo.dart
index 8f773fb..0385d6a 100644
--- a/examples/flutter_gallery/lib/demo/progress_indicator_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/progress_indicator_demo.dart
@@ -5,7 +5,7 @@
 import 'package:flutter/material.dart';
 
 class ProgressIndicatorDemo extends StatefulWidget {
-  static const String routeName = '/progress-indicator';
+  static const String routeName = '/material/progress-indicator';
 
   @override
   _ProgressIndicatorDemoState createState() => new _ProgressIndicatorDemoState();
diff --git a/examples/flutter_gallery/lib/demo/scrollable_tabs_demo.dart b/examples/flutter_gallery/lib/demo/material/scrollable_tabs_demo.dart
similarity index 97%
rename from examples/flutter_gallery/lib/demo/scrollable_tabs_demo.dart
rename to examples/flutter_gallery/lib/demo/material/scrollable_tabs_demo.dart
index b969e62..2e4901c 100644
--- a/examples/flutter_gallery/lib/demo/scrollable_tabs_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/scrollable_tabs_demo.dart
@@ -26,7 +26,7 @@
 ];
 
 class ScrollableTabsDemo extends StatefulWidget {
-  static const String routeName = '/scrollable-tabs';
+  static const String routeName = '/material/scrollable-tabs';
 
   @override
   ScrollableTabsDemoState createState() => new ScrollableTabsDemoState();
diff --git a/examples/flutter_gallery/lib/demo/selection_controls_demo.dart b/examples/flutter_gallery/lib/demo/material/selection_controls_demo.dart
similarity index 96%
rename from examples/flutter_gallery/lib/demo/selection_controls_demo.dart
rename to examples/flutter_gallery/lib/demo/material/selection_controls_demo.dart
index 8cb9208..f0f560c 100644
--- a/examples/flutter_gallery/lib/demo/selection_controls_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/selection_controls_demo.dart
@@ -4,7 +4,7 @@
 
 import 'package:flutter/material.dart';
 
-import '../gallery/demo.dart';
+import '../../gallery/demo.dart';
 
 const String _checkboxText =
   "Checkboxes allow the user to select multiple options from a set.";
@@ -26,7 +26,7 @@
 const String _switchCode = 'selectioncontrols_switch';
 
 class SelectionControlsDemo extends StatefulWidget {
-  static const String routeName = '/selection-controls';
+  static const String routeName = '/material/selection-controls';
 
   @override
   _SelectionControlsDemoState createState() => new _SelectionControlsDemoState();
@@ -169,15 +169,15 @@
             value: switchValue,
             onChanged: (bool value) {
               setState(() {
-              switchValue = value;
+                switchValue = value;
               });
             }
           ),
           // Disabled switches
           new Switch(value: true, onChanged: null),
           new Switch(value: false, onChanged: null)
-        ]
-      )
+        ],
+      ),
     );
   }
 }
diff --git a/examples/flutter_gallery/lib/demo/slider_demo.dart b/examples/flutter_gallery/lib/demo/material/slider_demo.dart
similarity index 97%
rename from examples/flutter_gallery/lib/demo/slider_demo.dart
rename to examples/flutter_gallery/lib/demo/material/slider_demo.dart
index dd55070..41d142a 100644
--- a/examples/flutter_gallery/lib/demo/slider_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/slider_demo.dart
@@ -5,7 +5,7 @@
 import 'package:flutter/material.dart';
 
 class SliderDemo extends StatefulWidget {
-  static const String routeName = '/slider';
+  static const String routeName = '/material/slider';
 
   @override
   _SliderDemoState createState() => new _SliderDemoState();
diff --git a/examples/flutter_gallery/lib/demo/snack_bar_demo.dart b/examples/flutter_gallery/lib/demo/material/snack_bar_demo.dart
similarity index 97%
rename from examples/flutter_gallery/lib/demo/snack_bar_demo.dart
rename to examples/flutter_gallery/lib/demo/material/snack_bar_demo.dart
index dfc2fe8..e78d255 100644
--- a/examples/flutter_gallery/lib/demo/snack_bar_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/snack_bar_demo.dart
@@ -19,7 +19,7 @@
 class SnackBarDemo extends StatefulWidget {
   SnackBarDemo({ Key key }) : super(key: key);
 
-  static const String routeName = '/snack-bar';
+  static const String routeName = '/material/snack-bar';
 
   @override
   _SnackBarDemoState createState() => new _SnackBarDemoState();
diff --git a/examples/flutter_gallery/lib/demo/tabs_demo.dart b/examples/flutter_gallery/lib/demo/material/tabs_demo.dart
similarity index 98%
rename from examples/flutter_gallery/lib/demo/tabs_demo.dart
rename to examples/flutter_gallery/lib/demo/material/tabs_demo.dart
index 6e41931..767afc8 100644
--- a/examples/flutter_gallery/lib/demo/tabs_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/tabs_demo.dart
@@ -112,7 +112,7 @@
 }
 
 class TabsDemo extends StatelessWidget {
-  static const String routeName = '/tabs';
+  static const String routeName = '/material/tabs';
 
   @override
   Widget build(BuildContext context) {
diff --git a/examples/flutter_gallery/lib/demo/tabs_fab_demo.dart b/examples/flutter_gallery/lib/demo/material/tabs_fab_demo.dart
similarity index 98%
rename from examples/flutter_gallery/lib/demo/tabs_fab_demo.dart
rename to examples/flutter_gallery/lib/demo/material/tabs_fab_demo.dart
index 5cdfa53..168479b 100644
--- a/examples/flutter_gallery/lib/demo/tabs_fab_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/tabs_fab_demo.dart
@@ -33,7 +33,7 @@
 ];
 
 class TabsFabDemo extends StatefulWidget {
-  static const String routeName = '/tabs-fab';
+  static const String routeName = '/material/tabs-fab';
 
   @override
   _TabsFabDemoState createState() => new _TabsFabDemoState();
diff --git a/examples/flutter_gallery/lib/demo/text_field_demo.dart b/examples/flutter_gallery/lib/demo/material/text_field_demo.dart
similarity index 98%
rename from examples/flutter_gallery/lib/demo/text_field_demo.dart
rename to examples/flutter_gallery/lib/demo/material/text_field_demo.dart
index 99f581c..d84487f 100644
--- a/examples/flutter_gallery/lib/demo/text_field_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/text_field_demo.dart
@@ -9,7 +9,7 @@
 class TextFieldDemo extends StatefulWidget {
   TextFieldDemo({ Key key }) : super(key: key);
 
-  static const String routeName = '/text-field';
+  static const String routeName = '/material/text-field';
 
   @override
   TextFieldDemoState createState() => new TextFieldDemoState();
diff --git a/examples/flutter_gallery/lib/demo/tooltip_demo.dart b/examples/flutter_gallery/lib/demo/material/tooltip_demo.dart
similarity index 97%
rename from examples/flutter_gallery/lib/demo/tooltip_demo.dart
rename to examples/flutter_gallery/lib/demo/material/tooltip_demo.dart
index 164f49e..84c3c0d 100644
--- a/examples/flutter_gallery/lib/demo/tooltip_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/tooltip_demo.dart
@@ -11,7 +11,7 @@
 
 class TooltipDemo extends StatelessWidget {
 
-  static const String routeName = '/tooltips';
+  static const String routeName = '/material/tooltips';
 
   @override
   Widget build(BuildContext context) {
diff --git a/examples/flutter_gallery/lib/demo/two_level_list_demo.dart b/examples/flutter_gallery/lib/demo/material/two_level_list_demo.dart
similarity index 94%
rename from examples/flutter_gallery/lib/demo/two_level_list_demo.dart
rename to examples/flutter_gallery/lib/demo/material/two_level_list_demo.dart
index 392685b..2307926 100644
--- a/examples/flutter_gallery/lib/demo/two_level_list_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/two_level_list_demo.dart
@@ -5,7 +5,7 @@
 import 'package:flutter/material.dart';
 
 class TwoLevelListDemo extends StatelessWidget {
-  static const String routeName = '/two-level-list';
+  static const String routeName = '/material/two-level-list';
 
   @override
   Widget build(BuildContext context) {
diff --git a/examples/flutter_gallery/lib/gallery/item.dart b/examples/flutter_gallery/lib/gallery/item.dart
index 8f9a9c4..c68f110 100644
--- a/examples/flutter_gallery/lib/gallery/item.dart
+++ b/examples/flutter_gallery/lib/gallery/item.dart
@@ -11,7 +11,7 @@
 typedef Widget GalleryDemoBuilder();
 
 class GalleryItem extends StatelessWidget {
-  GalleryItem({ this.title, this.subtitle, this.category: 'Components', this.routeName, this.buildRoute }) {
+  GalleryItem({ this.title, this.subtitle, this.category, this.routeName, this.buildRoute }) {
     assert(title != null);
     assert(category != null);
     assert(routeName != null);
@@ -51,184 +51,247 @@
     subtitle: 'Simple recipe browser',
     category: 'Demos',
     routeName: PestoDemo.routeName,
-    buildRoute: (BuildContext context) => new PestoDemo()
+    buildRoute: (BuildContext context) => new PestoDemo(),
   ),
   new GalleryItem(
     title: 'Shrine',
     subtitle:'Basic shopping app',
     category: 'Demos',
     routeName: ShrineDemo.routeName,
-    buildRoute: (BuildContext context) => new ShrineDemo()
+    buildRoute: (BuildContext context) => new ShrineDemo(),
   ),
   new GalleryItem(
     title: 'Contact profile',
-    category: 'Demos',
     subtitle: 'Address book entry with a flexible appbar',
+    category: 'Demos',
     routeName: ContactsDemo.routeName,
-    buildRoute: (BuildContext context) => new ContactsDemo()
+    buildRoute: (BuildContext context) => new ContactsDemo(),
   ),
-  // Components
+  // Material Components
   new GalleryItem(
     title: 'Bottom navigation',
     subtitle: 'Bottom navigation with cross-fading views',
+    category: 'Material Components',
     routeName: BottomNavigationDemo.routeName,
-    buildRoute: (BuildContext context) => new BottomNavigationDemo()
+    buildRoute: (BuildContext context) => new BottomNavigationDemo(),
   ),
   new GalleryItem(
     title: 'Buttons',
     subtitle: 'All kinds: flat, raised, dropdown, icon, etc',
+    category: 'Material Components',
     routeName: ButtonsDemo.routeName,
-    buildRoute: (BuildContext context) => new ButtonsDemo()
+    buildRoute: (BuildContext context) => new ButtonsDemo(),
   ),
   new GalleryItem(
     title: 'Cards',
     subtitle: 'Material with rounded corners and a drop shadow',
+    category: 'Material Components',
     routeName: CardsDemo.routeName,
-    buildRoute: (BuildContext context) => new CardsDemo()
+    buildRoute: (BuildContext context) => new CardsDemo(),
   ),
   new GalleryItem(
     title: 'Chips',
     subtitle: 'Label with an optional delete button and avatar',
+    category: 'Material Components',
     routeName: ChipDemo.routeName,
-    buildRoute: (BuildContext context) => new ChipDemo()
+    buildRoute: (BuildContext context) => new ChipDemo(),
   ),
   new GalleryItem(
     title: 'Date and time pickers',
     subtitle: 'Date and time selection widgets',
+    category: 'Material Components',
     routeName: DateAndTimePickerDemo.routeName,
-    buildRoute: (BuildContext context) => new DateAndTimePickerDemo()
+    buildRoute: (BuildContext context) => new DateAndTimePickerDemo(),
   ),
   new GalleryItem(
     title: 'Dialog',
     subtitle: 'All kinds: simple, alert, fullscreen, etc',
+    category: 'Material Components',
     routeName: DialogDemo.routeName,
-    buildRoute: (BuildContext context) => new DialogDemo()
+    buildRoute: (BuildContext context) => new DialogDemo(),
   ),
   new GalleryItem(
     title: 'Drawer',
     subtitle: 'Navigation drawer with a standard header',
+    category: 'Material Components',
     routeName: DrawerDemo.routeName,
-    buildRoute: (BuildContext context) => new DrawerDemo()
+    buildRoute: (BuildContext context) => new DrawerDemo(),
   ),
   new GalleryItem(
     title: 'Expand/collapse list control',
     subtitle: 'List with one level of sublists',
+    category: 'Material Components',
     routeName: TwoLevelListDemo.routeName,
-    buildRoute: (BuildContext context) => new TwoLevelListDemo()
+    buildRoute: (BuildContext context) => new TwoLevelListDemo(),
   ),
   new GalleryItem(
     title: 'Expansion panels',
     subtitle: 'List of expanding panels',
+    category: 'Material Components',
     routeName: ExpasionPanelsDemo.routeName,
-    buildRoute: (BuildContext context) => new ExpasionPanelsDemo()
+    buildRoute: (BuildContext context) => new ExpasionPanelsDemo(),
   ),
   new GalleryItem(
     title: 'Floating action button',
     subtitle: 'Action buttons with transitions',
+    category: 'Material Components',
     routeName: TabsFabDemo.routeName,
-    buildRoute: (BuildContext context) => new TabsFabDemo()
+    buildRoute: (BuildContext context) => new TabsFabDemo(),
   ),
   new GalleryItem(
     title: 'Grid',
     subtitle: 'Row and column layout',
+    category: 'Material Components',
     routeName: GridListDemo.routeName,
-    buildRoute: (BuildContext context) => new GridListDemo()
+    buildRoute: (BuildContext context) => new GridListDemo(),
   ),
   new GalleryItem(
     title: 'Icons',
     subtitle: 'Enabled and disabled icons with varying opacity',
+    category: 'Material Components',
     routeName: IconsDemo.routeName,
-    buildRoute: (BuildContext context) => new IconsDemo()
+    buildRoute: (BuildContext context) => new IconsDemo(),
   ),
   new GalleryItem(
     title: 'Leave-behind list items',
     subtitle: 'List items with hidden actions',
+    category: 'Material Components',
     routeName: LeaveBehindDemo.routeName,
-    buildRoute: (BuildContext context) => new LeaveBehindDemo()
+    buildRoute: (BuildContext context) => new LeaveBehindDemo(),
   ),
   new GalleryItem(
     title: 'List',
     subtitle: 'Layout variations for scrollable lists',
+    category: 'Material Components',
     routeName: ListDemo.routeName,
-    buildRoute: (BuildContext context) => new ListDemo()
+    buildRoute: (BuildContext context) => new ListDemo(),
   ),
   new GalleryItem(
     title: 'Menus',
     subtitle: 'Menu buttons and simple menus',
+    category: 'Material Components',
     routeName: MenuDemo.routeName,
-    buildRoute: (BuildContext context) => new MenuDemo()
+    buildRoute: (BuildContext context) => new MenuDemo(),
   ),
   new GalleryItem(
     title: 'Modal bottom sheet',
     subtitle: 'Modal sheet that slides up from the bottom',
+    category: 'Material Components',
     routeName: ModalBottomSheetDemo.routeName,
-    buildRoute: (BuildContext context) => new ModalBottomSheetDemo()
+    buildRoute: (BuildContext context) => new ModalBottomSheetDemo(),
   ),
   new GalleryItem(
     title: 'Page selector',
     subtitle: 'PageView with indicator',
+    category: 'Material Components',
     routeName: PageSelectorDemo.routeName,
-    buildRoute: (BuildContext context) => new PageSelectorDemo()
+    buildRoute: (BuildContext context) => new PageSelectorDemo(),
   ),
   new GalleryItem(
     title: 'Persistent bottom sheet',
     subtitle: 'Sheet that slides up from the bottom',
+    category: 'Material Components',
     routeName: PersistentBottomSheetDemo.routeName,
-    buildRoute: (BuildContext context) => new PersistentBottomSheetDemo()
+    buildRoute: (BuildContext context) => new PersistentBottomSheetDemo(),
   ),
   new GalleryItem(
     title: 'Progress indicators',
     subtitle: 'All kinds: linear, circular, indeterminate, etc',
+    category: 'Material Components',
     routeName: ProgressIndicatorDemo.routeName,
-    buildRoute: (BuildContext context) => new ProgressIndicatorDemo()
+    buildRoute: (BuildContext context) => new ProgressIndicatorDemo(),
   ),
   new GalleryItem(
     title: 'Pull to refresh',
     subtitle: 'Refresh indicators',
+    category: 'Material Components',
     routeName: OverscrollDemo.routeName,
-    buildRoute: (BuildContext context) => new OverscrollDemo()
+    buildRoute: (BuildContext context) => new OverscrollDemo(),
   ),
   new GalleryItem(
     title: 'Scrollable tabs',
     subtitle: 'Tab bar that scrolls',
+    category: 'Material Components',
     routeName: ScrollableTabsDemo.routeName,
-    buildRoute: (BuildContext context) => new ScrollableTabsDemo()
+    buildRoute: (BuildContext context) => new ScrollableTabsDemo(),
   ),
   new GalleryItem(
     title: 'Selection controls',
     subtitle: 'Checkboxes, radio buttons, and switches',
+    category: 'Material Components',
     routeName: SelectionControlsDemo.routeName,
-    buildRoute: (BuildContext context) => new SelectionControlsDemo()
+    buildRoute: (BuildContext context) => new SelectionControlsDemo(),
   ),
   new GalleryItem(
     title: 'Sliders',
     subtitle: 'Widgets that select a value by dragging the slider thumb',
+    category: 'Material Components',
     routeName: SliderDemo.routeName,
-    buildRoute: (BuildContext context) => new SliderDemo()
+    buildRoute: (BuildContext context) => new SliderDemo(),
   ),
   new GalleryItem(
     title: 'Snackbar',
     subtitle: 'Temporary message that appears at the bottom',
+    category: 'Material Components',
     routeName: SnackBarDemo.routeName,
-    buildRoute: (BuildContext context) => new SnackBarDemo()
+    buildRoute: (BuildContext context) => new SnackBarDemo(),
   ),
   new GalleryItem(
     title: 'Tabs',
     subtitle: 'Tabs with independently scrollable views',
+    category: 'Material Components',
     routeName: TabsDemo.routeName,
-    buildRoute: (BuildContext context) => new TabsDemo()
+    buildRoute: (BuildContext context) => new TabsDemo(),
   ),
   new GalleryItem(
     title: 'Text fields',
     subtitle: 'Single line of editable text and numbers',
+    category: 'Material Components',
     routeName: TextFieldDemo.routeName,
-    buildRoute: (BuildContext context) => new TextFieldDemo()
+    buildRoute: (BuildContext context) => new TextFieldDemo(),
   ),
   new GalleryItem(
     title: 'Tooltips',
     subtitle: 'Short message displayed after a long-press',
+    category: 'Material Components',
     routeName: TooltipDemo.routeName,
-    buildRoute: (BuildContext context) => new TooltipDemo()
+    buildRoute: (BuildContext context) => new TooltipDemo(),
+  ),
+  // Cupertino Components
+  new GalleryItem(
+    title: 'Activity Indicator',
+    subtitle: 'Cupertino styled activity indicator',
+    category: 'Cupertino Components',
+    routeName: CupertinoProgressIndicatorDemo.routeName,
+    buildRoute: (BuildContext context) => new CupertinoProgressIndicatorDemo(),
+  ),
+  new GalleryItem(
+    title: 'Buttons',
+    subtitle: 'Cupertino styled buttons',
+    category: 'Cupertino Components',
+    routeName: CupertinoButtonsDemo.routeName,
+    buildRoute: (BuildContext context) => new CupertinoButtonsDemo(),
+  ),
+  new GalleryItem(
+    title: 'Dialogs',
+    subtitle: 'Cupertino styled dialogs',
+    category: 'Cupertino Components',
+    routeName: CupertinoDialogDemo.routeName,
+    buildRoute: (BuildContext context) => new CupertinoDialogDemo(),
+  ),
+  new GalleryItem(
+    title: 'Sliders',
+    subtitle: 'Cupertino styled sliders',
+    category: 'Cupertino Components',
+    routeName: CupertinoSliderDemo.routeName,
+    buildRoute: (BuildContext context) => new CupertinoSliderDemo(),
+  ),
+  new GalleryItem(
+    title: 'Switches',
+    subtitle: 'Cupertino styled switches',
+    category: 'Cupertino Components',
+    routeName: CupertinoSwitchDemo.routeName,
+    buildRoute: (BuildContext context) => new CupertinoSwitchDemo(),
   ),
   // Styles
   new GalleryItem(
@@ -236,13 +299,13 @@
     subtitle: 'All of the predefined colors',
     category: 'Style',
     routeName: ColorsDemo.routeName,
-    buildRoute: (BuildContext context) => new ColorsDemo()
+    buildRoute: (BuildContext context) => new ColorsDemo(),
   ),
   new GalleryItem(
     title: 'Typography',
     subtitle: 'All of the predefined text styles',
     category: 'Style',
     routeName: TypographyDemo.routeName,
-    buildRoute: (BuildContext context) => new TypographyDemo()
+    buildRoute: (BuildContext context) => new TypographyDemo(),
   )
 ];
diff --git a/packages/flutter/lib/cupertino.dart b/packages/flutter/lib/cupertino.dart
index 1380952..cede429 100644
--- a/packages/flutter/lib/cupertino.dart
+++ b/packages/flutter/lib/cupertino.dart
@@ -8,6 +8,7 @@
 library cupertino;
 
 export 'src/cupertino/activity_indicator.dart';
+export 'src/cupertino/button.dart';
 export 'src/cupertino/dialog.dart';
 export 'src/cupertino/slider.dart';
 export 'src/cupertino/switch.dart';
diff --git a/packages/flutter/lib/src/cupertino/button.dart b/packages/flutter/lib/src/cupertino/button.dart
new file mode 100644
index 0000000..f3094a8
--- /dev/null
+++ b/packages/flutter/lib/src/cupertino/button.dart
@@ -0,0 +1,174 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+
+const TextStyle _kButtonTextStyle = const TextStyle(
+  fontFamily: '.SF UI Text',
+  inherit: false,
+  fontSize: 15.0,
+  fontWeight: FontWeight.normal,
+  color: CupertinoButton.kBlue,
+  textBaseline: TextBaseline.alphabetic,
+);
+
+final TextStyle _kDisabledButtonTextStyle = _kButtonTextStyle.copyWith(
+  color: CupertinoButton.kDisabledForeground,
+);
+
+final TextStyle _kBackgroundButtonTextStyle = _kButtonTextStyle.copyWith(
+  color: CupertinoButton.kWhite,
+);
+
+const EdgeInsets _kButtonPadding = const EdgeInsets.all(16.0);
+const EdgeInsets _kBackgroundButtonPadding =
+const EdgeInsets.symmetric(vertical: 16.0, horizontal: 64.0);
+
+/// An iOS style button.
+///
+/// Takes in a text or an icon that fades out and in on touch. May optionally have a
+/// background.
+///
+/// See also:
+///
+///  * <https://developer.apple.com/ios/human-interface-guidelines/ui-controls/buttons/>
+class CupertinoButton extends StatefulWidget {
+  // TODO(xster): move this to a common Cupertino color palatte with the next yak.
+  static const Color kBlue = const Color(0xFF007AFF);
+  static const Color kWhite = const Color(0xFFFFFFFF);
+  static const Color kDisabledBackground = const Color(0xFFA9A9A9);
+  static const Color kDisabledForeground = const Color(0xFFC4C4C4);
+
+
+  CupertinoButton({
+    @required this.child,
+    this.padding,
+    this.color,
+    @required this.onPressed,
+  });
+
+  /// The widget below this widget in the tree.
+  ///
+  /// Typically a [Text] widget.
+  final Widget child;
+
+  /// The amount of space to surround the child inside the bounds of the button.
+  ///
+  /// Defaults to 16.0 pixels.
+  final EdgeInsets padding;
+
+  /// The color of the button's background.
+  ///
+  /// Defaults to null which produces a button with no background or border.
+  final Color color;
+
+  /// The callback that is called when the button is tapped or otherwise activated.
+  ///
+  /// If this is set to null, the button will be disabled.
+  final VoidCallback onPressed;
+
+  /// Whether the button is enabled or disabled. Buttons are disabled by default. To
+  /// enable a button, set its [onPressed] property to a non-null value.
+  bool get enabled => onPressed != null;
+
+  @override
+  _CupertinoButtonState createState() => new _CupertinoButtonState();
+
+  @override
+  void debugFillDescription(List<String> description) {
+    super.debugFillDescription(description);
+    if (!enabled)
+      description.add('disabled');
+  }
+}
+
+class _CupertinoButtonState extends State<CupertinoButton> with SingleTickerProviderStateMixin {
+  // Eyeballed values. Feel free to tweak.
+  static const Duration kFadeOutDuration = const Duration(milliseconds: 10);
+  static const Duration kFadeInDuration = const Duration(milliseconds: 350);
+
+  AnimationController _animationController;
+
+  @override
+  void initState() {
+    super.initState();
+    _animationController = new AnimationController(
+      duration: const Duration(milliseconds: 200),
+      value: 1.0,
+      vsync: this,
+    );
+  }
+
+  @override
+  void dispose() {
+    _animationController.dispose();
+    _animationController = null;
+    super.dispose();
+  }
+
+  void _handleTapDown(PointerDownEvent event) {
+    _animationController.animateTo(0.1, duration: kFadeOutDuration);
+  }
+
+  void _handleTapUp(PointerUpEvent event) {
+    _animationController.animateTo(1.0, duration: kFadeInDuration);
+  }
+
+  void _handleTapCancel(PointerCancelEvent event) {
+    _animationController.animateTo(1.0, duration: kFadeInDuration);
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final bool enabled = config.enabled;
+    final Color backgroundColor = config.color;
+
+    return new Listener(
+      onPointerDown: enabled ? _handleTapDown : null,
+      onPointerUp: enabled ? _handleTapUp : null,
+      onPointerCancel: enabled ? _handleTapCancel : null,
+
+      child: new GestureDetector(
+        onTap: config.onPressed,
+        child: new ConstrainedBox(
+          constraints: const BoxConstraints(minWidth: 48.0, minHeight: 48.0),
+          child: new FadeTransition(
+            opacity: new CurvedAnimation(
+              parent: _animationController,
+              curve: Curves.decelerate,
+            ),
+            child: new DecoratedBox(
+              decoration: new BoxDecoration(
+                borderRadius: const BorderRadius.all(const Radius.circular(8.0)),
+                backgroundColor: backgroundColor != null && !enabled
+                    ? CupertinoButton.kDisabledBackground
+                    : backgroundColor,
+              ),
+              child: new Padding(
+                padding: config.padding != null
+                    ? config.padding
+                    : backgroundColor != null
+                        ? _kBackgroundButtonPadding
+                        : _kButtonPadding,
+                child: new Center(
+                  widthFactor: 1.0,
+                  heightFactor: 1.0,
+                  child: new DefaultTextStyle(
+                    style: backgroundColor != null
+                        ? _kBackgroundButtonTextStyle
+                        : enabled
+                            ? _kButtonTextStyle
+                            : _kDisabledButtonTextStyle,
+                    child: config.child,
+                  ),
+                ),
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+}
diff --git a/packages/flutter/lib/src/cupertino/dialog.dart b/packages/flutter/lib/src/cupertino/dialog.dart
index 39cb213..4317988 100644
--- a/packages/flutter/lib/src/cupertino/dialog.dart
+++ b/packages/flutter/lib/src/cupertino/dialog.dart
@@ -10,8 +10,8 @@
 const TextStyle _kCupertinoDialogTitleStyle = const TextStyle(
   fontFamily: '.SF UI Display',
   inherit: false,
-  fontSize:  16.0,
-  fontWeight: FontWeight.bold,
+  fontSize:  17.0,
+  fontWeight: FontWeight.w600,
   color: const Color(0xFF000000),
   height: 1.35,
   textBaseline: TextBaseline.alphabetic,
@@ -127,7 +127,7 @@
 
     if (title != null) {
       children.add(new Padding(
-        padding: const EdgeInsets.symmetric(horizontal: 20.0),
+        padding: const EdgeInsets.only(left: 20.0, right: 20.0, bottom: 12.0),
         child: new DefaultTextStyle(
           style: _kCupertinoDialogTitleStyle,
           textAlign: TextAlign.center,
diff --git a/packages/flutter/test/cupertino/button_test.dart b/packages/flutter/test/cupertino/button_test.dart
new file mode 100644
index 0000000..3a4d329
--- /dev/null
+++ b/packages/flutter/test/cupertino/button_test.dart
@@ -0,0 +1,101 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  testWidgets('Layout minimum size', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      new Center(child: new CupertinoButton(child: new Text(' '), onPressed: null))
+    );
+    RenderBox buttonBox = tester.renderObject(find.byType(CupertinoButton));
+    expect(
+      buttonBox.size,
+      greaterThanOrEqualTo(const Size.square(48.0)),
+    );
+    expect(
+      buttonBox.size,
+      lessThan(const Size.square(100.0)),
+    );
+  });
+
+  testWidgets('Size grows with text', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      new Center(child: new CupertinoButton(child: new Text('blah blah blah'), onPressed: null))
+    );
+    RenderBox buttonBox = tester.renderObject(find.byType(CupertinoButton));
+    expect(
+      buttonBox.size.width,
+      greaterThan(48.0),
+    );
+  });
+
+  testWidgets('Button with background is wider', (WidgetTester tester) async {
+    await tester.pumpWidget(new Center(child: new CupertinoButton(
+      child: new Text(' '),
+      onPressed: null,
+      color: new Color(0xFFFFFFFF),
+    )));
+    RenderBox buttonBox = tester.renderObject(find.byType(CupertinoButton));
+    expect(
+      buttonBox.size.width,
+      greaterThan(120.0),
+    );
+  });
+
+  testWidgets('Custom padding', (WidgetTester tester) async {
+    await tester.pumpWidget(new Center(child: new CupertinoButton(
+      child: new Text(' '),
+      onPressed: null,
+      padding: new EdgeInsets.all(100.0),
+    )));
+    RenderBox buttonBox = tester.renderObject(find.byType(CupertinoButton));
+    expect(
+      buttonBox.size,
+      greaterThan(const Size.square(100.0)),
+    );
+  });
+
+  testWidgets('Button takes taps', (WidgetTester tester) async {
+    bool value = false;
+    await tester.pumpWidget(
+      new StatefulBuilder(
+        builder: (BuildContext context, StateSetter setState) {
+          return new Center(
+            child: new CupertinoButton(
+              child: new Text('Tap me'),
+              onPressed: () {
+                setState(() {
+                  value = true;
+                });
+              },
+            ),
+          );
+        },
+      ),
+    );
+
+    expect(value, isFalse);
+    // No animating by default.
+    expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
+    await tester.tap(find.byType(CupertinoButton));
+    expect(value, isTrue);
+    // Animates.
+    expect(SchedulerBinding.instance.transientCallbackCount, equals(1));
+  });
+
+  testWidgets('Disabled button doesn\'t animate', (WidgetTester tester) async {
+    await tester.pumpWidget(new Center(child: new CupertinoButton(
+      child: new Text('Tap me'),
+      onPressed: null,
+    )));
+    expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
+    await tester.tap(find.byType(CupertinoButton));
+    // Still doesn't animate.
+    expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
+  });
+}