From e502e9c8f82f11b186669269e87dd9228a4a128c Mon Sep 17 00:00:00 2001 From: Ian Hickson Date: Mon, 20 Jun 2016 21:04:45 -0700 Subject: [PATCH] ImageIcon (#4649) Anywhere that accepted IconData now accepts either an Icon or an ImageIcon. Places that used to take an IconData in an `icon` argument, notably IconButton and DrawerItem, now take a Widget in that slot. You can wrap the value that used to be passed in in an Icon constructor to get the same result. Icon itself now takes the icon as a positional argument, for brevity. ThemeData now has an iconTheme as well as a primaryIconTheme, the same way it has had a textTheme and primaryTextTheme for a while. IconTheme.of() always returns a value now (though that value itself may have nulls in it). It defaults to the ThemeData.iconTheme. IconThemeData.fallback() is a new method that returns an icon theme data structure with all fields filled in. IconTheme.merge() is a new constructor that takes a context and creates a widget that mixes in the new values with the inherited values. Most places that introduced an IconTheme widget now use IconTheme.merge. IconThemeData.merge and IconThemeData.copyWith act in a way analogous to the similarly-named members of TextStyle. ImageIcon is introduced. It acts like Icon but takes an ImageProvider instead of an IconData. Also: Fix the analyzer to actually check the stocks app. --- dev/benchmarks/complex_layout/lib/main.dart | 35 ++++--- dev/manual_tests/card_collection.dart | 12 +-- dev/manual_tests/fitness_demo.dart | 2 +- dev/manual_tests/pageable_list.dart | 4 +- .../lib/demo/buttons_demo.dart | 6 +- .../lib/demo/contacts_demo.dart | 6 +- .../flutter_gallery/lib/demo/dialog_demo.dart | 2 +- .../lib/demo/full_screen_dialog_demo.dart | 6 +- .../lib/demo/grid_list_demo.dart | 4 +- .../flutter_gallery/lib/demo/icons_demo.dart | 6 +- .../lib/demo/leave_behind_demo.dart | 4 +- .../flutter_gallery/lib/demo/list_demo.dart | 6 +- .../flutter_gallery/lib/demo/menu_demo.dart | 8 +- .../lib/demo/overscroll_demo.dart | 4 +- .../lib/demo/page_selector_demo.dart | 6 +- .../demo/persistent_bottom_sheet_demo.dart | 2 +- .../flutter_gallery/lib/demo/pesto_demo.dart | 10 +- .../lib/demo/scrollable_tabs_demo.dart | 6 +- .../lib/demo/shrine/shrine_order.dart | 4 +- .../lib/demo/shrine/shrine_page.dart | 2 +- .../lib/demo/tabs_fab_demo.dart | 2 +- .../lib/demo/tooltip_demo.dart | 4 +- .../flutter_gallery/lib/gallery/demo.dart | 6 +- .../flutter_gallery/lib/gallery/drawer.dart | 8 +- .../lib/gallery/example_code.dart | 4 +- .../flutter_gallery/lib/gallery/home.dart | 6 +- examples/layers/widgets/media_query.dart | 2 +- examples/stocks/lib/stock_home.dart | 30 +++--- examples/stocks/lib/stock_settings.dart | 22 ++--- packages/flutter/lib/material.dart | 1 + .../flutter/lib/src/material/app_bar.dart | 23 +++-- packages/flutter/lib/src/material/button.dart | 3 +- packages/flutter/lib/src/material/chip.dart | 2 +- .../flutter/lib/src/material/data_table.dart | 9 +- .../flutter/lib/src/material/date_picker.dart | 7 +- .../flutter/lib/src/material/drawer_item.dart | 22 ++++- .../flutter/lib/src/material/drop_down.dart | 2 +- .../src/material/floating_action_button.dart | 5 +- .../lib/src/material/grid_tile_bar.dart | 3 +- packages/flutter/lib/src/material/icon.dart | 32 ++---- .../flutter/lib/src/material/icon_button.dart | 29 ++++-- .../flutter/lib/src/material/icon_theme.dart | 29 +++++- .../lib/src/material/icon_theme_data.dart | 51 +++++++++- .../flutter/lib/src/material/image_icon.dart | 98 +++++++++++++++++++ packages/flutter/lib/src/material/input.dart | 22 +++-- .../src/material/paginated_data_table.dart | 11 ++- .../flutter/lib/src/material/popup_menu.dart | 7 +- .../flutter/lib/src/material/scaffold.dart | 7 +- packages/flutter/lib/src/material/tabs.dart | 62 +++++++----- .../flutter/lib/src/material/theme_data.dart | 16 ++- .../lib/src/material/two_level_list.dart | 7 +- .../flutter/lib/src/painting/text_style.dart | 24 ++--- packages/flutter/test/material/icon_test.dart | 8 +- .../test/material/image_icon_test.dart | 80 +++++++++++++++ packages/flutter/test/widget/icon_test.dart | 2 +- .../lib/src/commands/analyze.dart | 6 +- .../templates/create/lib/main.dart.tmpl | 4 +- 57 files changed, 554 insertions(+), 237 deletions(-) create mode 100644 packages/flutter/lib/src/material/image_icon.dart create mode 100644 packages/flutter/test/material/image_icon_test.dart diff --git a/dev/benchmarks/complex_layout/lib/main.dart b/dev/benchmarks/complex_layout/lib/main.dart index ef00627683..7bee1d0376 100644 --- a/dev/benchmarks/complex_layout/lib/main.dart +++ b/dev/benchmarks/complex_layout/lib/main.dart @@ -75,7 +75,7 @@ class ComplexLayoutState extends State { title: new Text('Advanced Layout'), actions: [ new IconButton( - icon: Icons.create, + icon: new Icon(Icons.create), tooltip: 'Search', onPressed: () { print('Pressed search'); @@ -162,7 +162,7 @@ class MenuItemWithIcon extends StatelessWidget { Widget build(BuildContext context) { return new Row( children: [ - new Icon(icon: icon), + new Icon(icon), new Padding( padding: new EdgeInsets.only(left: 8.0, right: 8.0), child: new Text(title) @@ -263,7 +263,10 @@ class IconWithText extends StatelessWidget { return new Row( mainAxisAlignment: MainAxisAlignment.collapse, children: [ - new IconButton(icon: icon, onPressed: () { print('Pressed $title button'); } ), + new IconButton( + icon: new Icon(icon), + onPressed: () { print('Pressed $title button'); } + ), new Text(title) ] ); @@ -290,7 +293,7 @@ class MiniIconWithText extends StatelessWidget { backgroundColor: Theme.of(context).primaryColor, shape: BoxShape.circle ), - child: new Icon(icon: icon, color: Colors.white, size: 12.0) + child: new Icon(icon, color: Colors.white, size: 12.0) ) ), new Text(title, style: Theme.of(context).textTheme.caption) @@ -347,7 +350,7 @@ class UserHeader extends StatelessWidget { new Row( children: [ new Text('Yesterday at 11:55 • ', style: Theme.of(context).textTheme.caption), - new Icon(icon: Icons.people, size: 16.0, color: Theme.of(context).textTheme.caption.color) + new Icon(Icons.people, size: 16.0, color: Theme.of(context).textTheme.caption.color) ] ) ] @@ -392,8 +395,14 @@ class ItemImageBox extends StatelessWidget { child: new Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - new IconButton(icon: Icons.edit, onPressed: () { print('Pressed edit button'); }), - new IconButton(icon: Icons.zoom_in, onPressed: () { print('Pressed zoom button'); }) + new IconButton( + icon: new Icon(Icons.edit), + onPressed: () { print('Pressed edit button'); } + ), + new IconButton( + icon: new Icon(Icons.zoom_in), + onPressed: () { print('Pressed zoom button'); } + ), ] ) ), @@ -483,11 +492,11 @@ class ItemGalleryBox extends StatelessWidget { new Row( children: [ new IconButton( - icon: Icons.share, + icon: new Icon(Icons.share), onPressed: () { print('Pressed share'); } ), new IconButton( - icon: Icons.event, + icon: new Icon(Icons.event), onPressed: () { print('Pressed event'); } ), new Flexible( @@ -555,7 +564,7 @@ class BottomBarButton extends StatelessWidget { child: new Column( children: [ new IconButton( - icon: icon, + icon: new Icon(icon), onPressed: () { print('Pressed: $title'); } ), new Text(title, style: Theme.of(context).textTheme.caption) @@ -579,7 +588,7 @@ class GalleryDrawer extends StatelessWidget { children: [ new FancyDrawerHeader(), new DrawerItem( - icon: Icons.brightness_5, + icon: new Icon(Icons.brightness_5), onPressed: () { _changeTheme(context, true); }, selected: ComplexLayoutApp.of(context).lightTheme, child: new Row( @@ -594,7 +603,7 @@ class GalleryDrawer extends StatelessWidget { ) ), new DrawerItem( - icon: Icons.brightness_7, + icon: new Icon(Icons.brightness_7), onPressed: () { _changeTheme(context, false); }, selected: !ComplexLayoutApp.of(context).lightTheme, child: new Row( @@ -610,7 +619,7 @@ class GalleryDrawer extends StatelessWidget { ), new Divider(), new DrawerItem( - icon: Icons.hourglass_empty, + icon: new Icon(Icons.hourglass_empty), selected: timeDilation != 1.0, onPressed: () { ComplexLayoutApp.of(context).toggleAnimationSpeed(); }, child: new Row( diff --git a/dev/manual_tests/card_collection.dart b/dev/manual_tests/card_collection.dart index ebb669d790..ba057c6d71 100644 --- a/dev/manual_tests/card_collection.dart +++ b/dev/manual_tests/card_collection.dart @@ -147,7 +147,7 @@ class CardCollectionState extends State { buildFontRadioItem("Right-align text", TextAlign.right, _textAlign, _changeTextAlign, icon: Icons.format_align_right, enabled: !_editable), new Divider(), new DrawerItem( - icon: Icons.dvr, + icon: new Icon(Icons.dvr), onPressed: () { debugDumpApp(); debugDumpRenderTree(); }, child: new Text('Dump App to Console') ), @@ -227,7 +227,7 @@ class CardCollectionState extends State { Widget buildDrawerColorRadioItem(String label, Map itemValue, Map currentValue, ValueChanged> onChanged, { IconData icon, bool enabled: true }) { return new DrawerItem( - icon: icon, + icon: new Icon(icon), onPressed: enabled ? () { onChanged(itemValue); } : null, child: new Row( children: [ @@ -244,7 +244,7 @@ class CardCollectionState extends State { Widget buildDrawerDirectionRadioItem(String label, DismissDirection itemValue, DismissDirection currentValue, ValueChanged onChanged, { IconData icon, bool enabled: true }) { return new DrawerItem( - icon: icon, + icon: new Icon(icon), onPressed: enabled ? () { onChanged(itemValue); } : null, child: new Row( children: [ @@ -261,7 +261,7 @@ class CardCollectionState extends State { Widget buildFontRadioItem(String label, TextAlign itemValue, TextAlign currentValue, ValueChanged onChanged, { IconData icon, bool enabled: true }) { return new DrawerItem( - icon: icon, + icon: new Icon(icon), onPressed: enabled ? () { onChanged(itemValue); } : null, child: new Row( children: [ @@ -351,12 +351,12 @@ class CardCollectionState extends State { } // TODO(abarth): This icon is wrong in RTL. - Widget leftArrowIcon = new Icon(icon: Icons.arrow_back, size: 36.0); + Widget leftArrowIcon = new Icon(Icons.arrow_back, size: 36.0); if (_dismissDirection == DismissDirection.startToEnd) leftArrowIcon = new Opacity(opacity: 0.1, child: leftArrowIcon); // TODO(abarth): This icon is wrong in RTL. - Widget rightArrowIcon = new Icon(icon: Icons.arrow_forward, size: 36.0); + Widget rightArrowIcon = new Icon(Icons.arrow_forward, size: 36.0); if (_dismissDirection == DismissDirection.endToStart) rightArrowIcon = new Opacity(opacity: 0.1, child: rightArrowIcon); diff --git a/dev/manual_tests/fitness_demo.dart b/dev/manual_tests/fitness_demo.dart index 6499b081b4..06f1efad7c 100644 --- a/dev/manual_tests/fitness_demo.dart +++ b/dev/manual_tests/fitness_demo.dart @@ -153,7 +153,7 @@ class _FitnessDemoContentsState extends State<_FitnessDemoContents> { child: new Center( child: new Column( children: [ - new Icon(icon: icon, size: 48.0, color: color), + new Icon(icon, size: 48.0, color: color), new Text(value, style: new TextStyle(fontSize: 24.0, color: color)), new Text(description, style: new TextStyle(color: color)) ] diff --git a/dev/manual_tests/pageable_list.dart b/dev/manual_tests/pageable_list.dart index 5f5c60a5a9..2a436fbb40 100644 --- a/dev/manual_tests/pageable_list.dart +++ b/dev/manual_tests/pageable_list.dart @@ -87,13 +87,13 @@ class PageableListAppState extends State { child: new Block(children: [ new DrawerHeader(content: new Center(child: new Text('Options'))), new DrawerItem( - icon: Icons.more_horiz, + icon: new Icon(Icons.more_horiz), selected: scrollDirection == Axis.horizontal, child: new Text('Horizontal Layout'), onPressed: switchScrollDirection ), new DrawerItem( - icon: Icons.more_vert, + icon: new Icon(Icons.more_vert), selected: scrollDirection == Axis.vertical, child: new Text('Vertical Layout'), onPressed: switchScrollDirection diff --git a/examples/flutter_gallery/lib/demo/buttons_demo.dart b/examples/flutter_gallery/lib/demo/buttons_demo.dart index eecc902508..d3d1fce68e 100644 --- a/examples/flutter_gallery/lib/demo/buttons_demo.dart +++ b/examples/flutter_gallery/lib/demo/buttons_demo.dart @@ -168,14 +168,14 @@ class _ButtonsDemoState extends State { mainAxisAlignment: MainAxisAlignment.collapse, children: [ new IconButton( - icon: Icons.thumb_up, + icon: new Icon(Icons.thumb_up), onPressed: () { setState(() => iconButtonToggle = !iconButtonToggle); }, color: iconButtonToggle ? Theme.of(context).primaryColor : null ), new IconButton( - icon: Icons.thumb_up, + icon: new Icon(Icons.thumb_up), onPressed: null ) ] @@ -189,7 +189,7 @@ class _ButtonsDemoState extends State { return new Align( alignment: new FractionalOffset(0.5, 0.4), child: new FloatingActionButton( - child: new Icon(icon: Icons.add), + child: new Icon(Icons.add), onPressed: () { // Perform some action } diff --git a/examples/flutter_gallery/lib/demo/contacts_demo.dart b/examples/flutter_gallery/lib/demo/contacts_demo.dart index 5a6c8c1886..2d6f304852 100644 --- a/examples/flutter_gallery/lib/demo/contacts_demo.dart +++ b/examples/flutter_gallery/lib/demo/contacts_demo.dart @@ -23,7 +23,7 @@ class _ContactCategory extends StatelessWidget { children: [ new SizedBox( width: 72.0, - child: new Icon(icon: icon, color: Theme.of(context).primaryColor) + child: new Icon(icon, color: Theme.of(context).primaryColor) ), new Flexible(child: new Column(children: children)) ] @@ -59,7 +59,7 @@ class _ContactItem extends StatelessWidget { if (icon != null) { rowChildren.add(new SizedBox( width: 72.0, - child: new IconButton(icon: icon, onPressed: onPressed) + child: new IconButton(icon: new Icon(icon), onPressed: onPressed) )); } return new Padding( @@ -99,7 +99,7 @@ class ContactsDemoState extends State { expandedHeight: _appBarHeight, actions: [ new IconButton( - icon: Icons.create, + icon: new Icon(Icons.create), tooltip: 'Edit', onPressed: () { _scaffoldKey.currentState.showSnackBar(new SnackBar( diff --git a/examples/flutter_gallery/lib/demo/dialog_demo.dart b/examples/flutter_gallery/lib/demo/dialog_demo.dart index 621f373f15..691d8b213b 100644 --- a/examples/flutter_gallery/lib/demo/dialog_demo.dart +++ b/examples/flutter_gallery/lib/demo/dialog_demo.dart @@ -39,8 +39,8 @@ class DialogDemoItem extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ new Icon( + icon, size: 36.0, - icon: icon, color: color ), new Padding( diff --git a/examples/flutter_gallery/lib/demo/full_screen_dialog_demo.dart b/examples/flutter_gallery/lib/demo/full_screen_dialog_demo.dart index ecb7889814..6d8ddf3bfd 100644 --- a/examples/flutter_gallery/lib/demo/full_screen_dialog_demo.dart +++ b/examples/flutter_gallery/lib/demo/full_screen_dialog_demo.dart @@ -57,7 +57,7 @@ class DateTimeItem extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ new Text(new DateFormat('EEE, MMM d yyyy').format(date)), - new Icon(icon: Icons.arrow_drop_down, color: Colors.black54), + new Icon(Icons.arrow_drop_down, color: Colors.black54), ] ) ) @@ -82,7 +82,7 @@ class DateTimeItem extends StatelessWidget { child: new Row( children: [ new Text('$time'), - new Icon(icon: Icons.arrow_drop_down, color: Colors.black54), + new Icon(Icons.arrow_drop_down, color: Colors.black54), ] ) ) @@ -146,7 +146,7 @@ class FullScreenDialogDemoState extends State { return new Scaffold( appBar: new AppBar( leading: new IconButton( - icon: Icons.clear, + icon: new Icon(Icons.clear), onPressed: () { handleDismissButton(context); } ), title: new Text('New event'), diff --git a/examples/flutter_gallery/lib/demo/grid_list_demo.dart b/examples/flutter_gallery/lib/demo/grid_list_demo.dart index 75a6147196..e9bdd98c05 100644 --- a/examples/flutter_gallery/lib/demo/grid_list_demo.dart +++ b/examples/flutter_gallery/lib/demo/grid_list_demo.dart @@ -105,7 +105,7 @@ class GridDemoPhotoItem extends StatelessWidget { title: new Text(photo.title), backgroundColor: Colors.black45, leading: new Icon( - icon: icon, + icon, color: Colors.white ) ) @@ -122,7 +122,7 @@ class GridDemoPhotoItem extends StatelessWidget { title: new Text(photo.title), subtitle: new Text(photo.caption), trailing: new Icon( - icon: icon, + icon, color: Colors.white ) ) diff --git a/examples/flutter_gallery/lib/demo/icons_demo.dart b/examples/flutter_gallery/lib/demo/icons_demo.dart index 9a33b7a94d..c0a82fd09f 100644 --- a/examples/flutter_gallery/lib/demo/icons_demo.dart +++ b/examples/flutter_gallery/lib/demo/icons_demo.dart @@ -47,8 +47,8 @@ class IconsDemoState extends State { Widget buildIconButton(double size, IconData icon, bool enabled) { return new IconButton( + icon: new Icon(icon), size: size, - icon: icon, color: iconColor, tooltip: "${enabled ? 'Enabled' : 'Disabled'} icon button", onPressed: enabled ? handleIconButtonPress : null @@ -130,7 +130,7 @@ class IconsDemoState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ new Icon( - icon: Icons.brightness_7, + Icons.brightness_7, color: iconColor.withAlpha(0x33) // 0.2 * 255 = 0x33 ), new Slider( @@ -145,7 +145,7 @@ class IconsDemoState extends State { } ), new Icon( - icon: Icons.brightness_7, + Icons.brightness_7, color: iconColor.withAlpha(0xFF) ), ] diff --git a/examples/flutter_gallery/lib/demo/leave_behind_demo.dart b/examples/flutter_gallery/lib/demo/leave_behind_demo.dart index d0db0ba251..d929c58874 100644 --- a/examples/flutter_gallery/lib/demo/leave_behind_demo.dart +++ b/examples/flutter_gallery/lib/demo/leave_behind_demo.dart @@ -104,13 +104,13 @@ class LeaveBehindDemoState extends State { background: new Container( decoration: new BoxDecoration(backgroundColor: theme.primaryColor), child: new ListItem( - leading: new Icon(icon: Icons.delete, color: Colors.white, size: 36.0) + leading: new Icon(Icons.delete, color: Colors.white, size: 36.0) ) ), secondaryBackground: new Container( decoration: new BoxDecoration(backgroundColor: theme.primaryColor), child: new ListItem( - trailing: new Icon(icon: Icons.archive, color: Colors.white, size: 36.0) + trailing: new Icon(Icons.archive, color: Colors.white, size: 36.0) ) ), child: new Container( diff --git a/examples/flutter_gallery/lib/demo/list_demo.dart b/examples/flutter_gallery/lib/demo/list_demo.dart index 33dca56015..b8bb4f3934 100644 --- a/examples/flutter_gallery/lib/demo/list_demo.dart +++ b/examples/flutter_gallery/lib/demo/list_demo.dart @@ -144,7 +144,7 @@ class ListDemoState extends State { leading: _showAvatars ? new CircleAvatar(child: new Text(item)) : null, title: new Text('This item represents $item.'), subtitle: secondary, - trailing: _showIcons ? new Icon(icon: Icons.info, color: Theme.of(context).disabledColor) : null + trailing: _showIcons ? new Icon(Icons.info, color: Theme.of(context).disabledColor) : null ); } @@ -175,7 +175,7 @@ class ListDemoState extends State { title: new Text('Scrolling list\n$itemTypeText$layoutText'), actions: [ new IconButton( - icon: Icons.sort_by_alpha, + icon: new Icon(Icons.sort_by_alpha), tooltip: 'Sort', onPressed: () { setState(() { @@ -185,7 +185,7 @@ class ListDemoState extends State { } ), new IconButton( - icon: Icons.more_vert, + icon: new Icon(Icons.more_vert), tooltip: 'Show menu', onPressed: () { showConfigurationSheet(context); } ) diff --git a/examples/flutter_gallery/lib/demo/menu_demo.dart b/examples/flutter_gallery/lib/demo/menu_demo.dart index a5df0e16fd..76bff216e2 100644 --- a/examples/flutter_gallery/lib/demo/menu_demo.dart +++ b/examples/flutter_gallery/lib/demo/menu_demo.dart @@ -122,21 +122,21 @@ class MenuDemoState extends State { new PopupMenuItem( value: 'Preview', child: new ListItem( - leading: new Icon(icon: Icons.visibility), + leading: new Icon(Icons.visibility), title: new Text('Preview') ) ), new PopupMenuItem( value: 'Share', child: new ListItem( - leading: new Icon(icon: Icons.person_add), + leading: new Icon(Icons.person_add), title: new Text('Share') ) ), new PopupMenuItem( value: 'Get Link', child: new ListItem( - leading: new Icon(icon: Icons.link), + leading: new Icon(Icons.link), title: new Text('Get link') ) ), @@ -144,7 +144,7 @@ class MenuDemoState extends State { new PopupMenuItem( value: 'Remove', child: new ListItem( - leading: new Icon(icon: Icons.delete), + leading: new Icon(Icons.delete), title: new Text('Remove') ) ) diff --git a/examples/flutter_gallery/lib/demo/overscroll_demo.dart b/examples/flutter_gallery/lib/demo/overscroll_demo.dart index c9a3eb2429..7e0898493c 100644 --- a/examples/flutter_gallery/lib/demo/overscroll_demo.dart +++ b/examples/flutter_gallery/lib/demo/overscroll_demo.dart @@ -74,7 +74,7 @@ class OverscrollDemoState extends State { title: new Text('$indicatorTypeText'), actions: [ new IconButton( - icon: Icons.refresh, + icon: new Icon(Icons.refresh), tooltip: 'Pull to refresh', onPressed: () { setState(() { @@ -83,7 +83,7 @@ class OverscrollDemoState extends State { } ), new IconButton( - icon: Icons.play_for_work, + icon: new Icon(Icons.play_for_work), tooltip: 'Over-scroll indicator', onPressed: () { setState(() { diff --git a/examples/flutter_gallery/lib/demo/page_selector_demo.dart b/examples/flutter_gallery/lib/demo/page_selector_demo.dart index 84341fa3fd..e9afe8bd76 100644 --- a/examples/flutter_gallery/lib/demo/page_selector_demo.dart +++ b/examples/flutter_gallery/lib/demo/page_selector_demo.dart @@ -39,14 +39,14 @@ class PageSelectorDemo extends StatelessWidget { child: new Row( children: [ new IconButton( - icon: Icons.chevron_left, + icon: new Icon(Icons.chevron_left), color: color, onPressed: () { _handleArrowButtonPress(context, -1); }, tooltip: 'Page back' ), new TabPageSelector(), new IconButton( - icon: Icons.chevron_right, + icon: new Icon(Icons.chevron_right), color: color, onPressed: () { _handleArrowButtonPress(context, 1); }, tooltip: 'Page forward' @@ -63,7 +63,7 @@ class PageSelectorDemo extends StatelessWidget { padding: const EdgeInsets.all(12.0), child: new Card( child: new Center( - child: new Icon(icon: icon, size: 128.0, color: color) + child: new Icon(icon, size: 128.0, color: color) ) ) ); diff --git a/examples/flutter_gallery/lib/demo/persistent_bottom_sheet_demo.dart b/examples/flutter_gallery/lib/demo/persistent_bottom_sheet_demo.dart index b99af8e2c2..f0d9356677 100644 --- a/examples/flutter_gallery/lib/demo/persistent_bottom_sheet_demo.dart +++ b/examples/flutter_gallery/lib/demo/persistent_bottom_sheet_demo.dart @@ -61,7 +61,7 @@ class _PersistentBottomSheetDemoState extends State { floatingActionButton: new FloatingActionButton( onPressed: null, backgroundColor: Colors.redAccent[200], - child: new Icon(icon: Icons.add) + child: new Icon(Icons.add) ), body: new Center( child: new RaisedButton( diff --git a/examples/flutter_gallery/lib/demo/pesto_demo.dart b/examples/flutter_gallery/lib/demo/pesto_demo.dart index 65532cfa4c..998fe63b28 100644 --- a/examples/flutter_gallery/lib/demo/pesto_demo.dart +++ b/examples/flutter_gallery/lib/demo/pesto_demo.dart @@ -66,7 +66,7 @@ class _PestoDemoState extends State { appBar: _buildAppBar(context), drawer: _buildDrawer(context), floatingActionButton: new FloatingActionButton( - child: new Icon(icon: Icons.edit), + child: new Icon(Icons.edit), onPressed: () { } ), body: _buildBody(context) @@ -80,7 +80,7 @@ class _PestoDemoState extends State { expandedHeight: _kAppBarHeight, actions: [ new IconButton( - icon: Icons.search, + icon: new Icon(Icons.search), tooltip: 'Search', onPressed: () { _scaffoldKey.currentState.showSnackBar(new SnackBar( @@ -305,7 +305,7 @@ class _RecipePageState extends State<_RecipePage> { backgroundColor: Colors.transparent, elevation: 0, leading: new IconButton( - icon: Icons.arrow_back, + icon: new Icon(Icons.arrow_back), onPressed: () => Navigator.pop(context), tooltip: 'Back' ), @@ -364,7 +364,7 @@ class _RecipePageState extends State<_RecipePage> { new Positioned( right: 16.0, child: new FloatingActionButton( - child: new Icon(icon: isFavorite ? Icons.favorite : Icons.favorite_border), + child: new Icon(isFavorite ? Icons.favorite : Icons.favorite_border), onPressed: _toggleFavorite ) ) @@ -384,7 +384,7 @@ class _RecipePageState extends State<_RecipePage> { children: [ new Padding( padding: const EdgeInsets.only(right: 24.0), - child: new Icon(icon: icon, color: Colors.black54) + child: new Icon(icon, color: Colors.black54) ), new Text(label, style: menuItemStyle) ] diff --git a/examples/flutter_gallery/lib/demo/scrollable_tabs_demo.dart b/examples/flutter_gallery/lib/demo/scrollable_tabs_demo.dart index 567cab98e7..7aa405babf 100644 --- a/examples/flutter_gallery/lib/demo/scrollable_tabs_demo.dart +++ b/examples/flutter_gallery/lib/demo/scrollable_tabs_demo.dart @@ -78,9 +78,9 @@ class ScrollableTabsDemoState extends State { value: (IconData icon) { switch(_demoStyle) { case TabsDemoStyle.iconsAndText: - return new TabLabel(text: labels[icon], icon: icon); + return new TabLabel(text: labels[icon], icon: new Icon(icon)); case TabsDemoStyle.iconsOnly: - return new TabLabel(icon: icon); + return new TabLabel(icon: new Icon(icon)); case TabsDemoStyle.textOnly: return new TabLabel(text: labels[icon]); } @@ -96,7 +96,7 @@ class ScrollableTabsDemoState extends State { child:new Card( child: new Center( child: new Icon( - icon: icon, + icon, color: iconColor, size: 128.0 ) diff --git a/examples/flutter_gallery/lib/demo/shrine/shrine_order.dart b/examples/flutter_gallery/lib/demo/shrine/shrine_order.dart index b5b391995c..8524a2e990 100644 --- a/examples/flutter_gallery/lib/demo/shrine/shrine_order.dart +++ b/examples/flutter_gallery/lib/demo/shrine/shrine_order.dart @@ -55,7 +55,7 @@ class OrderItem extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 16.0), child: new Center( child: new Icon( - icon: Icons.info_outline, + Icons.info_outline, size: 24.0, color: const Color(0xFFFFE0E0) ) @@ -175,7 +175,7 @@ class _OrderPageState extends State { }, backgroundColor: const Color(0xFF16F0F0), child: new Icon( - icon: Icons.add_shopping_cart, + Icons.add_shopping_cart, color: Colors.black ) ), diff --git a/examples/flutter_gallery/lib/demo/shrine/shrine_page.dart b/examples/flutter_gallery/lib/demo/shrine/shrine_page.dart index 8dc30b7ab4..ff0bfe112a 100644 --- a/examples/flutter_gallery/lib/demo/shrine/shrine_page.dart +++ b/examples/flutter_gallery/lib/demo/shrine/shrine_page.dart @@ -100,7 +100,7 @@ class ShrinePageState extends State { ), actions: [ new IconButton( - icon: Icons.shopping_cart, + icon: new Icon(Icons.shopping_cart), tooltip: 'Shopping cart', onPressed: () { _showShoppingCart(); diff --git a/examples/flutter_gallery/lib/demo/tabs_fab_demo.dart b/examples/flutter_gallery/lib/demo/tabs_fab_demo.dart index 8e92f795f7..9ddd1096a7 100644 --- a/examples/flutter_gallery/lib/demo/tabs_fab_demo.dart +++ b/examples/flutter_gallery/lib/demo/tabs_fab_demo.dart @@ -15,7 +15,7 @@ class _Page { Color get labelColor => colors != null ? colors[300] : Colors.grey[300]; bool get fabDefined => colors != null && icon != null; Color get fabColor => colors[400]; - Icon get fabIcon => new Icon(icon: icon); + Icon get fabIcon => new Icon(icon); Key get fabKey => new ValueKey(fabColor); } diff --git a/examples/flutter_gallery/lib/demo/tooltip_demo.dart b/examples/flutter_gallery/lib/demo/tooltip_demo.dart index 00aa50bcbf..77a87fca52 100644 --- a/examples/flutter_gallery/lib/demo/tooltip_demo.dart +++ b/examples/flutter_gallery/lib/demo/tooltip_demo.dart @@ -31,8 +31,8 @@ class TooltipDemo extends StatelessWidget { new Tooltip( message: 'call icon', child: new Icon( + Icons.call, size: 18.0, - icon: Icons.call, color: theme.primaryColor ) ), @@ -42,7 +42,7 @@ class TooltipDemo extends StatelessWidget { new Center( child: new IconButton( size: 48.0, - icon: Icons.call, + icon: new Icon(Icons.call), color: theme.primaryColor, tooltip: 'Place a phone call', onPressed: () { diff --git a/examples/flutter_gallery/lib/gallery/demo.dart b/examples/flutter_gallery/lib/gallery/demo.dart index 38bf2e6d06..cf7d2a878f 100644 --- a/examples/flutter_gallery/lib/gallery/demo.dart +++ b/examples/flutter_gallery/lib/gallery/demo.dart @@ -158,7 +158,7 @@ class DemoBottomBar extends StatelessWidget { children: [ new Padding( padding: new EdgeInsets.only(right: 8.0), - child: new Icon(icon: Icons.code) + child: new Icon(Icons.code) ), new Text('VIEW CODE') ] @@ -170,7 +170,7 @@ class DemoBottomBar extends StatelessWidget { children: [ new Padding( padding: new EdgeInsets.only(right: 8.0), - child: new Icon(icon: Icons.star) + child: new Icon(Icons.star) ), new Text('LIVE DEMO') ] @@ -267,7 +267,7 @@ class FullScreenCodeDialogState extends State { return new Scaffold( appBar: new AppBar( leading: new IconButton( - icon: Icons.clear, + icon: new Icon(Icons.clear), onPressed: () { Navigator.pop(context); } ), title: new Text('Example code') diff --git a/examples/flutter_gallery/lib/gallery/drawer.dart b/examples/flutter_gallery/lib/gallery/drawer.dart index 0362a08a9a..7657db20be 100644 --- a/examples/flutter_gallery/lib/gallery/drawer.dart +++ b/examples/flutter_gallery/lib/gallery/drawer.dart @@ -36,7 +36,7 @@ class GalleryDrawer extends StatelessWidget { content: new Center(child: new Text('Flutter gallery')) ), new DrawerItem( - icon: Icons.brightness_5, + icon: new Icon(Icons.brightness_5), onPressed: () { onThemeChanged(true); }, selected: useLightTheme, child: new Row( @@ -51,7 +51,7 @@ class GalleryDrawer extends StatelessWidget { ) ), new DrawerItem( - icon: Icons.brightness_7, + icon: new Icon(Icons.brightness_7), onPressed: () { onThemeChanged(false); }, selected: useLightTheme, child: new Row( @@ -67,7 +67,7 @@ class GalleryDrawer extends StatelessWidget { ), new Divider(), new DrawerItem( - icon: Icons.hourglass_empty, + icon: new Icon(Icons.hourglass_empty), selected: timeDilation != 1.0, onPressed: () { onTimeDilationChanged(timeDilation != 1.0 ? 1.0 : 20.0); }, child: new Row( @@ -81,7 +81,7 @@ class GalleryDrawer extends StatelessWidget { ) ), new DrawerItem( - icon: Icons.assessment, + icon: new Icon(Icons.assessment), onPressed: () { onShowPerformanceOverlayChanged(!showPerformanceOverlay); }, selected: showPerformanceOverlay, child: new Row( diff --git a/examples/flutter_gallery/lib/gallery/example_code.dart b/examples/flutter_gallery/lib/gallery/example_code.dart index cf8b816e6b..9852bfcb35 100644 --- a/examples/flutter_gallery/lib/gallery/example_code.dart +++ b/examples/flutter_gallery/lib/gallery/example_code.dart @@ -83,7 +83,7 @@ bool value; // Toggleable icon button. new IconButton( - icon: Icons.thumb_up, + icon: new Icon(Icons.thumb_up), onPressed: () { setState(() => value = !value); }, @@ -99,7 +99,7 @@ new Scaffold( title: new Text('Demo') ), floatingActionButton: new FloatingActionButton( - child: new Icon(icon: Icons.add), + child: new Icon(Icons.add), onPressed: null ) ); diff --git a/examples/flutter_gallery/lib/gallery/home.dart b/examples/flutter_gallery/lib/gallery/home.dart index a78963dc5f..265e0d5362 100644 --- a/examples/flutter_gallery/lib/gallery/home.dart +++ b/examples/flutter_gallery/lib/gallery/home.dart @@ -80,17 +80,17 @@ class GalleryHomeState extends State { type: MaterialListType.oneLine, children: [ new TwoLevelSublist( - leading: new Icon(icon: Icons.star), + leading: new Icon(Icons.star), title: new Text('Demos'), children: _demoItems ), new TwoLevelSublist( - leading: new Icon(icon: Icons.extension), + leading: new Icon(Icons.extension), title: new Text('Components'), children: _componentItems ), new TwoLevelSublist( - leading: new Icon(icon: Icons.color_lens), + leading: new Icon(Icons.color_lens), title: new Text('Style'), children: _styleItems ) diff --git a/examples/layers/widgets/media_query.dart b/examples/layers/widgets/media_query.dart index fc2f51db75..e7026b3785 100644 --- a/examples/layers/widgets/media_query.dart +++ b/examples/layers/widgets/media_query.dart @@ -52,7 +52,7 @@ class AdaptedGridItem extends StatelessWidget { child: new Text(name) ), new IconButton( - icon: Icons.more_vert, + icon: new Icon(Icons.more_vert), onPressed: null ) ] diff --git a/examples/stocks/lib/stock_home.dart b/examples/stocks/lib/stock_home.dart index ba50cd85ac..ea5c9c1025 100644 --- a/examples/stocks/lib/stock_home.dart +++ b/examples/stocks/lib/stock_home.dart @@ -26,10 +26,11 @@ class _NotImplementedDialog extends StatelessWidget { content: new Text('This feature has not yet been implemented.'), actions: [ new FlatButton( + onPressed: () { debugDumpApp(); }, child: new Row( children: [ new Icon( - icon: Icons.dvr, + Icons.dvr, size: 18.0 ), new Container( @@ -37,14 +38,13 @@ class _NotImplementedDialog extends StatelessWidget { ), new Text('DUMP APP TO CONSOLE'), ] - ), - onPressed: () { debugDumpApp(); } + ) ), new FlatButton( - child: new Text('OH WELL'), onPressed: () { Navigator.pop(context, false); - } + }, + child: new Text('OH WELL') ) ] ); @@ -126,12 +126,12 @@ class StockHomeState extends State { child: new Block(children: [ new DrawerHeader(content: new Center(child: new Text('Stocks'))), new DrawerItem( - icon: Icons.assessment, + icon: new Icon(Icons.assessment), selected: true, child: new Text('Stock List') ), new DrawerItem( - icon: Icons.account_balance, + icon: new Icon(Icons.account_balance), onPressed: () { showDialog( context: context, @@ -158,7 +158,7 @@ class StockHomeState extends State { child: new Text('Account Balance') ), new DrawerItem( - icon: Icons.dvr, + icon: new Icon(Icons.dvr), onPressed: () { try { debugDumpApp(); @@ -173,7 +173,7 @@ class StockHomeState extends State { ), new Divider(), new DrawerItem( - icon: Icons.thumb_up, + icon: new Icon(Icons.thumb_up), onPressed: () => _handleStockModeChange(StockMode.optimistic), child: new Row( children: [ @@ -183,7 +183,7 @@ class StockHomeState extends State { ) ), new DrawerItem( - icon: Icons.thumb_down, + icon: new Icon(Icons.thumb_down), onPressed: () => _handleStockModeChange(StockMode.pessimistic), child: new Row( children: [ @@ -194,11 +194,11 @@ class StockHomeState extends State { ), new Divider(), new DrawerItem( - icon: Icons.settings, + icon: new Icon(Icons.settings), onPressed: _handleShowSettings, child: new Text('Settings')), new DrawerItem( - icon: Icons.help, + icon: new Icon(Icons.help), child: new Text('Help & Feedback')) ]) ); @@ -214,7 +214,7 @@ class StockHomeState extends State { title: new Text(StockStrings.of(context).title()), actions: [ new IconButton( - icon: Icons.search, + icon: new Icon(Icons.search), onPressed: _handleSearchBegin, tooltip: 'Search' ), @@ -307,7 +307,7 @@ class StockHomeState extends State { Widget buildSearchBar() { return new AppBar( leading: new IconButton( - icon: Icons.arrow_back, + icon: new Icon(Icons.arrow_back), color: Theme.of(context).accentColor, onPressed: _handleSearchEnd, tooltip: 'Back' @@ -332,7 +332,7 @@ class StockHomeState extends State { Widget buildFloatingActionButton() { return new FloatingActionButton( tooltip: 'Create company', - child: new Icon(icon: Icons.add), + child: new Icon(Icons.add), backgroundColor: Colors.redAccent[200], onPressed: _handleCreateCompany ); diff --git a/examples/stocks/lib/stock_settings.dart b/examples/stocks/lib/stock_settings.dart index f21f0fd3de..7de0713ec0 100644 --- a/examples/stocks/lib/stock_settings.dart +++ b/examples/stocks/lib/stock_settings.dart @@ -104,7 +104,7 @@ class StockSettingsState extends State { Widget buildSettingsPane(BuildContext context) { List rows = [ new DrawerItem( - icon: Icons.thumb_up, + icon: new Icon(Icons.thumb_up), onPressed: () => _confirmOptimismChange(), child: new Row( children: [ @@ -117,7 +117,7 @@ class StockSettingsState extends State { ) ), new DrawerItem( - icon: Icons.backup, + icon: new Icon(Icons.backup), onPressed: () { _handleBackupChanged(!(config.configuration.backupMode == BackupMode.enabled)); }, child: new Row( children: [ @@ -130,7 +130,7 @@ class StockSettingsState extends State { ) ), new DrawerItem( - icon: Icons.picture_in_picture, + icon: new Icon(Icons.picture_in_picture), onPressed: () { _handleShowPerformanceOverlayChanged(!config.configuration.showPerformanceOverlay); }, child: new Row( children: [ @@ -143,7 +143,7 @@ class StockSettingsState extends State { ) ), new DrawerItem( - icon: Icons.accessibility, + icon: new Icon(Icons.accessibility), onPressed: () { _handleShowSemanticsDebuggerChanged(!config.configuration.showSemanticsDebugger); }, child: new Row( children: [ @@ -158,9 +158,9 @@ class StockSettingsState extends State { ]; assert(() { // material grid and size construction lines are only available in checked mode - rows.addAll([ + rows.addAll([ new DrawerItem( - icon: Icons.border_clear, + icon: new Icon(Icons.border_clear), onPressed: () { _handleShowGridChanged(!config.configuration.debugShowGrid); }, child: new Row( children: [ @@ -173,7 +173,7 @@ class StockSettingsState extends State { ) ), new DrawerItem( - icon: Icons.border_all, + icon: new Icon(Icons.border_all), onPressed: () { _handleShowSizesChanged(!config.configuration.debugShowSizes); }, child: new Row( children: [ @@ -186,7 +186,7 @@ class StockSettingsState extends State { ) ), new DrawerItem( - icon: Icons.format_color_text, + icon: new Icon(Icons.format_color_text), onPressed: () { _handleShowBaselinesChanged(!config.configuration.debugShowBaselines); }, child: new Row( children: [ @@ -199,7 +199,7 @@ class StockSettingsState extends State { ) ), new DrawerItem( - icon: Icons.filter_none, + icon: new Icon(Icons.filter_none), onPressed: () { _handleShowLayersChanged(!config.configuration.debugShowLayers); }, child: new Row( children: [ @@ -212,7 +212,7 @@ class StockSettingsState extends State { ) ), new DrawerItem( - icon: Icons.mouse, + icon: new Icon(Icons.mouse), onPressed: () { _handleShowPointersChanged(!config.configuration.debugShowPointers); }, child: new Row( children: [ @@ -225,7 +225,7 @@ class StockSettingsState extends State { ) ), new DrawerItem( - icon: Icons.gradient, + icon: new Icon(Icons.gradient), onPressed: () { _handleShowRainbowChanged(!config.configuration.debugShowRainbow); }, child: new Row( children: [ diff --git a/packages/flutter/lib/material.dart b/packages/flutter/lib/material.dart index cb3046ad58..269b65e220 100644 --- a/packages/flutter/lib/material.dart +++ b/packages/flutter/lib/material.dart @@ -42,6 +42,7 @@ export 'src/material/icon_button.dart'; export 'src/material/icon_theme.dart'; export 'src/material/icon_theme_data.dart'; export 'src/material/icons.dart'; +export 'src/material/image_icon.dart'; export 'src/material/ink_well.dart'; export 'src/material/input.dart'; export 'src/material/list.dart'; diff --git a/packages/flutter/lib/src/material/app_bar.dart b/packages/flutter/lib/src/material/app_bar.dart index 7c6ddcf6f8..4a80c70c3c 100644 --- a/packages/flutter/lib/src/material/app_bar.dart +++ b/packages/flutter/lib/src/material/app_bar.dart @@ -46,6 +46,10 @@ abstract class AppBarBottomWidget extends Widget { /// AppBar's [collapsedHeight] and [bottomHeight] define how small the app bar /// will become when the application is scrolled. /// +/// By default, icons within an app bar will use the +/// [ThemeData.primaryIconTheme]. This can be overridden by nesting an +/// [IconTheme] inside the [AppBar]. +/// /// See also: /// /// * [Scaffold] @@ -126,6 +130,10 @@ class AppBar extends StatelessWidget { /// tandem with [backgroundColor]. /// /// Defaults to [ThemeData.brightness]. + /// + /// Icons within the app bar will continue to use + /// [ThemeData.primaryIconTheme]. If this clashes with the brightness + /// specified here, consider using an [IconTheme] inside the [AppBar]. final Brightness brightness; /// The typographic style to use for text in the app bar. @@ -207,7 +215,7 @@ class AppBar extends StatelessWidget { final double statusBarHeight = MediaQuery.of(context).padding.top; final ThemeData theme = Theme.of(context); - IconThemeData iconTheme = IconTheme.of(context) ?? theme.primaryIconTheme; + IconThemeData iconTheme = theme.primaryIconTheme; TextStyle centerStyle = textTheme?.title ?? theme.primaryTextTheme.title; TextStyle sideStyle = textTheme?.body1 ?? theme.primaryTextTheme.body1; @@ -223,13 +231,9 @@ class AppBar extends StatelessWidget { centerStyle = centerStyle.copyWith(color: centerStyle.color.withOpacity(opacity)); if (sideStyle?.color != null) sideStyle = sideStyle.copyWith(color: sideStyle.color.withOpacity(opacity)); - - if (iconTheme != null) { - iconTheme = new IconThemeData( - opacity: opacity * iconTheme.opacity, - color: iconTheme.color - ); - } + iconTheme = iconTheme.copyWith( + opacity: opacity * (iconTheme.opacity ?? 1.0) + ); } final List toolBarRow = []; @@ -256,7 +260,8 @@ class AppBar extends StatelessWidget { Widget appBar = new SizedBox( height: kToolBarHeight, - child: new IconTheme( + child: new IconTheme.merge( + context: context, data: iconTheme, child: new DefaultTextStyle( style: sideStyle, diff --git a/packages/flutter/lib/src/material/button.dart b/packages/flutter/lib/src/material/button.dart index 17575228fc..e53add934c 100644 --- a/packages/flutter/lib/src/material/button.dart +++ b/packages/flutter/lib/src/material/button.dart @@ -245,7 +245,8 @@ class _MaterialButtonState extends State { final ButtonTheme buttonTheme = ButtonTheme.of(context); final double height = config.height ?? buttonTheme.height; final int elevation = (_highlight ? config.highlightElevation : config.elevation) ?? 0; - Widget contents = new IconTheme( + Widget contents = new IconTheme.merge( + context: context, data: new IconThemeData( color: textColor ), diff --git a/packages/flutter/lib/src/material/chip.dart b/packages/flutter/lib/src/material/chip.dart index da18996632..34b4f2ad03 100644 --- a/packages/flutter/lib/src/material/chip.dart +++ b/packages/flutter/lib/src/material/chip.dart @@ -96,7 +96,7 @@ class Chip extends StatelessWidget { child: new Container( padding: const EdgeInsets.symmetric(horizontal: 4.0), child: new Icon( - icon: Icons.cancel, + Icons.cancel, size: 18.0, color: Colors.black54 ) diff --git a/packages/flutter/lib/src/material/data_table.dart b/packages/flutter/lib/src/material/data_table.dart index e01351d7a2..37f4ce0b9f 100644 --- a/packages/flutter/lib/src/material/data_table.dart +++ b/packages/flutter/lib/src/material/data_table.dart @@ -436,6 +436,7 @@ class DataTable extends StatelessWidget { } Widget _buildDataCell({ + BuildContext context, EdgeInsets padding, Widget label, bool numeric, @@ -445,7 +446,7 @@ class DataTable extends StatelessWidget { VoidCallback onSelectChanged }) { if (showEditIcon) { - final Widget icon = new Icon(icon: Icons.edit, size: 18.0); + final Widget icon = new Icon(Icons.edit, size: 18.0); label = new Flexible(child: label); label = new Row(children: numeric ? [ icon, label ] : [ label, icon ]); } @@ -460,7 +461,8 @@ class DataTable extends StatelessWidget { fontSize: 13.0, color: placeholder ? Colors.black38 : Colors.black87 // TODO(ianh): defer to theme, since this won't work in e.g. the dark theme ), - child: new IconTheme( + child: new IconTheme.merge( + context: context, data: new IconThemeData( color: Colors.black54 ), @@ -561,6 +563,7 @@ class DataTable extends StatelessWidget { for (DataRow row in rows) { DataCell cell = row.cells[dataColumnIndex]; tableRows[rowIndex].children[displayColumnIndex] = _buildDataCell( + context: context, padding: padding, label: cell.widget, numeric: column.numeric, @@ -765,7 +768,7 @@ class _SortArrowState extends State<_SortArrow> { ..setTranslationRaw(0.0, _kArrowIconBaselineOffset, 0.0), alignment: FractionalOffset.center, child: new Icon( - icon: Icons.arrow_downward, + Icons.arrow_downward, size: _kArrowIconSize, color: Colors.black87 ) diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart index 04081ff545..718eb29622 100644 --- a/packages/flutter/lib/src/material/date_picker.dart +++ b/packages/flutter/lib/src/material/date_picker.dart @@ -12,8 +12,9 @@ import 'package:meta/meta.dart'; import 'colors.dart'; import 'debug.dart'; -import 'icons.dart'; +import 'icon.dart'; import 'icon_button.dart'; +import 'icons.dart'; import 'ink_well.dart'; import 'theme.dart'; import 'typography.dart'; @@ -471,7 +472,7 @@ class _MonthPickerState extends State { top: 0.0, left: 8.0, child: new IconButton( - icon: Icons.chevron_left, + icon: new Icon(Icons.chevron_left), tooltip: 'Previous month', onPressed: _handlePreviousMonth ) @@ -480,7 +481,7 @@ class _MonthPickerState extends State { top: 0.0, right: 8.0, child: new IconButton( - icon: Icons.chevron_right, + icon: new Icon(Icons.chevron_right), tooltip: 'Next month', onPressed: _handleNextMonth ) diff --git a/packages/flutter/lib/src/material/drawer_item.dart b/packages/flutter/lib/src/material/drawer_item.dart index 40f32f5ccd..cafa0eedf5 100644 --- a/packages/flutter/lib/src/material/drawer_item.dart +++ b/packages/flutter/lib/src/material/drawer_item.dart @@ -8,7 +8,9 @@ import 'colors.dart'; import 'constants.dart'; import 'debug.dart'; import 'icon.dart'; -import 'icons.dart'; +import 'icon_theme.dart'; +import 'icon_theme_data.dart'; +import 'image_icon.dart'; import 'ink_well.dart'; import 'theme.dart'; @@ -36,7 +38,13 @@ class DrawerItem extends StatelessWidget { }) : super(key: key); /// The icon to display before the child widget. - final IconData icon; + /// + /// The size and color of the icon is configured automatically using an + /// [IconTheme] and therefore do not need to be explicitly given in the + /// icon widget. + /// + /// See [Icon], [ImageIcon]. + final Widget icon; /// The widget below this widget in the tree. final Widget child; @@ -94,9 +102,13 @@ class DrawerItem extends StatelessWidget { children.add( new Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: new Icon( - icon: icon, - color: _getIconColor(themeData) + child: new IconTheme.merge( + context: context, + data: new IconThemeData( + color: _getIconColor(themeData), + size: 24.0 + ), + child: icon ) ) ); diff --git a/packages/flutter/lib/src/material/drop_down.dart b/packages/flutter/lib/src/material/drop_down.dart index b3bebacad5..18a9b2d114 100644 --- a/packages/flutter/lib/src/material/drop_down.dart +++ b/packages/flutter/lib/src/material/drop_down.dart @@ -495,7 +495,7 @@ class _DropDownButtonState extends State> { alignment: FractionalOffset.centerLeft, children: config.items ), - new Icon(icon: Icons.arrow_drop_down, size: config.iconSize) + new Icon(Icons.arrow_drop_down, size: config.iconSize) ] ) ); diff --git a/packages/flutter/lib/src/material/floating_action_button.dart b/packages/flutter/lib/src/material/floating_action_button.dart index 35c84849fe..8180d8fbdf 100644 --- a/packages/flutter/lib/src/material/floating_action_button.dart +++ b/packages/flutter/lib/src/material/floating_action_button.dart @@ -104,13 +104,14 @@ class _FloatingActionButtonState extends State { Color iconColor = Colors.white; Color materialColor = config.backgroundColor; if (materialColor == null) { - ThemeData themeData = Theme.of(context); + final ThemeData themeData = Theme.of(context); materialColor = themeData.accentColor; iconColor = themeData.accentColorBrightness == Brightness.dark ? Colors.white : Colors.black; } Widget result = new Center( - child: new IconTheme( + child: new IconTheme.merge( + context: context, data: new IconThemeData(color: iconColor), child: config.child ) diff --git a/packages/flutter/lib/src/material/grid_tile_bar.dart b/packages/flutter/lib/src/material/grid_tile_bar.dart index d0ca0b0b32..e6fdccbd06 100644 --- a/packages/flutter/lib/src/material/grid_tile_bar.dart +++ b/packages/flutter/lib/src/material/grid_tile_bar.dart @@ -118,7 +118,8 @@ class GridTileBar extends StatelessWidget { return new Container( padding: padding, decoration: decoration, - child: new IconTheme( + child: new IconTheme.merge( + context: context, data: new IconThemeData(color: Colors.white), child: new Row( crossAxisAlignment: CrossAxisAlignment.center, diff --git a/packages/flutter/lib/src/material/icon.dart b/packages/flutter/lib/src/material/icon.dart index da8f3c6010..bad3934f0b 100644 --- a/packages/flutter/lib/src/material/icon.dart +++ b/packages/flutter/lib/src/material/icon.dart @@ -4,10 +4,10 @@ import 'package:flutter/widgets.dart'; -import 'colors.dart'; import 'icons.dart'; import 'icon_button.dart'; import 'icon_theme.dart'; +import 'icon_theme_data.dart'; import 'theme.dart'; /// A material design icon. @@ -29,20 +29,21 @@ import 'theme.dart'; /// * [IconButton], for interactive icons. /// * [Icons], for the list of available icons for use with this class. /// * [IconTheme], which provides ambient configuration for icons. +/// * [ImageIcon], for showing icons from [AssetImage]s or other [ImageProvider]s. class Icon extends StatelessWidget { /// Creates an icon. /// /// The [size] and [color] default to the value given by the current [IconTheme]. - const Icon({ + const Icon(this.icon, { Key key, - this.icon, this.size, this.color }) : super(key: key); /// The icon to display. The available icons are described in [Icons]. /// - /// If null, no icon is shown. + /// The icon can be null, in which case the widget will render as an empty + /// space of the specified [size]. final IconData icon; /// The size of the icon in logical pixels. @@ -66,30 +67,17 @@ class Icon extends StatelessWidget { /// [IconTheme], if any. final Color color; - Color _getDefaultColorForBrightness(Brightness brightness) { - switch (brightness) { - case Brightness.dark: - return Colors.white; - case Brightness.light: - return Colors.black; - } - assert(brightness != null); - return null; - } - - Color _getDefaultColor(BuildContext context) { - return IconTheme.of(context)?.color ?? _getDefaultColorForBrightness(Theme.of(context).brightness); - } - @override Widget build(BuildContext context) { - final double iconSize = size ?? IconTheme.of(context)?.size ?? 24.0; + final IconThemeData iconTheme = IconTheme.of(context).fallback(); + + final double iconSize = size ?? iconTheme.size; if (icon == null) return new SizedBox(width: iconSize, height: iconSize); - final double iconOpacity = IconTheme.of(context)?.opacity ?? 1.0; - Color iconColor = color ?? _getDefaultColor(context); + final double iconOpacity = iconTheme.opacity; + Color iconColor = color ?? iconTheme.color; if (iconOpacity != 1.0) iconColor = iconColor.withOpacity(iconColor.opacity * iconOpacity); diff --git a/packages/flutter/lib/src/material/icon_button.dart b/packages/flutter/lib/src/material/icon_button.dart index ab283cd1ff..c432a609bb 100644 --- a/packages/flutter/lib/src/material/icon_button.dart +++ b/packages/flutter/lib/src/material/icon_button.dart @@ -7,6 +7,8 @@ import 'package:meta/meta.dart'; import 'debug.dart'; import 'icon.dart'; +import 'icon_theme.dart'; +import 'icon_theme_data.dart'; import 'icons.dart'; import 'ink_well.dart'; import 'theme.dart'; @@ -40,8 +42,8 @@ class IconButton extends StatelessWidget { /// The [size], [padding], and [alignment] arguments must not be null (though /// they each have default values). /// - /// The [icon] argument must be specified. See [Icons] for a list of icons to - /// use for this argument. + /// The [icon] argument must be specified, and is typically either an [Icon] + /// or an [ImageIcon]. const IconButton({ Key key, this.size: 24.0, @@ -70,13 +72,19 @@ class IconButton extends StatelessWidget { /// This property must not be null. It defaults to [FractionalOffset.center]. final FractionalOffset alignment; - /// The icon to display inside the button, from the list in [Icons]. + /// The icon to display inside the button. + /// + /// The size and color of the icon is configured automatically using an + /// [IconTheme] and therefore does not need to be explicitly given in the + /// icon widget. /// /// This property must not be null. - final IconData icon; + /// + /// See [Icon], [ImageIcon]. + final Widget icon; /// The color to use for the icon inside the button, if the icon is enabled. - /// Defaults to the current color, as defined by [Icon.color]. + /// Defaults to leaving this up to the [icon] widget. /// /// The icon is enabled if [onPressed] is not null. /// @@ -117,10 +125,13 @@ class IconButton extends StatelessWidget { maxHeight: size, child: new Align( alignment: alignment, - child: new Icon( - size: size, - icon: icon, - color: currentColor + child: new IconTheme.merge( + context: context, + data: new IconThemeData( + size: size, + color: currentColor + ), + child: icon ) ) ) diff --git a/packages/flutter/lib/src/material/icon_theme.dart b/packages/flutter/lib/src/material/icon_theme.dart index 3aba060dee..0cbeba17fc 100644 --- a/packages/flutter/lib/src/material/icon_theme.dart +++ b/packages/flutter/lib/src/material/icon_theme.dart @@ -6,8 +6,11 @@ import 'package:flutter/widgets.dart'; import 'package:meta/meta.dart'; import 'icon_theme_data.dart'; +import 'theme.dart'; /// Controls the default color, opacity, and size of icons in a widget subtree. +/// +/// The icon theme is honored by [Icon] and [ImageIcon] widgets. class IconTheme extends InheritedWidget { /// Creates an icon theme that controls the color, opacity, and size of /// descendant widgets. @@ -16,19 +19,39 @@ class IconTheme extends InheritedWidget { IconTheme({ Key key, @required this.data, - Widget child + @required Widget child }) : super(key: key, child: child) { assert(data != null); assert(child != null); } + /// Creates an icon theme that controls the color, opacity, and size of + /// descendant widgets, and merges in the current icon theme, if any. + /// + /// The [context], [data], and [child] arguments must not be null. + factory IconTheme.merge({ + Key key, + @required BuildContext context, + @required IconThemeData data, + @required Widget child + }) { + return new IconTheme( + key: key, + data: IconTheme.of(context).merge(data), + child: child + ); + } + /// The color, opacity, and size to use for icons in this subtree. final IconThemeData data; - /// The data from the closest instance of this class that encloses the given context. + /// The data from the closest instance of this class that encloses the given + /// context. + /// + /// Defaults to the current [ThemeData.iconTheme]. static IconThemeData of(BuildContext context) { IconTheme result = context.inheritFromWidgetOfExactType(IconTheme); - return result?.data; + return result?.data ?? Theme.of(context).iconTheme; } @override diff --git a/packages/flutter/lib/src/material/icon_theme_data.dart b/packages/flutter/lib/src/material/icon_theme_data.dart index 7abbe1acc1..d6dc31fadb 100644 --- a/packages/flutter/lib/src/material/icon_theme_data.dart +++ b/packages/flutter/lib/src/material/icon_theme_data.dart @@ -9,18 +9,61 @@ import 'dart:ui' show Color, hashValues; /// /// Used by [IconTheme] to control the color, opacity, and size of icons in a /// widget subtree. +/// +/// To obtain the current icon theme, use [IconTheme.of]. To convert an icon +/// theme to a version with all the fields filled in, use [fallback]. class IconThemeData { /// Creates an icon theme data. /// /// The opacity applies to both explicit and default icon colors. The value /// is clamped between 0.0 and 1.0. - const IconThemeData({ this.color, double opacity: 1.0, this.size }) : _opacity = opacity; + const IconThemeData({ this.color, double opacity, this.size }) : _opacity = opacity; + + /// Creates a copy of this icon theme but with the given fields replaced with + /// the new values. + IconThemeData copyWith({ Color color, double opacity, double size }) { + return new IconThemeData( + color: color ?? this.color, + opacity: opacity ?? this.opacity, + size: size ?? this.size + ); + } + + /// Returns a new icon theme that matches this icon theme but with some values + /// replaced by the non-null parameters of the given icon theme. If the given + /// icon theme is null, simply returns this icon theme. + IconThemeData merge(IconThemeData other) { + if (other == null) + return this; + return copyWith( + color: other.color, + opacity: other.opacity, + size: other.size + ); + } + + /// Creates an icon theme that is identical to this icon theme but with + /// any null fields filled in. Specific fallbacks can be given, but in their + /// absence, this method defaults to black, fully opaque, and size 24.0. + IconThemeData fallback({ + Color color: const Color(0xFF000000), + double opacity: 1.0, + double size: 24.0 + }) { + if (this.color != null && this.opacity != null && this.size != null) + return this; + return new IconThemeData( + color: this.color ?? color, + opacity: this.opacity ?? opacity, + size: this.size ?? size + ); + } /// The default color for icons. final Color color; /// An opacity to apply to both explicit and default icon colors. - double get opacity => (_opacity ?? 1.0).clamp(0.0, 1.0); + double get opacity => _opacity?.clamp(0.0, 1.0); final double _opacity; /// The default size for icons. @@ -53,8 +96,8 @@ class IconThemeData { List result = []; if (color != null) result.add('color: $color'); - if (opacity != 1.0) - result.add('opacity: $opacity'); + if (_opacity != null) + result.add('opacity: $_opacity'); if (size != null) result.add('size: $size'); if (result.length == 0) diff --git a/packages/flutter/lib/src/material/image_icon.dart b/packages/flutter/lib/src/material/image_icon.dart new file mode 100644 index 0000000000..50c2a3f9be --- /dev/null +++ b/packages/flutter/lib/src/material/image_icon.dart @@ -0,0 +1,98 @@ +// 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/services.dart'; +import 'package:flutter/widgets.dart'; + +import 'icons.dart'; +import 'icon_button.dart'; +import 'icon_theme.dart'; +import 'icon_theme_data.dart'; + +/// An icon that comes from an [ImageProvider], e.g. an [AssetImage]. +/// +/// See also: +/// +/// * [IconButton], for interactive icons. +/// * [IconTheme], which provides ambient configuration for icons. +/// * [Icon] and [Icons], for icons from the material design library. +class ImageIcon extends StatelessWidget { + /// Creates an image icon. + /// + /// The [size] and [color] default to the value given by the current [IconTheme]. + const ImageIcon(this.image, { + Key key, + this.size, + this.color + }) : super(key: key); + + /// The image to display as the icon. + /// + /// The icon can be null, in which case the widget will render as an empty + /// space of the specified [size]. + final ImageProvider image; + + /// The size of the icon in logical pixels. + /// + /// Icons occupy a square with width and height equal to size. + /// + /// Defaults to the current [IconTheme] size, if any. If there is no + /// [IconTheme], or it does not specify an explicit size, then it defaults to + /// 24.0. + final double size; + + /// The color to use when drawing the icon. + /// + /// Defaults to the current [IconTheme] color, if any. If there is + /// no [IconTheme], then it defaults to not recolorizing the image. + /// + /// The image will additionally be adjusted by the opacity of the current + /// [IconTheme], if any. + final Color color; + + @override + Widget build(BuildContext context) { + final IconThemeData iconTheme = IconTheme.of(context).fallback(); + + final double iconSize = size ?? iconTheme.size; + + if (image == null) + return new SizedBox(width: iconSize, height: iconSize); + + final double iconOpacity = iconTheme.opacity; + final Color iconColor = color ?? iconTheme.color; + + Widget result = new Image( + image: image, + width: iconSize, + height: iconSize, + color: iconColor, + fit: ImageFit.scaleDown, + alignment: FractionalOffset.center + ); + + if (iconOpacity != 1.0) { + result = new Opacity( + opacity: iconOpacity, + child: result + ); + } + + return result; + } + + @override + void debugFillDescription(List description) { + super.debugFillDescription(description); + if (image != null) { + description.add('$image'); + } else { + description.add(''); + } + if (size != null) + description.add('size: $size'); + if (color != null) + description.add('color: $color'); + } +} diff --git a/packages/flutter/lib/src/material/input.dart b/packages/flutter/lib/src/material/input.dart index 04514d2f56..df4365fa9f 100644 --- a/packages/flutter/lib/src/material/input.dart +++ b/packages/flutter/lib/src/material/input.dart @@ -8,7 +8,8 @@ import 'package:flutter/widgets.dart'; import 'colors.dart'; import 'debug.dart'; import 'icon.dart'; -import 'icons.dart'; +import 'icon_theme.dart'; +import 'icon_theme_data.dart'; import 'material.dart'; import 'text_selection.dart'; import 'theme.dart'; @@ -50,7 +51,13 @@ class Input extends StatefulWidget { final KeyboardType keyboardType; /// An icon to show adjacent to the input field. - final IconData icon; + /// + /// The size and color of the icon is configured automatically using an + /// [IconTheme] and therefore does not need to be explicitly given in the + /// icon widget. + /// + /// See [Icon], [ImageIcon]. + final Widget icon; /// Text to show above the input field. final String labelText; @@ -224,10 +231,13 @@ class _InputState extends State { new Container( margin: new EdgeInsets.only(right: 16.0, top: iconTop), width: config.isDense ? 40.0 : 48.0, - child: new Icon( - icon: config.icon, - color: focused ? activeColor : Colors.black45, - size: config.isDense ? 18.0 : 24.0 + child: new IconTheme.merge( + context: context, + data: new IconThemeData( + color: focused ? activeColor : Colors.black45, + size: config.isDense ? 18.0 : 24.0 + ), + child: config.icon ) ), new Flexible(child: child) diff --git a/packages/flutter/lib/src/material/paginated_data_table.dart b/packages/flutter/lib/src/material/paginated_data_table.dart index 57d1eb500b..6bd5d2a70c 100644 --- a/packages/flutter/lib/src/material/paginated_data_table.dart +++ b/packages/flutter/lib/src/material/paginated_data_table.dart @@ -14,6 +14,7 @@ import 'card.dart'; import 'data_table.dart'; import 'data_table_source.dart'; import 'drop_down.dart'; +import 'icon.dart'; import 'icon_button.dart'; import 'icon_theme.dart'; import 'icon_theme_data.dart'; @@ -333,16 +334,16 @@ class PaginatedDataTableState extends State { ), new Container(width: 32.0), new IconButton( + icon: new Icon(Icons.chevron_left), padding: EdgeInsets.zero, - icon: Icons.chevron_left, onPressed: _firstRowIndex <= 0 ? null : () { pageTo(math.max(_firstRowIndex - config.rowsPerPage, 0)); } ), new Container(width: 24.0), new IconButton( + icon: new Icon(Icons.chevron_right), padding: EdgeInsets.zero, - icon: Icons.chevron_right, onPressed: (!_rowCountApproximate && (_firstRowIndex + config.rowsPerPage >= _rowCount)) ? null : () { pageTo(_firstRowIndex + config.rowsPerPage); } @@ -360,7 +361,8 @@ class PaginatedDataTableState extends State { // See https://www.google.com/design/spec/components/data-tables.html#data-tables-tables-within-cards style: _selectedRowCount > 0 ? themeData.textTheme.subhead.copyWith(color: themeData.accentColor) : themeData.textTheme.title.copyWith(fontWeight: FontWeight.w400), - child: new IconTheme( + child: new IconTheme.merge( + context: context, data: new IconThemeData( opacity: 0.54 ), @@ -395,7 +397,8 @@ class PaginatedDataTableState extends State { ), new DefaultTextStyle( style: footerTextStyle, - child: new IconTheme( + child: new IconTheme.merge( + context: context, data: new IconThemeData( opacity: 0.54 ), diff --git a/packages/flutter/lib/src/material/popup_menu.dart b/packages/flutter/lib/src/material/popup_menu.dart index 45cf6a23fb..f902216282 100644 --- a/packages/flutter/lib/src/material/popup_menu.dart +++ b/packages/flutter/lib/src/material/popup_menu.dart @@ -160,7 +160,8 @@ class _PopupMenuItemState> extends State { ); if (!config.enabled) { final bool isDark = theme.brightness == Brightness.dark; - item = new IconTheme( + item = new IconTheme.merge( + context: context, data: new IconThemeData(opacity: isDark ? 0.5 : 0.38), child: item ); @@ -244,7 +245,7 @@ class _CheckedPopupMenuItemState extends _PopupMenuItemState extends State> { Widget build(BuildContext context) { if (config.child == null) { return new IconButton( - icon: Icons.more_vert, + icon: new Icon(Icons.more_vert), padding: config.padding, tooltip: config.tooltip, onPressed: () { showButtonMenu(context); } diff --git a/packages/flutter/lib/src/material/scaffold.dart b/packages/flutter/lib/src/material/scaffold.dart index ada2eef7ac..63b560b5bc 100644 --- a/packages/flutter/lib/src/material/scaffold.dart +++ b/packages/flutter/lib/src/material/scaffold.dart @@ -11,8 +11,9 @@ import 'package:flutter/widgets.dart'; import 'app_bar.dart'; import 'bottom_sheet.dart'; import 'drawer.dart'; -import 'icons.dart'; +import 'icon.dart'; import 'icon_button.dart'; +import 'icons.dart'; import 'material.dart'; import 'snack_bar.dart'; @@ -556,7 +557,7 @@ class ScaffoldState extends State { if (leading == null) { if (config.drawer != null) { leading = new IconButton( - icon: Icons.menu, + icon: new Icon(Icons.menu), alignment: FractionalOffset.centerLeft, onPressed: openDrawer, tooltip: 'Open navigation menu' // TODO(ianh): Figure out how to localize this string @@ -565,7 +566,7 @@ class ScaffoldState extends State { _shouldShowBackArrow ??= Navigator.canPop(context); if (_shouldShowBackArrow) { leading = new IconButton( - icon: Icons.arrow_back, + icon: new Icon(Icons.arrow_back), alignment: FractionalOffset.centerLeft, onPressed: () => Navigator.pop(context), tooltip: 'Back' // TODO(ianh): Figure out how to localize this string diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart index bb78cfd982..02c3efdcec 100644 --- a/packages/flutter/lib/src/material/tabs.dart +++ b/packages/flutter/lib/src/material/tabs.dart @@ -14,7 +14,6 @@ import 'app_bar.dart'; import 'colors.dart'; import 'debug.dart'; import 'icon.dart'; -import 'icons.dart'; import 'icon_theme.dart'; import 'icon_theme_data.dart'; import 'ink_well.dart'; @@ -312,8 +311,14 @@ class TabLabel { /// The text to display as the label of the tab. final String text; - /// Data for an [Icon] to display as the label of the tab. - final IconData icon; + /// The icon to display as the label of the tab. + /// + /// The size and color of the icon is configured automatically using an + /// [IconTheme] and therefore does not need to be explicitly given in the + /// icon widget. + /// + /// See [Icon], [ImageIcon]. + final Widget icon; /// Called if [icon] is null to build an icon as a label for this tab. /// @@ -322,6 +327,12 @@ class TabLabel { /// /// Return value must be non-null. final TabLabelIconBuilder iconBuilder; + + /// Whether this label has any text (specified using [text]). + bool get hasText => text != null; + + /// Whether this label has an icon (specified either using [icon] or [iconBuilder]). + bool get hasIcon => icon != null || iconBuilder != null; } class _Tab extends StatelessWidget { @@ -331,7 +342,7 @@ class _Tab extends StatelessWidget { this.label, this.color }) : super(key: key) { - assert(label.text != null || label.icon != null || label.iconBuilder != null); + assert(label.hasText || label.hasIcon); } final VoidCallback onSelected; @@ -350,9 +361,16 @@ class _Tab extends StatelessWidget { } Widget _buildLabelIcon(BuildContext context) { - assert(label.icon != null || label.iconBuilder != null); + assert(label.hasIcon); if (label.icon != null) { - return new Icon(icon: label.icon, color: color); + return new IconTheme.merge( + context: context, + data: new IconThemeData( + color: color, + size: 24.0 + ), + child: label.icon + ); } else { return new SizedBox( width: 24.0, @@ -366,9 +384,9 @@ class _Tab extends StatelessWidget { Widget build(BuildContext context) { assert(debugCheckHasMaterial(context)); Widget labelContent; - if (label.icon == null && label.iconBuilder == null) { + if (!label.hasIcon) { labelContent = _buildLabelText(); - } else if (label.text == null) { + } else if (!label.hasText) { labelContent = _buildLabelIcon(context); } else { labelContent = new Column( @@ -676,7 +694,7 @@ class TabBar extends Scrollable implements AppBarBottomWidget { @override double get bottomHeight { for (TabLabel label in labels.values) { - if (label.text != null && (label.icon != null || label.iconBuilder != null)) + if (label.hasText && label.hasIcon) return _kTextAndIconTabHeight + _kTabIndicatorHeight; } return _kTabHeight + _kTabIndicatorHeight; @@ -922,7 +940,6 @@ class _TabBarState extends ScrollableState> implements TabBarSelect } final TextStyle textStyle = themeData.primaryTextTheme.body2; - final IconThemeData iconTheme = themeData.primaryIconTheme; final Color selectedLabelColor = config.labelColor ?? themeData.primaryTextTheme.body2.color; final Color labelColor = selectedLabelColor.withAlpha(0xB2); // 70% alpha @@ -931,23 +948,20 @@ class _TabBarState extends ScrollableState> implements TabBarSelect int tabIndex = 0; for (TabLabel label in config.labels.values) { tabs.add(_toTab(label, tabIndex++, labelColor, selectedLabelColor)); - if (label.text != null && (label.icon != null || label.iconBuilder != null)) + if (label.hasText && label.hasIcon) textAndIcons = true; } - Widget contents = new IconTheme( - data: iconTheme, - child: new DefaultTextStyle( - style: textStyle, - child: new _TabBarWrapper( - children: tabs, - selectedIndex: _selection?.index, - indicatorColor: indicatorColor, - indicatorRect: _indicatorRect, - textAndIcons: textAndIcons, - isScrollable: config.isScrollable, - onLayoutChanged: _layoutChanged - ) + Widget contents = new DefaultTextStyle( + style: textStyle, + child: new _TabBarWrapper( + children: tabs, + selectedIndex: _selection?.index, + indicatorColor: indicatorColor, + indicatorRect: _indicatorRect, + textAndIcons: textAndIcons, + isScrollable: config.isScrollable, + onLayoutChanged: _layoutChanged ) ); diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart index 1d9f4c663e..6652a7db3a 100644 --- a/packages/flutter/lib/src/material/theme_data.dart +++ b/packages/flutter/lib/src/material/theme_data.dart @@ -89,6 +89,7 @@ class ThemeData { Color errorColor, TextTheme textTheme, TextTheme primaryTextTheme, + IconThemeData iconTheme, IconThemeData primaryIconTheme }) { brightness ??= Brightness.light; @@ -96,6 +97,7 @@ class ThemeData { primarySwatch ??= Colors.blue; primaryColor ??= isDark ? Colors.grey[900] : primarySwatch[500]; primaryColorBrightness ??= Brightness.dark; + final bool primaryIsDark = primaryColorBrightness == Brightness.dark; accentColor ??= isDark ? Colors.tealAccent[200] : primarySwatch[500]; accentColorBrightness ??= Brightness.dark; canvasColor ??= isDark ? Colors.grey[850] : Colors.grey[50]; @@ -115,8 +117,9 @@ class ThemeData { hintColor ??= isDark ? const Color(0x42FFFFFF) : const Color(0x4C000000); errorColor ??= Colors.red[700]; textTheme ??= isDark ? Typography.white : Typography.black; - primaryTextTheme ??= primaryColorBrightness == Brightness.dark ? Typography.white : Typography.black; - primaryIconTheme ??= primaryColorBrightness == Brightness.dark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black); + primaryTextTheme ??= primaryIsDark ? Typography.white : Typography.black; + iconTheme ??= isDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black); + primaryIconTheme ??= primaryIsDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black); return new ThemeData.raw( brightness: brightness, primaryColor: primaryColor, @@ -141,6 +144,7 @@ class ThemeData { errorColor: errorColor, textTheme: textTheme, primaryTextTheme: primaryTextTheme, + iconTheme: iconTheme, primaryIconTheme: primaryIconTheme ); } @@ -175,6 +179,7 @@ class ThemeData { this.errorColor, this.textTheme, this.primaryTextTheme, + this.iconTheme, this.primaryIconTheme }) { assert(brightness != null); @@ -200,6 +205,7 @@ class ThemeData { assert(errorColor != null); assert(textTheme != null); assert(primaryTextTheme != null); + assert(iconTheme != null); assert(primaryIconTheme != null); } @@ -307,6 +313,9 @@ class ThemeData { /// A text theme that contrasts with the primary color. final TextTheme primaryTextTheme; + /// An icon theme that contrasts with the card and canvas colors. + final IconThemeData iconTheme; + /// An icon theme that contrasts with the primary color. final IconThemeData primaryIconTheme; @@ -336,6 +345,7 @@ class ThemeData { errorColor: Color.lerp(begin.errorColor, end.errorColor, t), textTheme: TextTheme.lerp(begin.textTheme, end.textTheme, t), primaryTextTheme: TextTheme.lerp(begin.primaryTextTheme, end.primaryTextTheme, t), + iconTheme: IconThemeData.lerp(begin.iconTheme, end.iconTheme, t), primaryIconTheme: IconThemeData.lerp(begin.primaryIconTheme, end.primaryIconTheme, t) ); } @@ -368,6 +378,7 @@ class ThemeData { (otherData.errorColor == errorColor) && (otherData.textTheme == textTheme) && (otherData.primaryTextTheme == primaryTextTheme) && + (otherData.iconTheme == iconTheme) && (otherData.primaryIconTheme == primaryIconTheme); } @@ -398,6 +409,7 @@ class ThemeData { errorColor, textTheme, primaryTextTheme, + iconTheme, primaryIconTheme ) ); diff --git a/packages/flutter/lib/src/material/two_level_list.dart b/packages/flutter/lib/src/material/two_level_list.dart index b14c056f65..509b6194ce 100644 --- a/packages/flutter/lib/src/material/two_level_list.dart +++ b/packages/flutter/lib/src/material/two_level_list.dart @@ -111,7 +111,8 @@ class _TwoLevelSublistState extends State { ), child: new Column( children: [ - new IconTheme( + new IconTheme.merge( + context: context, data: new IconThemeData(color: _iconColor.evaluate(_easeInAnimation)), child: new TwoLevelListItem( onTap: _handleOnTap, @@ -122,9 +123,7 @@ class _TwoLevelSublistState extends State { ), trailing: new RotationTransition( turns: _iconTurns, - child: new Icon( - icon: Icons.expand_more - ) + child: new Icon(Icons.expand_more) ) ) ), diff --git a/packages/flutter/lib/src/painting/text_style.dart b/packages/flutter/lib/src/painting/text_style.dart index 3d42fd2a76..88798a2a19 100644 --- a/packages/flutter/lib/src/painting/text_style.dart +++ b/packages/flutter/lib/src/painting/text_style.dart @@ -85,18 +85,18 @@ class TextStyle { }) { return new TextStyle( inherit: inherit, - color: color != null ? color : this.color, - fontFamily: fontFamily != null ? fontFamily : this.fontFamily, - fontSize: fontSize != null ? fontSize : this.fontSize, - fontWeight: fontWeight != null ? fontWeight : this.fontWeight, - fontStyle: fontStyle != null ? fontStyle : this.fontStyle, - letterSpacing: letterSpacing != null ? letterSpacing : this.letterSpacing, - wordSpacing: wordSpacing != null ? wordSpacing : this.wordSpacing, - textBaseline: textBaseline != null ? textBaseline : this.textBaseline, - height: height != null ? height : this.height, - decoration: decoration != null ? decoration : this.decoration, - decorationColor: decorationColor != null ? decorationColor : this.decorationColor, - decorationStyle: decorationStyle != null ? decorationStyle : this.decorationStyle + color: color ?? this.color, + fontFamily: fontFamily ?? this.fontFamily, + fontSize: fontSize ?? this.fontSize, + fontWeight: fontWeight ?? this.fontWeight, + fontStyle: fontStyle ?? this.fontStyle, + letterSpacing: letterSpacing ?? this.letterSpacing, + wordSpacing: wordSpacing ?? this.wordSpacing, + textBaseline: textBaseline ?? this.textBaseline, + height: height ?? this.height, + decoration: decoration ?? this.decoration, + decorationColor: decorationColor ?? this.decorationColor, + decorationStyle: decorationStyle ?? this.decorationStyle ); } diff --git a/packages/flutter/test/material/icon_test.dart b/packages/flutter/test/material/icon_test.dart index 73eee1ba62..1383141fc1 100644 --- a/packages/flutter/test/material/icon_test.dart +++ b/packages/flutter/test/material/icon_test.dart @@ -11,7 +11,7 @@ void main() { testWidgets('Icon sizing - no theme, default size', (WidgetTester tester) async { await tester.pumpWidget( new Center( - child: new Icon() + child: new Icon(null) ) ); @@ -23,6 +23,7 @@ void main() { await tester.pumpWidget( new Center( child: new Icon( + null, size: 96.0 ) ) @@ -37,7 +38,7 @@ void main() { new Center( child: new IconTheme( data: new IconThemeData(size: 36.0), - child: new Icon() + child: new Icon(null) ) ) ); @@ -52,6 +53,7 @@ void main() { child: new IconTheme( data: new IconThemeData(size: 36.0), child: new Icon( + null, size: 48.0 ) ) @@ -67,7 +69,7 @@ void main() { new Center( child: new IconTheme( data: new IconThemeData(), - child: new Icon() + child: new Icon(null) ) ) ); diff --git a/packages/flutter/test/material/image_icon_test.dart b/packages/flutter/test/material/image_icon_test.dart new file mode 100644 index 0000000000..5d8bca01f2 --- /dev/null +++ b/packages/flutter/test/material/image_icon_test.dart @@ -0,0 +1,80 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('ImageIcon sizing - no theme, default size', (WidgetTester tester) async { + await tester.pumpWidget( + new Center( + child: new ImageIcon(null) + ) + ); + + RenderBox renderObject = tester.renderObject(find.byType(ImageIcon)); + expect(renderObject.size, equals(const Size.square(24.0))); + }); + + testWidgets('ImageIcon sizing - no theme, explicit size', (WidgetTester tester) async { + await tester.pumpWidget( + new Center( + child: new ImageIcon( + null, + size: 96.0 + ) + ) + ); + + RenderBox renderObject = tester.renderObject(find.byType(ImageIcon)); + expect(renderObject.size, equals(const Size.square(96.0))); + }); + + testWidgets('ImageIcon sizing - sized theme', (WidgetTester tester) async { + await tester.pumpWidget( + new Center( + child: new IconTheme( + data: new IconThemeData(size: 36.0), + child: new ImageIcon(null) + ) + ) + ); + + RenderBox renderObject = tester.renderObject(find.byType(ImageIcon)); + expect(renderObject.size, equals(const Size.square(36.0))); + }); + + testWidgets('ImageIcon sizing - sized theme, explicit size', (WidgetTester tester) async { + await tester.pumpWidget( + new Center( + child: new IconTheme( + data: new IconThemeData(size: 36.0), + child: new ImageIcon( + null, + size: 48.0 + ) + ) + ) + ); + + RenderBox renderObject = tester.renderObject(find.byType(ImageIcon)); + expect(renderObject.size, equals(const Size.square(48.0))); + }); + + testWidgets('ImageIcon sizing - sizeless theme, default size', (WidgetTester tester) async { + await tester.pumpWidget( + new Center( + child: new IconTheme( + data: new IconThemeData(), + child: new ImageIcon(null) + ) + ) + ); + + RenderBox renderObject = tester.renderObject(find.byType(ImageIcon)); + expect(renderObject.size, equals(const Size.square(24.0))); + }); +} diff --git a/packages/flutter/test/widget/icon_test.dart b/packages/flutter/test/widget/icon_test.dart index 2718a21e7d..3baaf06328 100644 --- a/packages/flutter/test/widget/icon_test.dart +++ b/packages/flutter/test/widget/icon_test.dart @@ -13,7 +13,7 @@ void main() { color: Colors.green[500], opacity: 0.5 ), - child: new Icon(icon: Icons.add) + child: new Icon(Icons.add) ) ); Text text = tester.widget(find.byType(Text)); diff --git a/packages/flutter_tools/lib/src/commands/analyze.dart b/packages/flutter_tools/lib/src/commands/analyze.dart index 2e2945115c..6ed4de35bc 100644 --- a/packages/flutter_tools/lib/src/commands/analyze.dart +++ b/packages/flutter_tools/lib/src/commands/analyze.dart @@ -131,9 +131,9 @@ class AnalyzeCommand extends FlutterCommand { pubSpecDirectories.add(currentDirectory); } - //TODO (ianh): Fix the intl package resource generator - //TODO (pq): extract this regexp from the exclude in options - RegExp stockExampleFiles = new RegExp('examples/stocks/lib/.*\.dart\$'); + // TODO(ianh): Fix the intl package resource generator + // TODO(pq): extract this regexp from the exclude in options + RegExp stockExampleFiles = new RegExp('examples/stocks/lib/i18n/.*\.dart\$'); if (flutterRepo) { for (Directory dir in runner.getRepoPackages()) { diff --git a/packages/flutter_tools/templates/create/lib/main.dart.tmpl b/packages/flutter_tools/templates/create/lib/main.dart.tmpl index 85251e5636..2b5c44747f 100644 --- a/packages/flutter_tools/templates/create/lib/main.dart.tmpl +++ b/packages/flutter_tools/templates/create/lib/main.dart.tmpl @@ -48,9 +48,7 @@ class _FlutterDemoState extends State { floatingActionButton: new FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', - child: new Icon( - icon: Icons.add - ) + child: new Icon(Icons.add) ) ); }