From 4264d5e7494fda8bcaff7ecb717f726a20f0f607 Mon Sep 17 00:00:00 2001 From: Hans Muller Date: Mon, 8 Feb 2016 16:47:55 -0800 Subject: [PATCH] Added button dialog and snackbar demos --- .../lib/demo/buttons_demo.dart | 211 ++++++++++++ .../lib/demo/dialog_demo.dart | 37 +++ .../lib/demo/snack_bar_demo.dart | 73 +++++ .../material_gallery/lib/demo/tabs_demo.dart | 2 +- .../lib/demo/tabs_fab_demo.dart | 12 +- .../material_gallery/lib/gallery/app.dart | 35 ++ .../material_gallery/lib/gallery/demo.dart | 17 + .../material_gallery/lib/gallery/drawer.dart | 78 +++++ .../material_gallery/lib/gallery/home.dart | 129 ++++++++ .../material_gallery/lib/gallery/section.dart | 105 ++++++ examples/material_gallery/lib/main.dart | 307 +----------------- .../lib/src/material/material_button.dart | 3 +- packages/flutter/lib/src/material/tabs.dart | 7 + 13 files changed, 703 insertions(+), 313 deletions(-) create mode 100644 examples/material_gallery/lib/demo/buttons_demo.dart create mode 100644 examples/material_gallery/lib/demo/dialog_demo.dart create mode 100644 examples/material_gallery/lib/demo/snack_bar_demo.dart create mode 100644 examples/material_gallery/lib/gallery/app.dart create mode 100644 examples/material_gallery/lib/gallery/demo.dart create mode 100644 examples/material_gallery/lib/gallery/drawer.dart create mode 100644 examples/material_gallery/lib/gallery/home.dart create mode 100644 examples/material_gallery/lib/gallery/section.dart diff --git a/examples/material_gallery/lib/demo/buttons_demo.dart b/examples/material_gallery/lib/demo/buttons_demo.dart new file mode 100644 index 00000000000..610ad3d7b7c --- /dev/null +++ b/examples/material_gallery/lib/demo/buttons_demo.dart @@ -0,0 +1,211 @@ +// 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/material.dart'; +import 'package:flutter/widgets.dart'; + +import 'tabs_fab_demo.dart'; +import 'dialog_demo.dart'; +import 'snack_bar_demo.dart'; + +const String _floatingText = + "A floating action button is a circular material button that lifts " + "and displays an ink reaction on press. It turns and fades in when " + "it changes."; + +const String _raisedText = + "A raised button is typically a rectangular material button that lifts " + "and displays ink reactions on press. Raised buttons add dimension to " + "mostly flat layouts. They emphasize functions on busy or wide spaces."; + +const String _flatText = + "A flat button is made of ink that displays ink reactions on press " + "but does not lift. Use flat buttons on toolbars, in dialogs and " + "inline with padding"; + +const String _dropdownText = + "A dropdown button selects between multiple selections. The button " + "displays the current state and a down arrow."; + +class _ButtonDemo { + _ButtonDemo({ this.title, this.text, this.builder }) { + assert(title != null); + assert(text != null); + assert(builder != null); + } + + final String title; + final String text; + final WidgetBuilder builder; + + TabLabel get tabLabel => new TabLabel(text: title.toUpperCase()); + + // The TabBarSelection created below saves and restores _ButtonDemo objects + // to recover this demo's selected tab. To enable it to compare restored + // _ButtonDemo objects with new ones, define hashCode and operator== . + + bool operator==(Object other) { + if (other.runtimeType != runtimeType) + return false; + _ButtonDemo typedOther = other; + return typedOther.title == title && typedOther.text == text; + } + + int get hashCode => hashValues(title.hashCode, text.hashCode); +} + +class ButtonsDemo extends StatefulComponent { + _ButtonsDemoState createState() => new _ButtonsDemoState(); +} + +class _ButtonsDemoState extends State { + List<_ButtonDemo> demos; + + void initState() { + super.initState(); + demos = <_ButtonDemo>[ + new _ButtonDemo(title: 'FLOATING', text: _floatingText, builder: buildFloatingButton), + new _ButtonDemo(title: 'RAISED', text: _raisedText, builder: buildRaisedButton), + new _ButtonDemo(title: 'FLAT', text: _flatText, builder: buildFlatButton), + new _ButtonDemo(title: 'DROPDOWN', text: _dropdownText, builder: buildDropdownButton) + ]; + } + + Widget buildFloatingButton(BuildContext context) { + return new SizedBox( + height: 128.0, + child: new Center( + child: new FloatingActionButton( + child: new Icon(icon: 'content/add'), + onPressed: () { + Navigator.push(context, new MaterialPageRoute(builder: (_) => new TabsFabDemo())); + } + ) + ) + ); + } + + Widget buildRaisedButton(BuildContext context) { + return new Container( + margin: const EdgeDims.symmetric(vertical: 16.0), + child: new Column( + children: [ + new RaisedButton( + child: new Text("Launch Demo"), + onPressed: () { + Navigator.push(context, new MaterialPageRoute(builder: (_) => new SnackBarDemo())); + } + ), + new RaisedButton( + child: new Text("Disabled") + ) + ] + .map((Widget child) { + return new Container( + margin: const EdgeDims.symmetric(vertical: 8.0), + child: child + ); + }) + .toList() + ) + ); + } + + Widget buildFlatButton(BuildContext context) { + return new Container( + margin: const EdgeDims.symmetric(vertical: 16.0), + child: new ButtonTheme( + color: ButtonColor.accent, + child: new Column( + children: [ + new FlatButton( + child: new Text("Launch Demo"), + onPressed: () { + showDialog(context: context, child: new DialogDemo()).then((String value) { + if (value != null) { + Scaffold.of(context).showSnackBar(new SnackBar( + content: new Text('You dismissed the dialog with "$value"') + )); + } + }); + } + ), + new FlatButton( + child: new Text("Disabled") + ) + ] + .map((Widget child) { + return new Container( + margin: const EdgeDims.symmetric(vertical: 8.0), + child: child + ); + }) + .toList() + ) + ) + ); + } + + String dropdownValue = "Free"; + + Widget buildDropdownButton(BuildContext context) { + return new SizedBox( + height: 256.0, + child: new Center( + child: new DropDownButton( + value: dropdownValue, + onChanged: (String newValue) { + setState(() { + if (newValue != null) + dropdownValue = newValue; + }); + }, + items: ["One", "Two", "Free", "Four"] + .map((String value) { + return new DropDownMenuItem( + value: value, + child: new Text(value)); + }) + .toList() + ) + ) + ); + } + + Widget buildTabView(_ButtonDemo demo) { + return new Builder( + builder: (BuildContext context) { + final TextStyle textStyle = Theme.of(context).text.caption.copyWith(fontSize: 16.0); + return new Column( + alignItems: FlexAlignItems.stretch, + children: [ + demo.builder(context), + new Padding( + padding: const EdgeDims.symmetric(horizontal: 32.0), + child: new Text(demo.text, style: textStyle) + ) + ] + ); + } + ); + } + + Widget build(BuildContext context) { + return new TabBarSelection<_ButtonDemo>( + values: demos, + child: new Scaffold( + toolBar: new ToolBar( + center: new Text("Buttons"), + tabBar: new TabBar<_ButtonDemo>( + isScrollable: true, + labels: new Map.fromIterable(demos, value: (_ButtonDemo demo) => demo.tabLabel) + ) + ), + body: new TabBarView( + children: demos.map(buildTabView).toList() + ) + ) + ); + } +} diff --git a/examples/material_gallery/lib/demo/dialog_demo.dart b/examples/material_gallery/lib/demo/dialog_demo.dart new file mode 100644 index 00000000000..d92186efb43 --- /dev/null +++ b/examples/material_gallery/lib/demo/dialog_demo.dart @@ -0,0 +1,37 @@ +// 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/material.dart'; +import 'package:flutter/widgets.dart'; + +const String _dialogText = + "Use dialogs sparingly because they are interruptive. Their sudden appearance " + "forces users to stop their current task and focus on the dialog content. " + "Alternatives to dialogs include menus or inline expansion, both of which " + "maintain the current context."; + +class DialogDemo extends StatelessComponent { + DialogDemo({ Key key }) : super(key: key); + + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + return new Dialog( + title: new Text("This is a Dialog"), + content: new Text( + _dialogText, + style: theme.text.subhead.copyWith(color: theme.text.caption.color) + ), + actions: [ + new FlatButton( + child: new Text("CANCEL"), + onPressed: () { Navigator.pop(context, "CANCEL"); } + ), + new FlatButton( + child: new Text("OK"), + onPressed: () { Navigator.pop(context, "OK"); } + ) + ] + ); + } +} diff --git a/examples/material_gallery/lib/demo/snack_bar_demo.dart b/examples/material_gallery/lib/demo/snack_bar_demo.dart new file mode 100644 index 00000000000..de0a9a02457 --- /dev/null +++ b/examples/material_gallery/lib/demo/snack_bar_demo.dart @@ -0,0 +1,73 @@ +// 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/material.dart'; +import 'package:flutter/widgets.dart'; + +const String _text1 = + "Snackbars provide lightweight feedback about an operation by " + "showing a brief message at the bottom of the screen. Snackbars " + "can contain an action."; + +const String _text2 = + "Snackbars should contain a single line of text directly related " + "to the operation performed. They cannot contain icons."; + +const String _text3 = + "By default snackbars automatically disappear after a few seconds "; + +class SnackBarDemo extends StatelessComponent { + SnackBarDemo({ Key key }) : super(key: key); + + Widget buildBody(BuildContext context) { + return new Padding( + padding: const EdgeDims.all(24.0), + child: new Column( + children: [ + new Text(_text1), + new Text(_text2), + new RaisedButton( + child: new Text('Show a SnackBar'), + onPressed: () { + Scaffold.of(context).showSnackBar(new SnackBar( + content: new Text('This is a SnackBar'), + actions: [ + new SnackBarAction( + label: 'Action', + onPressed: () { + Scaffold.of(context).showSnackBar(new SnackBar( + content: new Text("You pressed the SnackBar's Action") + )); + } + ) + ] + )); + } + ), + new Text(_text3), + ] + .map((Widget child) { + return new Container( + margin: const EdgeDims.symmetric(vertical: 12.0), + child: child + ); + }) + .toList() + ) + ); + } + + Widget build(BuildContext context) { + return new Scaffold( + toolBar: new ToolBar( + center: new Text('SnackBar') + ), + body: new Builder( + // Create an inner BuildContext so that the snackBar onPressed methods + // can refer to the Scaffold with Scaffold.of(). + builder: buildBody + ) + ); + } +} diff --git a/examples/material_gallery/lib/demo/tabs_demo.dart b/examples/material_gallery/lib/demo/tabs_demo.dart index 4d474fecf26..c26c5d522d8 100644 --- a/examples/material_gallery/lib/demo/tabs_demo.dart +++ b/examples/material_gallery/lib/demo/tabs_demo.dart @@ -17,7 +17,7 @@ class TabsDemo extends StatelessComponent { isScrollable: true, labels: new Map.fromIterable( iconNames, - value: (String iconName) => new TabLabel(text: iconName, icon: "action/$iconName") + value: (String iconName) => new TabLabel(text: iconName.toUpperCase(), icon: "action/$iconName") ) ) ), diff --git a/examples/material_gallery/lib/demo/tabs_fab_demo.dart b/examples/material_gallery/lib/demo/tabs_fab_demo.dart index dc125be2972..6fba81268d0 100644 --- a/examples/material_gallery/lib/demo/tabs_fab_demo.dart +++ b/examples/material_gallery/lib/demo/tabs_fab_demo.dart @@ -11,7 +11,7 @@ class _Page { final Map colors; final String icon; - TabLabel get tabLabel => new TabLabel(text: label); + TabLabel get tabLabel => new TabLabel(text: label.toUpperCase()); Color get labelColor => colors != null ? colors[300] : Colors.grey[300]; bool get fabDefined => colors != null && icon != null; Color get fabColor => colors[400]; @@ -31,7 +31,7 @@ class TabsFabDemo extends StatefulComponent { class _TabsFabDemoState extends State { final GlobalKey scaffoldKey = new GlobalKey(); - final List<_Page> _pages = <_Page>[ + final List<_Page> pages = <_Page>[ new _Page(label: 'Blue', colors: Colors.indigo, icon: 'content/add'), new _Page(label: 'Too', colors: Colors.indigo, icon: 'content/add'), new _Page(label: 'Eco', colors: Colors.green, icon: 'content/create'), @@ -43,7 +43,7 @@ class _TabsFabDemoState extends State { void initState() { super.initState(); - selectedPage = _pages[0]; + selectedPage = pages[0]; } void _handleTabSelection(_Page page) { @@ -90,14 +90,14 @@ class _TabsFabDemoState extends State { Widget build(BuildContext context) { return new TabBarSelection<_Page>( - values: _pages, + values: pages, onChanged: _handleTabSelection, child: new Scaffold( key: scaffoldKey, toolBar: new ToolBar( center: new Text("FAB per Tab"), tabBar: new TabBar<_Page>( - labels: new Map.fromIterable(_pages, value: (_Page page) => page.tabLabel) + labels: new Map.fromIterable(pages, value: (_Page page) => page.tabLabel) ) ), floatingActionButton: !selectedPage.fabDefined ? null : new FloatingActionButton( @@ -106,7 +106,7 @@ class _TabsFabDemoState extends State { child: selectedPage.fabIcon, onPressed: _showExplanatoryText ), - body: new TabBarView(children: _pages.map(buildTabView).toList()) + body: new TabBarView(children: pages.map(buildTabView).toList()) ) ); } diff --git a/examples/material_gallery/lib/gallery/app.dart b/examples/material_gallery/lib/gallery/app.dart new file mode 100644 index 00000000000..1b05d4dd3a7 --- /dev/null +++ b/examples/material_gallery/lib/gallery/app.dart @@ -0,0 +1,35 @@ +// 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 'home.dart'; + +class GalleryApp extends StatefulComponent { + GalleryApp({ Key key }) : super(key: key); + + static GalleryAppState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher()); + + GalleryAppState createState() => new GalleryAppState(); +} + +class GalleryAppState extends State { + bool _lightTheme = true; + bool get lightTheme => _lightTheme; + void set lightTheme(bool value) { + setState(() { + _lightTheme = value; + }); + } + + Widget build(BuildContext context) { + return new MaterialApp( + title: 'Flutter Material Gallery', + theme: lightTheme ? new ThemeData.light() : new ThemeData.dark(), + routes: { + '/': (RouteArguments args) => new GalleryHome() + } + ); + } +} diff --git a/examples/material_gallery/lib/gallery/demo.dart b/examples/material_gallery/lib/gallery/demo.dart new file mode 100644 index 00000000000..0416bab8892 --- /dev/null +++ b/examples/material_gallery/lib/gallery/demo.dart @@ -0,0 +1,17 @@ +// 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/widgets.dart'; + +typedef Widget GalleryDemoBuilder(); + +class GalleryDemo { + GalleryDemo({ this.title, this.builder }) { + assert(title != null); + assert(builder != null); + } + + final String title; + final GalleryDemoBuilder builder; +} diff --git a/examples/material_gallery/lib/gallery/drawer.dart b/examples/material_gallery/lib/gallery/drawer.dart new file mode 100644 index 00000000000..6836cfc3d48 --- /dev/null +++ b/examples/material_gallery/lib/gallery/drawer.dart @@ -0,0 +1,78 @@ +// 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/material.dart'; +import 'package:flutter/scheduler.dart' show timeDilation; +import 'package:flutter/widgets.dart'; + +import 'app.dart'; + +class GalleryDrawer extends StatelessComponent { + GalleryDrawer({ Key key }) : super(key: key); + + void _changeTheme(BuildContext context, bool value) { + GalleryApp.of(context).lightTheme = value; + } + + void _toggleAnimationSpeed(BuildContext context) { + GalleryApp.of(context).setState(() { + timeDilation = (timeDilation != 1.0) ? 1.0 : 5.0; + }); + } + + Widget build(BuildContext context) { + return new Drawer( + child: new Block( + children: [ + new DrawerHeader(child: new Text('Flutter Gallery')), + new DrawerItem( + icon: 'image/brightness_5', + onPressed: () { _changeTheme(context, true); }, + selected: GalleryApp.of(context).lightTheme, + child: new Row( + children: [ + new Flexible(child: new Text('Light')), + new Radio( + value: true, + groupValue: GalleryApp.of(context).lightTheme, + onChanged: (bool value) { _changeTheme(context, value); } + ) + ] + ) + ), + new DrawerItem( + icon: 'image/brightness_7', + onPressed: () { _changeTheme(context, false); }, + selected: !GalleryApp.of(context).lightTheme, + child: new Row( + children: [ + new Flexible(child: new Text('Dark')), + new Radio( + value: false, + groupValue: GalleryApp.of(context).lightTheme, + onChanged: (bool value) { _changeTheme(context, value); } + ) + ] + ) + ), + new DrawerDivider(), + new DrawerItem( + icon: 'action/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); } + ) + ] + ) + ) + ] + ) + ); + } +} diff --git a/examples/material_gallery/lib/gallery/home.dart b/examples/material_gallery/lib/gallery/home.dart new file mode 100644 index 00000000000..381eaa2d378 --- /dev/null +++ b/examples/material_gallery/lib/gallery/home.dart @@ -0,0 +1,129 @@ +// 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/material.dart'; +import 'package:flutter/widgets.dart'; + +import 'demo.dart'; +import 'drawer.dart'; +import 'section.dart'; + +import '../demo/buttons_demo.dart'; +import '../demo/chip_demo.dart'; +import '../demo/date_picker_demo.dart'; +import '../demo/dialog_demo.dart'; +import '../demo/drop_down_demo.dart'; +import '../demo/modal_bottom_sheet_demo.dart'; +import '../demo/page_selector_demo.dart'; +import '../demo/persistent_bottom_sheet_demo.dart'; +import '../demo/progress_indicator_demo.dart'; +import '../demo/toggle_controls_demo.dart'; +import '../demo/scrolling_techniques_demo.dart'; +import '../demo/slider_demo.dart'; +import '../demo/snack_bar_demo.dart'; +import '../demo/tabs_demo.dart'; +import '../demo/tabs_fab_demo.dart'; +import '../demo/time_picker_demo.dart'; +import '../demo/two_level_list_demo.dart'; +import '../demo/weathers_demo.dart'; + +class GalleryHome extends StatefulComponent { + GalleryHome({ Key key }) : super(key: key); + + GalleryHomeState createState() => new GalleryHomeState(); +} + +class GalleryHomeState extends State { + Widget build(BuildContext context) { + return new Scaffold( + appBarHeight: 128.0, + drawer: new GalleryDrawer(), + toolBar: new ToolBar( + flexibleSpace: (BuildContext context) { + return new Container( + padding: const EdgeDims.only(left: 16.0, bottom: 24.0), + height: 128.0, + child: new Align( + alignment: const FractionalOffset(0.0, 1.0), + child: new Text('Flutter Gallery', style: Typography.white.headline) + ) + ); + } + ), + body: new Padding( + padding: const EdgeDims.all(4.0), + child: new Block( + children: [ + new Row( + children: [ + new GallerySection( + title: 'Animation', + image: 'assets/section_animation.png', + colors: Colors.purple, + demos: [ + new GalleryDemo(title: 'Weathers', builder: () => new WeathersDemo()) + ] + ), + new GallerySection( + title: 'Style', + image: 'assets/section_style.png', + colors: Colors.green + ) + ] + ), + new Row( + children: [ + new GallerySection( + title: 'Layout', + image: 'assets/section_layout.png', + colors: Colors.pink + ), + new GallerySection( + title: 'Components', + image: 'assets/section_components.png', + colors: Colors.amber, + demos: [ + new GalleryDemo(title: 'Buttons', builder: () => new ButtonsDemo()), + new GalleryDemo(title: 'Chips', builder: () => new ChipDemo()), + new GalleryDemo(title: 'Date Picker', builder: () => new DatePickerDemo()), + new GalleryDemo(title: 'Dialog', builder: () => new DialogDemo()), + new GalleryDemo(title: 'Dropdown Button', builder: () => new DropDownDemo()), + new GalleryDemo(title: 'Expland/Collapse List Control', builder: () => new TwoLevelListDemo()), + new GalleryDemo(title: 'Floating Action Button', builder: () => new TabsFabDemo()), + new GalleryDemo(title: 'Modal Bottom Sheet', builder: () => new ModalBottomSheetDemo()), + new GalleryDemo(title: 'Page Selector', builder: () => new PageSelectorDemo()), + new GalleryDemo(title: 'Persistent Bottom Sheet', builder: () => new PersistentBottomSheetDemo()), + new GalleryDemo(title: 'Progress Indicators', builder: () => new ProgressIndicatorDemo()), + new GalleryDemo(title: 'Selection Controls', builder: () => new ToggleControlsDemo()), + new GalleryDemo(title: 'Sliders', builder: () => new SliderDemo()), + new GalleryDemo(title: 'SnackBar', builder: () => new SnackBarDemo()), + new GalleryDemo(title: 'Tabs', builder: () => new TabsDemo()), + new GalleryDemo(title: 'Time Picker', builder: () => new TimePickerDemo()) + ] + ) + ] + ), + new Row( + children: [ + new GallerySection( + title: 'Patterns', + image: 'assets/section_patterns.png', + colors: Colors.cyan, + demos: [ + new GalleryDemo(title: 'Scrolling Techniques', builder: () => new ScrollingTechniquesDemo()) + ] + ), + new GallerySection( + title: 'Usability', + image: 'assets/section_usability.png', + colors: Colors.lightGreen + ) + ] + ) + ] + ) + ) + ); + } +} diff --git a/examples/material_gallery/lib/gallery/section.dart b/examples/material_gallery/lib/gallery/section.dart new file mode 100644 index 00000000000..c4d63b62445 --- /dev/null +++ b/examples/material_gallery/lib/gallery/section.dart @@ -0,0 +1,105 @@ +// 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/material.dart'; +import 'package:flutter/widgets.dart'; + +import 'demo.dart'; + +class GallerySection extends StatelessComponent { + GallerySection({ this.title, this.image, this.colors, this.demos }); + + final String title; + final String image; + final Map colors; + final List demos; + + void showDemo(GalleryDemo demo, BuildContext context, ThemeData theme) { + Navigator.push(context, new MaterialPageRoute( + builder: (BuildContext context) { + Widget child = (demo.builder == null) ? null : demo.builder(); + return new Theme(data: theme, child: child); + } + )); + } + + void showDemos(BuildContext context) { + final ThemeData theme = new ThemeData( + brightness: Theme.of(context).brightness, + primarySwatch: colors + ); + final double appBarHeight = 200.0; + final Key scrollableKey = new ValueKey(title); // assume section titles differ + Navigator.push(context, new MaterialPageRoute( + builder: (BuildContext context) { + return new Theme( + data: theme, + child: new Scaffold( + appBarHeight: appBarHeight, + appBarBehavior: AppBarBehavior.scroll, + scrollableKey: scrollableKey, + toolBar: new ToolBar( + flexibleSpace: (BuildContext context) => new FlexibleSpaceBar(title: new Text(title)) + ), + body: new Material( + child: new MaterialList( + scrollableKey: scrollableKey, + scrollablePadding: new EdgeDims.only(top: appBarHeight), + type: MaterialListType.oneLine, + children: (demos ?? const []).map((GalleryDemo demo) { + return new ListItem( + center: new Text(demo.title, style: theme.text.subhead), + onTap: () { showDemo(demo, context, theme); } + ); + }) + ) + ) + ) + ); + } + )); + } + + Widget build (BuildContext context) { + final ThemeData theme = new ThemeData( + brightness: Theme.of(context).brightness, + primarySwatch: colors + ); + final TextStyle titleTextStyle = theme.text.title.copyWith( + color: theme.brightness == ThemeBrightness.dark ? Colors.black : Colors.white + ); + return new Flexible( + child: new GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { showDemos(context); }, + child: new Container( + height: 256.0, + margin: const EdgeDims.all(4.0), + decoration: new BoxDecoration(backgroundColor: theme.primaryColor), + child: new Column( + children: [ + new Flexible( + child: new Padding( + padding: const EdgeDims.symmetric(horizontal: 12.0), + child: new AssetImage( + name: image, + alignment: const FractionalOffset(0.5, 0.5), + fit: ImageFit.contain + ) + ) + ), + new Padding( + padding: const EdgeDims.all(16.0), + child: new Align( + alignment: const FractionalOffset(0.0, 1.0), + child: new Text(title, style: titleTextStyle) + ) + ) + ] + ) + ) + ) + ); + } +} diff --git a/examples/material_gallery/lib/main.dart b/examples/material_gallery/lib/main.dart index bd69b1b94d9..92f08efb71e 100644 --- a/examples/material_gallery/lib/main.dart +++ b/examples/material_gallery/lib/main.dart @@ -2,312 +2,9 @@ // 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; +import 'package:flutter/widgets.dart'; -import 'demo/chip_demo.dart'; -import 'demo/date_picker_demo.dart'; -import 'demo/drop_down_demo.dart'; -import 'demo/modal_bottom_sheet_demo.dart'; -import 'demo/page_selector_demo.dart'; -import 'demo/persistent_bottom_sheet_demo.dart'; -import 'demo/progress_indicator_demo.dart'; -import 'demo/toggle_controls_demo.dart'; -import 'demo/scrolling_techniques_demo.dart'; -import 'demo/slider_demo.dart'; -import 'demo/tabs_demo.dart'; -import 'demo/tabs_fab_demo.dart'; -import 'demo/time_picker_demo.dart'; -import 'demo/two_level_list_demo.dart'; -import 'demo/weathers_demo.dart'; - -typedef Widget GalleryDemoBuilder(); - -class GalleryDemo { - GalleryDemo({ this.title, this.builder }); - - final String title; - final GalleryDemoBuilder builder; -} - -class GallerySection extends StatelessComponent { - GallerySection({ this.title, this.image, this.colors, this.demos }); - - final String title; - final String image; - final Map colors; - final List demos; - - void showDemo(GalleryDemo demo, BuildContext context, ThemeData theme) { - Navigator.push(context, new MaterialPageRoute( - builder: (BuildContext context) { - Widget child = (demo.builder == null) ? null : demo.builder(); - return new Theme(data: theme, child: child); - } - )); - } - - void showDemos(BuildContext context) { - final theme = new ThemeData( - brightness: Theme.of(context).brightness, - primarySwatch: colors - ); - final appBarHeight = 200.0; - final scrollableKey = new ValueKey(title); // assume section titles differ - Navigator.push(context, new MaterialPageRoute( - builder: (BuildContext context) { - return new Theme( - data: theme, - child: new Scaffold( - appBarHeight: appBarHeight, - appBarBehavior: AppBarBehavior.scroll, - scrollableKey: scrollableKey, - toolBar: new ToolBar( - flexibleSpace: (BuildContext context) => new FlexibleSpaceBar(title: new Text(title)) - ), - body: new Material( - child: new MaterialList( - scrollableKey: scrollableKey, - scrollablePadding: new EdgeDims.only(top: appBarHeight), - type: MaterialListType.oneLine, - children: (demos ?? []).map((GalleryDemo demo) { - return new ListItem( - center: new Text(demo.title, style: theme.text.subhead), - onTap: () { showDemo(demo, context, theme); } - ); - }) - ) - ) - ) - ); - } - )); - } - - Widget build (BuildContext context) { - final theme = new ThemeData( - brightness: Theme.of(context).brightness, - primarySwatch: colors - ); - final titleTextStyle = theme.text.title.copyWith( - color: theme.brightness == ThemeBrightness.dark ? Colors.black : Colors.white - ); - return new Flexible( - child: new GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { showDemos(context); }, - child: new Container( - height: 256.0, - margin: const EdgeDims.all(4.0), - decoration: new BoxDecoration(backgroundColor: theme.primaryColor), - child: new Column( - children: [ - new Flexible( - child: new Padding( - padding: const EdgeDims.symmetric(horizontal: 12.0), - child: new AssetImage( - name: image, - alignment: const FractionalOffset(0.5, 0.5), - fit: ImageFit.contain - ) - ) - ), - new Padding( - padding: const EdgeDims.all(16.0), - child: new Align( - alignment: const FractionalOffset(0.0, 1.0), - child: new Text(title, style: titleTextStyle) - ) - ) - ] - ) - ) - ) - ); - } -} - -class GalleryHome extends StatefulComponent { - GalleryHomeState createState() => new GalleryHomeState(); -} - -class GalleryHomeState extends State { - void _changeTheme(BuildContext context, bool value) { - GalleryApp.of(context).lightTheme = value; - } - - void _toggleAnimationSpeed() { - setState((){ - timeDilation = (timeDilation != 1.0) ? 1.0 : 5.0; - }); - } - - Widget build(BuildContext context) { - return new Scaffold( - appBarHeight: 128.0, - toolBar: new ToolBar( - flexibleSpace: (BuildContext context) { - return new Container( - padding: const EdgeDims.only(left: 16.0, bottom: 24.0), - height: 128.0, - child: new Align( - alignment: const FractionalOffset(0.0, 1.0), - child: new Text('Flutter Gallery', style: Typography.white.headline) - ) - ); - } - ), - drawer: new Drawer( - child: new Block( - children: [ - new DrawerHeader(child: new Text('Flutter Gallery')), - new DrawerItem( - icon: 'image/brightness_5', - onPressed: () { _changeTheme(context, true); }, - selected: GalleryApp.of(context).lightTheme, - child: new Row( - children: [ - new Flexible(child: new Text('Light')), - new Radio( - value: true, - groupValue: GalleryApp.of(context).lightTheme, - onChanged: (bool value) { _changeTheme(context, value); } - ) - ] - ) - ), - new DrawerItem( - icon: 'image/brightness_7', - onPressed: () { _changeTheme(context, false); }, - selected: !GalleryApp.of(context).lightTheme, - child: new Row( - children: [ - new Flexible(child: new Text('Dark')), - new Radio( - value: false, - groupValue: GalleryApp.of(context).lightTheme, - onChanged: (bool value) { _changeTheme(context, value); } - ) - ] - ) - ), - new DrawerDivider(), - new DrawerItem( - icon: 'action/hourglass_empty', - selected: timeDilation != 1.0, - onPressed: () { _toggleAnimationSpeed(); }, - child: new Row( - children: [ - new Flexible(child: new Text('Animate Slowly')), - new Checkbox( - value: timeDilation != 1.0, - onChanged: (bool value) { _toggleAnimationSpeed(); } - ) - ] - ) - ) - ] - ) - ), - body: new Padding( - padding: const EdgeDims.all(4.0), - child: new Block( - children: [ - new Row( - children: [ - new GallerySection( - title: 'Animation', - image: 'assets/section_animation.png', - colors: Colors.purple, - demos: [ - new GalleryDemo(title: 'Weathers', builder: () => new WeathersDemo()) - ] - ), - new GallerySection( - title: 'Style', - image: 'assets/section_style.png', - colors: Colors.green - ) - ] - ), - new Row( - children: [ - new GallerySection( - title: 'Layout', - image: 'assets/section_layout.png', - colors: Colors.pink - ), - new GallerySection( - title: 'Components', - image: 'assets/section_components.png', - colors: Colors.amber, - demos: [ - new GalleryDemo(title: 'Chips', builder: () => new ChipDemo()), - new GalleryDemo(title: 'Date Picker', builder: () => new DatePickerDemo()), - new GalleryDemo(title: 'Dropdown Button', builder: () => new DropDownDemo()), - new GalleryDemo(title: 'Expland/Collapse List Control', builder: () => new TwoLevelListDemo()), - new GalleryDemo(title: 'Floating Action Button', builder: () => new TabsFabDemo()), - new GalleryDemo(title: 'Modal Bottom Sheet', builder: () => new ModalBottomSheetDemo()), - new GalleryDemo(title: 'Page Selector', builder: () => new PageSelectorDemo()), - new GalleryDemo(title: 'Persistent Bottom Sheet', builder: () => new PersistentBottomSheetDemo()), - new GalleryDemo(title: 'Progress Indicators', builder: () => new ProgressIndicatorDemo()), - new GalleryDemo(title: 'Selection Controls', builder: () => new ToggleControlsDemo()), - new GalleryDemo(title: 'Sliders', builder: () => new SliderDemo()), - new GalleryDemo(title: 'Tabs', builder: () => new TabsDemo()), - new GalleryDemo(title: 'Time Picker', builder: () => new TimePickerDemo()) - ] - ) - ] - ), - new Row( - children: [ - new GallerySection( - title: 'Patterns', - image: 'assets/section_patterns.png', - colors: Colors.cyan, - demos: [ - new GalleryDemo(title: 'Scrolling Techniques', builder: () => new ScrollingTechniquesDemo()) - ] - ), - new GallerySection( - title: 'Usability', - image: 'assets/section_usability.png', - colors: Colors.lightGreen - ) - ] - ) - ] - ) - ) - ); - } -} - -class GalleryApp extends StatefulComponent { - static GalleryAppState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher()); - - GalleryAppState createState() => new GalleryAppState(); -} - -class GalleryAppState extends State { - bool _lightTheme = true; - bool get lightTheme => _lightTheme; - void set lightTheme(bool value) { - setState(() { - _lightTheme = value; - }); - } - - Widget build(BuildContext context) { - return new MaterialApp( - title: 'Flutter Material Gallery', - theme: lightTheme ? new ThemeData.light() : new ThemeData.dark(), - routes: { - '/': (RouteArguments args) => new GalleryHome() - } - ); - } -} +import 'gallery/app.dart'; void main() { runApp(new GalleryApp()); diff --git a/packages/flutter/lib/src/material/material_button.dart b/packages/flutter/lib/src/material/material_button.dart index 9ae0c19e62e..6c03af19f61 100644 --- a/packages/flutter/lib/src/material/material_button.dart +++ b/packages/flutter/lib/src/material/material_button.dart @@ -134,7 +134,8 @@ abstract class MaterialButtonState extends State { return new Container( height: 36.0, constraints: new BoxConstraints(minWidth: 88.0), - margin: new EdgeDims.all(8.0), + padding: const EdgeDims.symmetric(horizontal: 8.0), + margin: const EdgeDims.all(8.0), child: contents ); } diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart index 6e7269af02a..af9e669b3e8 100644 --- a/packages/flutter/lib/src/material/tabs.dart +++ b/packages/flutter/lib/src/material/tabs.dart @@ -436,6 +436,12 @@ class TabBarSelectionState extends State> { void initState() { super.initState(); _value = config.value ?? PageStorage.of(context)?.readState(context) ?? values.first; + + // If the selection's values have changed since the selected value was saved with + // PageStorage.writeState() then use the default. + if (!values.contains(_value)) + _value = values.first; + _previousValue = _value; _initValueToIndex(); } @@ -870,6 +876,7 @@ class _TabBarViewState extends PageableListState implements TabBarSe void _updateItemsAndScrollBehavior() { assert(_selection != null); final int selectedIndex = _selection.index; + assert(selectedIndex != null); _updateItemsForSelectedIndex(selectedIndex); _updateScrollBehaviorForSelectedIndex(selectedIndex); }