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.
This commit is contained in:
Ian Hickson 2016-06-20 21:04:45 -07:00 committed by GitHub
parent e071f0bafd
commit e502e9c8f8
57 changed files with 554 additions and 237 deletions

View File

@ -75,7 +75,7 @@ class ComplexLayoutState extends State<ComplexLayout> {
title: new Text('Advanced Layout'), title: new Text('Advanced Layout'),
actions: <Widget>[ actions: <Widget>[
new IconButton( new IconButton(
icon: Icons.create, icon: new Icon(Icons.create),
tooltip: 'Search', tooltip: 'Search',
onPressed: () { onPressed: () {
print('Pressed search'); print('Pressed search');
@ -162,7 +162,7 @@ class MenuItemWithIcon extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new Row( return new Row(
children: <Widget>[ children: <Widget>[
new Icon(icon: icon), new Icon(icon),
new Padding( new Padding(
padding: new EdgeInsets.only(left: 8.0, right: 8.0), padding: new EdgeInsets.only(left: 8.0, right: 8.0),
child: new Text(title) child: new Text(title)
@ -263,7 +263,10 @@ class IconWithText extends StatelessWidget {
return new Row( return new Row(
mainAxisAlignment: MainAxisAlignment.collapse, mainAxisAlignment: MainAxisAlignment.collapse,
children: <Widget>[ children: <Widget>[
new IconButton(icon: icon, onPressed: () { print('Pressed $title button'); } ), new IconButton(
icon: new Icon(icon),
onPressed: () { print('Pressed $title button'); }
),
new Text(title) new Text(title)
] ]
); );
@ -290,7 +293,7 @@ class MiniIconWithText extends StatelessWidget {
backgroundColor: Theme.of(context).primaryColor, backgroundColor: Theme.of(context).primaryColor,
shape: BoxShape.circle 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) new Text(title, style: Theme.of(context).textTheme.caption)
@ -347,7 +350,7 @@ class UserHeader extends StatelessWidget {
new Row( new Row(
children: <Widget>[ children: <Widget>[
new Text('Yesterday at 11:55 • ', style: Theme.of(context).textTheme.caption), 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( child: new Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[ children: <Widget>[
new IconButton(icon: Icons.edit, onPressed: () { print('Pressed edit button'); }), new IconButton(
new IconButton(icon: Icons.zoom_in, onPressed: () { print('Pressed zoom button'); }) 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( new Row(
children: <Widget>[ children: <Widget>[
new IconButton( new IconButton(
icon: Icons.share, icon: new Icon(Icons.share),
onPressed: () { print('Pressed share'); } onPressed: () { print('Pressed share'); }
), ),
new IconButton( new IconButton(
icon: Icons.event, icon: new Icon(Icons.event),
onPressed: () { print('Pressed event'); } onPressed: () { print('Pressed event'); }
), ),
new Flexible( new Flexible(
@ -555,7 +564,7 @@ class BottomBarButton extends StatelessWidget {
child: new Column( child: new Column(
children: <Widget>[ children: <Widget>[
new IconButton( new IconButton(
icon: icon, icon: new Icon(icon),
onPressed: () { print('Pressed: $title'); } onPressed: () { print('Pressed: $title'); }
), ),
new Text(title, style: Theme.of(context).textTheme.caption) new Text(title, style: Theme.of(context).textTheme.caption)
@ -579,7 +588,7 @@ class GalleryDrawer extends StatelessWidget {
children: <Widget>[ children: <Widget>[
new FancyDrawerHeader(), new FancyDrawerHeader(),
new DrawerItem( new DrawerItem(
icon: Icons.brightness_5, icon: new Icon(Icons.brightness_5),
onPressed: () { _changeTheme(context, true); }, onPressed: () { _changeTheme(context, true); },
selected: ComplexLayoutApp.of(context).lightTheme, selected: ComplexLayoutApp.of(context).lightTheme,
child: new Row( child: new Row(
@ -594,7 +603,7 @@ class GalleryDrawer extends StatelessWidget {
) )
), ),
new DrawerItem( new DrawerItem(
icon: Icons.brightness_7, icon: new Icon(Icons.brightness_7),
onPressed: () { _changeTheme(context, false); }, onPressed: () { _changeTheme(context, false); },
selected: !ComplexLayoutApp.of(context).lightTheme, selected: !ComplexLayoutApp.of(context).lightTheme,
child: new Row( child: new Row(
@ -610,7 +619,7 @@ class GalleryDrawer extends StatelessWidget {
), ),
new Divider(), new Divider(),
new DrawerItem( new DrawerItem(
icon: Icons.hourglass_empty, icon: new Icon(Icons.hourglass_empty),
selected: timeDilation != 1.0, selected: timeDilation != 1.0,
onPressed: () { ComplexLayoutApp.of(context).toggleAnimationSpeed(); }, onPressed: () { ComplexLayoutApp.of(context).toggleAnimationSpeed(); },
child: new Row( child: new Row(

View File

@ -147,7 +147,7 @@ class CardCollectionState extends State<CardCollection> {
buildFontRadioItem("Right-align text", TextAlign.right, _textAlign, _changeTextAlign, icon: Icons.format_align_right, enabled: !_editable), buildFontRadioItem("Right-align text", TextAlign.right, _textAlign, _changeTextAlign, icon: Icons.format_align_right, enabled: !_editable),
new Divider(), new Divider(),
new DrawerItem( new DrawerItem(
icon: Icons.dvr, icon: new Icon(Icons.dvr),
onPressed: () { debugDumpApp(); debugDumpRenderTree(); }, onPressed: () { debugDumpApp(); debugDumpRenderTree(); },
child: new Text('Dump App to Console') child: new Text('Dump App to Console')
), ),
@ -227,7 +227,7 @@ class CardCollectionState extends State<CardCollection> {
Widget buildDrawerColorRadioItem(String label, Map<int, Color> itemValue, Map<int, Color> currentValue, ValueChanged<Map<int, Color>> onChanged, { IconData icon, bool enabled: true }) { Widget buildDrawerColorRadioItem(String label, Map<int, Color> itemValue, Map<int, Color> currentValue, ValueChanged<Map<int, Color>> onChanged, { IconData icon, bool enabled: true }) {
return new DrawerItem( return new DrawerItem(
icon: icon, icon: new Icon(icon),
onPressed: enabled ? () { onChanged(itemValue); } : null, onPressed: enabled ? () { onChanged(itemValue); } : null,
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
@ -244,7 +244,7 @@ class CardCollectionState extends State<CardCollection> {
Widget buildDrawerDirectionRadioItem(String label, DismissDirection itemValue, DismissDirection currentValue, ValueChanged<DismissDirection> onChanged, { IconData icon, bool enabled: true }) { Widget buildDrawerDirectionRadioItem(String label, DismissDirection itemValue, DismissDirection currentValue, ValueChanged<DismissDirection> onChanged, { IconData icon, bool enabled: true }) {
return new DrawerItem( return new DrawerItem(
icon: icon, icon: new Icon(icon),
onPressed: enabled ? () { onChanged(itemValue); } : null, onPressed: enabled ? () { onChanged(itemValue); } : null,
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
@ -261,7 +261,7 @@ class CardCollectionState extends State<CardCollection> {
Widget buildFontRadioItem(String label, TextAlign itemValue, TextAlign currentValue, ValueChanged<TextAlign> onChanged, { IconData icon, bool enabled: true }) { Widget buildFontRadioItem(String label, TextAlign itemValue, TextAlign currentValue, ValueChanged<TextAlign> onChanged, { IconData icon, bool enabled: true }) {
return new DrawerItem( return new DrawerItem(
icon: icon, icon: new Icon(icon),
onPressed: enabled ? () { onChanged(itemValue); } : null, onPressed: enabled ? () { onChanged(itemValue); } : null,
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
@ -351,12 +351,12 @@ class CardCollectionState extends State<CardCollection> {
} }
// TODO(abarth): This icon is wrong in RTL. // 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) if (_dismissDirection == DismissDirection.startToEnd)
leftArrowIcon = new Opacity(opacity: 0.1, child: leftArrowIcon); leftArrowIcon = new Opacity(opacity: 0.1, child: leftArrowIcon);
// TODO(abarth): This icon is wrong in RTL. // 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) if (_dismissDirection == DismissDirection.endToStart)
rightArrowIcon = new Opacity(opacity: 0.1, child: rightArrowIcon); rightArrowIcon = new Opacity(opacity: 0.1, child: rightArrowIcon);

View File

@ -153,7 +153,7 @@ class _FitnessDemoContentsState extends State<_FitnessDemoContents> {
child: new Center( child: new Center(
child: new Column( child: new Column(
children: <Widget>[ children: <Widget>[
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(value, style: new TextStyle(fontSize: 24.0, color: color)),
new Text(description, style: new TextStyle(color: color)) new Text(description, style: new TextStyle(color: color))
] ]

View File

@ -87,13 +87,13 @@ class PageableListAppState extends State<PageableListApp> {
child: new Block(children: <Widget>[ child: new Block(children: <Widget>[
new DrawerHeader(content: new Center(child: new Text('Options'))), new DrawerHeader(content: new Center(child: new Text('Options'))),
new DrawerItem( new DrawerItem(
icon: Icons.more_horiz, icon: new Icon(Icons.more_horiz),
selected: scrollDirection == Axis.horizontal, selected: scrollDirection == Axis.horizontal,
child: new Text('Horizontal Layout'), child: new Text('Horizontal Layout'),
onPressed: switchScrollDirection onPressed: switchScrollDirection
), ),
new DrawerItem( new DrawerItem(
icon: Icons.more_vert, icon: new Icon(Icons.more_vert),
selected: scrollDirection == Axis.vertical, selected: scrollDirection == Axis.vertical,
child: new Text('Vertical Layout'), child: new Text('Vertical Layout'),
onPressed: switchScrollDirection onPressed: switchScrollDirection

View File

@ -168,14 +168,14 @@ class _ButtonsDemoState extends State<ButtonsDemo> {
mainAxisAlignment: MainAxisAlignment.collapse, mainAxisAlignment: MainAxisAlignment.collapse,
children: <Widget>[ children: <Widget>[
new IconButton( new IconButton(
icon: Icons.thumb_up, icon: new Icon(Icons.thumb_up),
onPressed: () { onPressed: () {
setState(() => iconButtonToggle = !iconButtonToggle); setState(() => iconButtonToggle = !iconButtonToggle);
}, },
color: iconButtonToggle ? Theme.of(context).primaryColor : null color: iconButtonToggle ? Theme.of(context).primaryColor : null
), ),
new IconButton( new IconButton(
icon: Icons.thumb_up, icon: new Icon(Icons.thumb_up),
onPressed: null onPressed: null
) )
] ]
@ -189,7 +189,7 @@ class _ButtonsDemoState extends State<ButtonsDemo> {
return new Align( return new Align(
alignment: new FractionalOffset(0.5, 0.4), alignment: new FractionalOffset(0.5, 0.4),
child: new FloatingActionButton( child: new FloatingActionButton(
child: new Icon(icon: Icons.add), child: new Icon(Icons.add),
onPressed: () { onPressed: () {
// Perform some action // Perform some action
} }

View File

@ -23,7 +23,7 @@ class _ContactCategory extends StatelessWidget {
children: <Widget>[ children: <Widget>[
new SizedBox( new SizedBox(
width: 72.0, 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)) new Flexible(child: new Column(children: children))
] ]
@ -59,7 +59,7 @@ class _ContactItem extends StatelessWidget {
if (icon != null) { if (icon != null) {
rowChildren.add(new SizedBox( rowChildren.add(new SizedBox(
width: 72.0, width: 72.0,
child: new IconButton(icon: icon, onPressed: onPressed) child: new IconButton(icon: new Icon(icon), onPressed: onPressed)
)); ));
} }
return new Padding( return new Padding(
@ -99,7 +99,7 @@ class ContactsDemoState extends State<ContactsDemo> {
expandedHeight: _appBarHeight, expandedHeight: _appBarHeight,
actions: <Widget>[ actions: <Widget>[
new IconButton( new IconButton(
icon: Icons.create, icon: new Icon(Icons.create),
tooltip: 'Edit', tooltip: 'Edit',
onPressed: () { onPressed: () {
_scaffoldKey.currentState.showSnackBar(new SnackBar( _scaffoldKey.currentState.showSnackBar(new SnackBar(

View File

@ -39,8 +39,8 @@ class DialogDemoItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
new Icon( new Icon(
icon,
size: 36.0, size: 36.0,
icon: icon,
color: color color: color
), ),
new Padding( new Padding(

View File

@ -57,7 +57,7 @@ class DateTimeItem extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
new Text(new DateFormat('EEE, MMM d yyyy').format(date)), 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( child: new Row(
children: <Widget>[ children: <Widget>[
new Text('$time'), 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<FullScreenDialogDemo> {
return new Scaffold( return new Scaffold(
appBar: new AppBar( appBar: new AppBar(
leading: new IconButton( leading: new IconButton(
icon: Icons.clear, icon: new Icon(Icons.clear),
onPressed: () { handleDismissButton(context); } onPressed: () { handleDismissButton(context); }
), ),
title: new Text('New event'), title: new Text('New event'),

View File

@ -105,7 +105,7 @@ class GridDemoPhotoItem extends StatelessWidget {
title: new Text(photo.title), title: new Text(photo.title),
backgroundColor: Colors.black45, backgroundColor: Colors.black45,
leading: new Icon( leading: new Icon(
icon: icon, icon,
color: Colors.white color: Colors.white
) )
) )
@ -122,7 +122,7 @@ class GridDemoPhotoItem extends StatelessWidget {
title: new Text(photo.title), title: new Text(photo.title),
subtitle: new Text(photo.caption), subtitle: new Text(photo.caption),
trailing: new Icon( trailing: new Icon(
icon: icon, icon,
color: Colors.white color: Colors.white
) )
) )

View File

@ -47,8 +47,8 @@ class IconsDemoState extends State<IconsDemo> {
Widget buildIconButton(double size, IconData icon, bool enabled) { Widget buildIconButton(double size, IconData icon, bool enabled) {
return new IconButton( return new IconButton(
icon: new Icon(icon),
size: size, size: size,
icon: icon,
color: iconColor, color: iconColor,
tooltip: "${enabled ? 'Enabled' : 'Disabled'} icon button", tooltip: "${enabled ? 'Enabled' : 'Disabled'} icon button",
onPressed: enabled ? handleIconButtonPress : null onPressed: enabled ? handleIconButtonPress : null
@ -130,7 +130,7 @@ class IconsDemoState extends State<IconsDemo> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
new Icon( new Icon(
icon: Icons.brightness_7, Icons.brightness_7,
color: iconColor.withAlpha(0x33) // 0.2 * 255 = 0x33 color: iconColor.withAlpha(0x33) // 0.2 * 255 = 0x33
), ),
new Slider( new Slider(
@ -145,7 +145,7 @@ class IconsDemoState extends State<IconsDemo> {
} }
), ),
new Icon( new Icon(
icon: Icons.brightness_7, Icons.brightness_7,
color: iconColor.withAlpha(0xFF) color: iconColor.withAlpha(0xFF)
), ),
] ]

View File

@ -104,13 +104,13 @@ class LeaveBehindDemoState extends State<LeaveBehindDemo> {
background: new Container( background: new Container(
decoration: new BoxDecoration(backgroundColor: theme.primaryColor), decoration: new BoxDecoration(backgroundColor: theme.primaryColor),
child: new ListItem( 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( secondaryBackground: new Container(
decoration: new BoxDecoration(backgroundColor: theme.primaryColor), decoration: new BoxDecoration(backgroundColor: theme.primaryColor),
child: new ListItem( 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( child: new Container(

View File

@ -144,7 +144,7 @@ class ListDemoState extends State<ListDemo> {
leading: _showAvatars ? new CircleAvatar(child: new Text(item)) : null, leading: _showAvatars ? new CircleAvatar(child: new Text(item)) : null,
title: new Text('This item represents $item.'), title: new Text('This item represents $item.'),
subtitle: secondary, 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<ListDemo> {
title: new Text('Scrolling list\n$itemTypeText$layoutText'), title: new Text('Scrolling list\n$itemTypeText$layoutText'),
actions: <Widget>[ actions: <Widget>[
new IconButton( new IconButton(
icon: Icons.sort_by_alpha, icon: new Icon(Icons.sort_by_alpha),
tooltip: 'Sort', tooltip: 'Sort',
onPressed: () { onPressed: () {
setState(() { setState(() {
@ -185,7 +185,7 @@ class ListDemoState extends State<ListDemo> {
} }
), ),
new IconButton( new IconButton(
icon: Icons.more_vert, icon: new Icon(Icons.more_vert),
tooltip: 'Show menu', tooltip: 'Show menu',
onPressed: () { showConfigurationSheet(context); } onPressed: () { showConfigurationSheet(context); }
) )

View File

@ -122,21 +122,21 @@ class MenuDemoState extends State<MenuDemo> {
new PopupMenuItem<String>( new PopupMenuItem<String>(
value: 'Preview', value: 'Preview',
child: new ListItem( child: new ListItem(
leading: new Icon(icon: Icons.visibility), leading: new Icon(Icons.visibility),
title: new Text('Preview') title: new Text('Preview')
) )
), ),
new PopupMenuItem<String>( new PopupMenuItem<String>(
value: 'Share', value: 'Share',
child: new ListItem( child: new ListItem(
leading: new Icon(icon: Icons.person_add), leading: new Icon(Icons.person_add),
title: new Text('Share') title: new Text('Share')
) )
), ),
new PopupMenuItem<String>( new PopupMenuItem<String>(
value: 'Get Link', value: 'Get Link',
child: new ListItem( child: new ListItem(
leading: new Icon(icon: Icons.link), leading: new Icon(Icons.link),
title: new Text('Get link') title: new Text('Get link')
) )
), ),
@ -144,7 +144,7 @@ class MenuDemoState extends State<MenuDemo> {
new PopupMenuItem<String>( new PopupMenuItem<String>(
value: 'Remove', value: 'Remove',
child: new ListItem( child: new ListItem(
leading: new Icon(icon: Icons.delete), leading: new Icon(Icons.delete),
title: new Text('Remove') title: new Text('Remove')
) )
) )

View File

@ -74,7 +74,7 @@ class OverscrollDemoState extends State<OverscrollDemo> {
title: new Text('$indicatorTypeText'), title: new Text('$indicatorTypeText'),
actions: <Widget>[ actions: <Widget>[
new IconButton( new IconButton(
icon: Icons.refresh, icon: new Icon(Icons.refresh),
tooltip: 'Pull to refresh', tooltip: 'Pull to refresh',
onPressed: () { onPressed: () {
setState(() { setState(() {
@ -83,7 +83,7 @@ class OverscrollDemoState extends State<OverscrollDemo> {
} }
), ),
new IconButton( new IconButton(
icon: Icons.play_for_work, icon: new Icon(Icons.play_for_work),
tooltip: 'Over-scroll indicator', tooltip: 'Over-scroll indicator',
onPressed: () { onPressed: () {
setState(() { setState(() {

View File

@ -39,14 +39,14 @@ class PageSelectorDemo extends StatelessWidget {
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
new IconButton( new IconButton(
icon: Icons.chevron_left, icon: new Icon(Icons.chevron_left),
color: color, color: color,
onPressed: () { _handleArrowButtonPress(context, -1); }, onPressed: () { _handleArrowButtonPress(context, -1); },
tooltip: 'Page back' tooltip: 'Page back'
), ),
new TabPageSelector<IconData>(), new TabPageSelector<IconData>(),
new IconButton( new IconButton(
icon: Icons.chevron_right, icon: new Icon(Icons.chevron_right),
color: color, color: color,
onPressed: () { _handleArrowButtonPress(context, 1); }, onPressed: () { _handleArrowButtonPress(context, 1); },
tooltip: 'Page forward' tooltip: 'Page forward'
@ -63,7 +63,7 @@ class PageSelectorDemo extends StatelessWidget {
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),
child: new Card( child: new Card(
child: new Center( child: new Center(
child: new Icon(icon: icon, size: 128.0, color: color) child: new Icon(icon, size: 128.0, color: color)
) )
) )
); );

View File

@ -61,7 +61,7 @@ class _PersistentBottomSheetDemoState extends State<PersistentBottomSheetDemo> {
floatingActionButton: new FloatingActionButton( floatingActionButton: new FloatingActionButton(
onPressed: null, onPressed: null,
backgroundColor: Colors.redAccent[200], backgroundColor: Colors.redAccent[200],
child: new Icon(icon: Icons.add) child: new Icon(Icons.add)
), ),
body: new Center( body: new Center(
child: new RaisedButton( child: new RaisedButton(

View File

@ -66,7 +66,7 @@ class _PestoDemoState extends State<PestoDemo> {
appBar: _buildAppBar(context), appBar: _buildAppBar(context),
drawer: _buildDrawer(context), drawer: _buildDrawer(context),
floatingActionButton: new FloatingActionButton( floatingActionButton: new FloatingActionButton(
child: new Icon(icon: Icons.edit), child: new Icon(Icons.edit),
onPressed: () { } onPressed: () { }
), ),
body: _buildBody(context) body: _buildBody(context)
@ -80,7 +80,7 @@ class _PestoDemoState extends State<PestoDemo> {
expandedHeight: _kAppBarHeight, expandedHeight: _kAppBarHeight,
actions: <Widget>[ actions: <Widget>[
new IconButton( new IconButton(
icon: Icons.search, icon: new Icon(Icons.search),
tooltip: 'Search', tooltip: 'Search',
onPressed: () { onPressed: () {
_scaffoldKey.currentState.showSnackBar(new SnackBar( _scaffoldKey.currentState.showSnackBar(new SnackBar(
@ -305,7 +305,7 @@ class _RecipePageState extends State<_RecipePage> {
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
elevation: 0, elevation: 0,
leading: new IconButton( leading: new IconButton(
icon: Icons.arrow_back, icon: new Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
tooltip: 'Back' tooltip: 'Back'
), ),
@ -364,7 +364,7 @@ class _RecipePageState extends State<_RecipePage> {
new Positioned( new Positioned(
right: 16.0, right: 16.0,
child: new FloatingActionButton( child: new FloatingActionButton(
child: new Icon(icon: isFavorite ? Icons.favorite : Icons.favorite_border), child: new Icon(isFavorite ? Icons.favorite : Icons.favorite_border),
onPressed: _toggleFavorite onPressed: _toggleFavorite
) )
) )
@ -384,7 +384,7 @@ class _RecipePageState extends State<_RecipePage> {
children: <Widget>[ children: <Widget>[
new Padding( new Padding(
padding: const EdgeInsets.only(right: 24.0), 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) new Text(label, style: menuItemStyle)
] ]

View File

@ -78,9 +78,9 @@ class ScrollableTabsDemoState extends State<ScrollableTabsDemo> {
value: (IconData icon) { value: (IconData icon) {
switch(_demoStyle) { switch(_demoStyle) {
case TabsDemoStyle.iconsAndText: case TabsDemoStyle.iconsAndText:
return new TabLabel(text: labels[icon], icon: icon); return new TabLabel(text: labels[icon], icon: new Icon(icon));
case TabsDemoStyle.iconsOnly: case TabsDemoStyle.iconsOnly:
return new TabLabel(icon: icon); return new TabLabel(icon: new Icon(icon));
case TabsDemoStyle.textOnly: case TabsDemoStyle.textOnly:
return new TabLabel(text: labels[icon]); return new TabLabel(text: labels[icon]);
} }
@ -96,7 +96,7 @@ class ScrollableTabsDemoState extends State<ScrollableTabsDemo> {
child:new Card( child:new Card(
child: new Center( child: new Center(
child: new Icon( child: new Icon(
icon: icon, icon,
color: iconColor, color: iconColor,
size: 128.0 size: 128.0
) )

View File

@ -55,7 +55,7 @@ class OrderItem extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 16.0), padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: new Center( child: new Center(
child: new Icon( child: new Icon(
icon: Icons.info_outline, Icons.info_outline,
size: 24.0, size: 24.0,
color: const Color(0xFFFFE0E0) color: const Color(0xFFFFE0E0)
) )
@ -175,7 +175,7 @@ class _OrderPageState extends State<OrderPage> {
}, },
backgroundColor: const Color(0xFF16F0F0), backgroundColor: const Color(0xFF16F0F0),
child: new Icon( child: new Icon(
icon: Icons.add_shopping_cart, Icons.add_shopping_cart,
color: Colors.black color: Colors.black
) )
), ),

View File

@ -100,7 +100,7 @@ class ShrinePageState extends State<ShrinePage> {
), ),
actions: <Widget>[ actions: <Widget>[
new IconButton( new IconButton(
icon: Icons.shopping_cart, icon: new Icon(Icons.shopping_cart),
tooltip: 'Shopping cart', tooltip: 'Shopping cart',
onPressed: () { onPressed: () {
_showShoppingCart(); _showShoppingCart();

View File

@ -15,7 +15,7 @@ class _Page {
Color get labelColor => colors != null ? colors[300] : Colors.grey[300]; Color get labelColor => colors != null ? colors[300] : Colors.grey[300];
bool get fabDefined => colors != null && icon != null; bool get fabDefined => colors != null && icon != null;
Color get fabColor => colors[400]; Color get fabColor => colors[400];
Icon get fabIcon => new Icon(icon: icon); Icon get fabIcon => new Icon(icon);
Key get fabKey => new ValueKey<Color>(fabColor); Key get fabKey => new ValueKey<Color>(fabColor);
} }

View File

@ -31,8 +31,8 @@ class TooltipDemo extends StatelessWidget {
new Tooltip( new Tooltip(
message: 'call icon', message: 'call icon',
child: new Icon( child: new Icon(
Icons.call,
size: 18.0, size: 18.0,
icon: Icons.call,
color: theme.primaryColor color: theme.primaryColor
) )
), ),
@ -42,7 +42,7 @@ class TooltipDemo extends StatelessWidget {
new Center( new Center(
child: new IconButton( child: new IconButton(
size: 48.0, size: 48.0,
icon: Icons.call, icon: new Icon(Icons.call),
color: theme.primaryColor, color: theme.primaryColor,
tooltip: 'Place a phone call', tooltip: 'Place a phone call',
onPressed: () { onPressed: () {

View File

@ -158,7 +158,7 @@ class DemoBottomBar extends StatelessWidget {
children: <Widget>[ children: <Widget>[
new Padding( new Padding(
padding: new EdgeInsets.only(right: 8.0), padding: new EdgeInsets.only(right: 8.0),
child: new Icon(icon: Icons.code) child: new Icon(Icons.code)
), ),
new Text('VIEW CODE') new Text('VIEW CODE')
] ]
@ -170,7 +170,7 @@ class DemoBottomBar extends StatelessWidget {
children: <Widget>[ children: <Widget>[
new Padding( new Padding(
padding: new EdgeInsets.only(right: 8.0), padding: new EdgeInsets.only(right: 8.0),
child: new Icon(icon: Icons.star) child: new Icon(Icons.star)
), ),
new Text('LIVE DEMO') new Text('LIVE DEMO')
] ]
@ -267,7 +267,7 @@ class FullScreenCodeDialogState extends State<FullScreenCodeDialog> {
return new Scaffold( return new Scaffold(
appBar: new AppBar( appBar: new AppBar(
leading: new IconButton( leading: new IconButton(
icon: Icons.clear, icon: new Icon(Icons.clear),
onPressed: () { Navigator.pop(context); } onPressed: () { Navigator.pop(context); }
), ),
title: new Text('Example code') title: new Text('Example code')

View File

@ -36,7 +36,7 @@ class GalleryDrawer extends StatelessWidget {
content: new Center(child: new Text('Flutter gallery')) content: new Center(child: new Text('Flutter gallery'))
), ),
new DrawerItem( new DrawerItem(
icon: Icons.brightness_5, icon: new Icon(Icons.brightness_5),
onPressed: () { onThemeChanged(true); }, onPressed: () { onThemeChanged(true); },
selected: useLightTheme, selected: useLightTheme,
child: new Row( child: new Row(
@ -51,7 +51,7 @@ class GalleryDrawer extends StatelessWidget {
) )
), ),
new DrawerItem( new DrawerItem(
icon: Icons.brightness_7, icon: new Icon(Icons.brightness_7),
onPressed: () { onThemeChanged(false); }, onPressed: () { onThemeChanged(false); },
selected: useLightTheme, selected: useLightTheme,
child: new Row( child: new Row(
@ -67,7 +67,7 @@ class GalleryDrawer extends StatelessWidget {
), ),
new Divider(), new Divider(),
new DrawerItem( new DrawerItem(
icon: Icons.hourglass_empty, icon: new Icon(Icons.hourglass_empty),
selected: timeDilation != 1.0, selected: timeDilation != 1.0,
onPressed: () { onTimeDilationChanged(timeDilation != 1.0 ? 1.0 : 20.0); }, onPressed: () { onTimeDilationChanged(timeDilation != 1.0 ? 1.0 : 20.0); },
child: new Row( child: new Row(
@ -81,7 +81,7 @@ class GalleryDrawer extends StatelessWidget {
) )
), ),
new DrawerItem( new DrawerItem(
icon: Icons.assessment, icon: new Icon(Icons.assessment),
onPressed: () { onShowPerformanceOverlayChanged(!showPerformanceOverlay); }, onPressed: () { onShowPerformanceOverlayChanged(!showPerformanceOverlay); },
selected: showPerformanceOverlay, selected: showPerformanceOverlay,
child: new Row( child: new Row(

View File

@ -83,7 +83,7 @@ bool value;
// Toggleable icon button. // Toggleable icon button.
new IconButton( new IconButton(
icon: Icons.thumb_up, icon: new Icon(Icons.thumb_up),
onPressed: () { onPressed: () {
setState(() => value = !value); setState(() => value = !value);
}, },
@ -99,7 +99,7 @@ new Scaffold(
title: new Text('Demo') title: new Text('Demo')
), ),
floatingActionButton: new FloatingActionButton( floatingActionButton: new FloatingActionButton(
child: new Icon(icon: Icons.add), child: new Icon(Icons.add),
onPressed: null onPressed: null
) )
); );

View File

@ -80,17 +80,17 @@ class GalleryHomeState extends State<GalleryHome> {
type: MaterialListType.oneLine, type: MaterialListType.oneLine,
children: <Widget>[ children: <Widget>[
new TwoLevelSublist( new TwoLevelSublist(
leading: new Icon(icon: Icons.star), leading: new Icon(Icons.star),
title: new Text('Demos'), title: new Text('Demos'),
children: _demoItems children: _demoItems
), ),
new TwoLevelSublist( new TwoLevelSublist(
leading: new Icon(icon: Icons.extension), leading: new Icon(Icons.extension),
title: new Text('Components'), title: new Text('Components'),
children: _componentItems children: _componentItems
), ),
new TwoLevelSublist( new TwoLevelSublist(
leading: new Icon(icon: Icons.color_lens), leading: new Icon(Icons.color_lens),
title: new Text('Style'), title: new Text('Style'),
children: _styleItems children: _styleItems
) )

View File

@ -52,7 +52,7 @@ class AdaptedGridItem extends StatelessWidget {
child: new Text(name) child: new Text(name)
), ),
new IconButton( new IconButton(
icon: Icons.more_vert, icon: new Icon(Icons.more_vert),
onPressed: null onPressed: null
) )
] ]

View File

@ -26,10 +26,11 @@ class _NotImplementedDialog extends StatelessWidget {
content: new Text('This feature has not yet been implemented.'), content: new Text('This feature has not yet been implemented.'),
actions: <Widget>[ actions: <Widget>[
new FlatButton( new FlatButton(
onPressed: () { debugDumpApp(); },
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
new Icon( new Icon(
icon: Icons.dvr, Icons.dvr,
size: 18.0 size: 18.0
), ),
new Container( new Container(
@ -37,14 +38,13 @@ class _NotImplementedDialog extends StatelessWidget {
), ),
new Text('DUMP APP TO CONSOLE'), new Text('DUMP APP TO CONSOLE'),
] ]
), )
onPressed: () { debugDumpApp(); }
), ),
new FlatButton( new FlatButton(
child: new Text('OH WELL'),
onPressed: () { onPressed: () {
Navigator.pop(context, false); Navigator.pop(context, false);
} },
child: new Text('OH WELL')
) )
] ]
); );
@ -126,12 +126,12 @@ class StockHomeState extends State<StockHome> {
child: new Block(children: <Widget>[ child: new Block(children: <Widget>[
new DrawerHeader(content: new Center(child: new Text('Stocks'))), new DrawerHeader(content: new Center(child: new Text('Stocks'))),
new DrawerItem( new DrawerItem(
icon: Icons.assessment, icon: new Icon(Icons.assessment),
selected: true, selected: true,
child: new Text('Stock List') child: new Text('Stock List')
), ),
new DrawerItem( new DrawerItem(
icon: Icons.account_balance, icon: new Icon(Icons.account_balance),
onPressed: () { onPressed: () {
showDialog( showDialog(
context: context, context: context,
@ -158,7 +158,7 @@ class StockHomeState extends State<StockHome> {
child: new Text('Account Balance') child: new Text('Account Balance')
), ),
new DrawerItem( new DrawerItem(
icon: Icons.dvr, icon: new Icon(Icons.dvr),
onPressed: () { onPressed: () {
try { try {
debugDumpApp(); debugDumpApp();
@ -173,7 +173,7 @@ class StockHomeState extends State<StockHome> {
), ),
new Divider(), new Divider(),
new DrawerItem( new DrawerItem(
icon: Icons.thumb_up, icon: new Icon(Icons.thumb_up),
onPressed: () => _handleStockModeChange(StockMode.optimistic), onPressed: () => _handleStockModeChange(StockMode.optimistic),
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
@ -183,7 +183,7 @@ class StockHomeState extends State<StockHome> {
) )
), ),
new DrawerItem( new DrawerItem(
icon: Icons.thumb_down, icon: new Icon(Icons.thumb_down),
onPressed: () => _handleStockModeChange(StockMode.pessimistic), onPressed: () => _handleStockModeChange(StockMode.pessimistic),
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
@ -194,11 +194,11 @@ class StockHomeState extends State<StockHome> {
), ),
new Divider(), new Divider(),
new DrawerItem( new DrawerItem(
icon: Icons.settings, icon: new Icon(Icons.settings),
onPressed: _handleShowSettings, onPressed: _handleShowSettings,
child: new Text('Settings')), child: new Text('Settings')),
new DrawerItem( new DrawerItem(
icon: Icons.help, icon: new Icon(Icons.help),
child: new Text('Help & Feedback')) child: new Text('Help & Feedback'))
]) ])
); );
@ -214,7 +214,7 @@ class StockHomeState extends State<StockHome> {
title: new Text(StockStrings.of(context).title()), title: new Text(StockStrings.of(context).title()),
actions: <Widget>[ actions: <Widget>[
new IconButton( new IconButton(
icon: Icons.search, icon: new Icon(Icons.search),
onPressed: _handleSearchBegin, onPressed: _handleSearchBegin,
tooltip: 'Search' tooltip: 'Search'
), ),
@ -307,7 +307,7 @@ class StockHomeState extends State<StockHome> {
Widget buildSearchBar() { Widget buildSearchBar() {
return new AppBar( return new AppBar(
leading: new IconButton( leading: new IconButton(
icon: Icons.arrow_back, icon: new Icon(Icons.arrow_back),
color: Theme.of(context).accentColor, color: Theme.of(context).accentColor,
onPressed: _handleSearchEnd, onPressed: _handleSearchEnd,
tooltip: 'Back' tooltip: 'Back'
@ -332,7 +332,7 @@ class StockHomeState extends State<StockHome> {
Widget buildFloatingActionButton() { Widget buildFloatingActionButton() {
return new FloatingActionButton( return new FloatingActionButton(
tooltip: 'Create company', tooltip: 'Create company',
child: new Icon(icon: Icons.add), child: new Icon(Icons.add),
backgroundColor: Colors.redAccent[200], backgroundColor: Colors.redAccent[200],
onPressed: _handleCreateCompany onPressed: _handleCreateCompany
); );

View File

@ -104,7 +104,7 @@ class StockSettingsState extends State<StockSettings> {
Widget buildSettingsPane(BuildContext context) { Widget buildSettingsPane(BuildContext context) {
List<Widget> rows = <Widget>[ List<Widget> rows = <Widget>[
new DrawerItem( new DrawerItem(
icon: Icons.thumb_up, icon: new Icon(Icons.thumb_up),
onPressed: () => _confirmOptimismChange(), onPressed: () => _confirmOptimismChange(),
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
@ -117,7 +117,7 @@ class StockSettingsState extends State<StockSettings> {
) )
), ),
new DrawerItem( new DrawerItem(
icon: Icons.backup, icon: new Icon(Icons.backup),
onPressed: () { _handleBackupChanged(!(config.configuration.backupMode == BackupMode.enabled)); }, onPressed: () { _handleBackupChanged(!(config.configuration.backupMode == BackupMode.enabled)); },
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
@ -130,7 +130,7 @@ class StockSettingsState extends State<StockSettings> {
) )
), ),
new DrawerItem( new DrawerItem(
icon: Icons.picture_in_picture, icon: new Icon(Icons.picture_in_picture),
onPressed: () { _handleShowPerformanceOverlayChanged(!config.configuration.showPerformanceOverlay); }, onPressed: () { _handleShowPerformanceOverlayChanged(!config.configuration.showPerformanceOverlay); },
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
@ -143,7 +143,7 @@ class StockSettingsState extends State<StockSettings> {
) )
), ),
new DrawerItem( new DrawerItem(
icon: Icons.accessibility, icon: new Icon(Icons.accessibility),
onPressed: () { _handleShowSemanticsDebuggerChanged(!config.configuration.showSemanticsDebugger); }, onPressed: () { _handleShowSemanticsDebuggerChanged(!config.configuration.showSemanticsDebugger); },
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
@ -158,9 +158,9 @@ class StockSettingsState extends State<StockSettings> {
]; ];
assert(() { assert(() {
// material grid and size construction lines are only available in checked mode // material grid and size construction lines are only available in checked mode
rows.addAll([ rows.addAll(<Widget>[
new DrawerItem( new DrawerItem(
icon: Icons.border_clear, icon: new Icon(Icons.border_clear),
onPressed: () { _handleShowGridChanged(!config.configuration.debugShowGrid); }, onPressed: () { _handleShowGridChanged(!config.configuration.debugShowGrid); },
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
@ -173,7 +173,7 @@ class StockSettingsState extends State<StockSettings> {
) )
), ),
new DrawerItem( new DrawerItem(
icon: Icons.border_all, icon: new Icon(Icons.border_all),
onPressed: () { _handleShowSizesChanged(!config.configuration.debugShowSizes); }, onPressed: () { _handleShowSizesChanged(!config.configuration.debugShowSizes); },
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
@ -186,7 +186,7 @@ class StockSettingsState extends State<StockSettings> {
) )
), ),
new DrawerItem( new DrawerItem(
icon: Icons.format_color_text, icon: new Icon(Icons.format_color_text),
onPressed: () { _handleShowBaselinesChanged(!config.configuration.debugShowBaselines); }, onPressed: () { _handleShowBaselinesChanged(!config.configuration.debugShowBaselines); },
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
@ -199,7 +199,7 @@ class StockSettingsState extends State<StockSettings> {
) )
), ),
new DrawerItem( new DrawerItem(
icon: Icons.filter_none, icon: new Icon(Icons.filter_none),
onPressed: () { _handleShowLayersChanged(!config.configuration.debugShowLayers); }, onPressed: () { _handleShowLayersChanged(!config.configuration.debugShowLayers); },
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
@ -212,7 +212,7 @@ class StockSettingsState extends State<StockSettings> {
) )
), ),
new DrawerItem( new DrawerItem(
icon: Icons.mouse, icon: new Icon(Icons.mouse),
onPressed: () { _handleShowPointersChanged(!config.configuration.debugShowPointers); }, onPressed: () { _handleShowPointersChanged(!config.configuration.debugShowPointers); },
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
@ -225,7 +225,7 @@ class StockSettingsState extends State<StockSettings> {
) )
), ),
new DrawerItem( new DrawerItem(
icon: Icons.gradient, icon: new Icon(Icons.gradient),
onPressed: () { _handleShowRainbowChanged(!config.configuration.debugShowRainbow); }, onPressed: () { _handleShowRainbowChanged(!config.configuration.debugShowRainbow); },
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[

View File

@ -42,6 +42,7 @@ export 'src/material/icon_button.dart';
export 'src/material/icon_theme.dart'; export 'src/material/icon_theme.dart';
export 'src/material/icon_theme_data.dart'; export 'src/material/icon_theme_data.dart';
export 'src/material/icons.dart'; export 'src/material/icons.dart';
export 'src/material/image_icon.dart';
export 'src/material/ink_well.dart'; export 'src/material/ink_well.dart';
export 'src/material/input.dart'; export 'src/material/input.dart';
export 'src/material/list.dart'; export 'src/material/list.dart';

View File

@ -46,6 +46,10 @@ abstract class AppBarBottomWidget extends Widget {
/// AppBar's [collapsedHeight] and [bottomHeight] define how small the app bar /// AppBar's [collapsedHeight] and [bottomHeight] define how small the app bar
/// will become when the application is scrolled. /// 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: /// See also:
/// ///
/// * [Scaffold] /// * [Scaffold]
@ -126,6 +130,10 @@ class AppBar extends StatelessWidget {
/// tandem with [backgroundColor]. /// tandem with [backgroundColor].
/// ///
/// Defaults to [ThemeData.brightness]. /// 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; final Brightness brightness;
/// The typographic style to use for text in the app bar. /// 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 double statusBarHeight = MediaQuery.of(context).padding.top;
final ThemeData theme = Theme.of(context); 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 centerStyle = textTheme?.title ?? theme.primaryTextTheme.title;
TextStyle sideStyle = textTheme?.body1 ?? theme.primaryTextTheme.body1; TextStyle sideStyle = textTheme?.body1 ?? theme.primaryTextTheme.body1;
@ -223,13 +231,9 @@ class AppBar extends StatelessWidget {
centerStyle = centerStyle.copyWith(color: centerStyle.color.withOpacity(opacity)); centerStyle = centerStyle.copyWith(color: centerStyle.color.withOpacity(opacity));
if (sideStyle?.color != null) if (sideStyle?.color != null)
sideStyle = sideStyle.copyWith(color: sideStyle.color.withOpacity(opacity)); sideStyle = sideStyle.copyWith(color: sideStyle.color.withOpacity(opacity));
iconTheme = iconTheme.copyWith(
if (iconTheme != null) { opacity: opacity * (iconTheme.opacity ?? 1.0)
iconTheme = new IconThemeData( );
opacity: opacity * iconTheme.opacity,
color: iconTheme.color
);
}
} }
final List<Widget> toolBarRow = <Widget>[]; final List<Widget> toolBarRow = <Widget>[];
@ -256,7 +260,8 @@ class AppBar extends StatelessWidget {
Widget appBar = new SizedBox( Widget appBar = new SizedBox(
height: kToolBarHeight, height: kToolBarHeight,
child: new IconTheme( child: new IconTheme.merge(
context: context,
data: iconTheme, data: iconTheme,
child: new DefaultTextStyle( child: new DefaultTextStyle(
style: sideStyle, style: sideStyle,

View File

@ -245,7 +245,8 @@ class _MaterialButtonState extends State<MaterialButton> {
final ButtonTheme buttonTheme = ButtonTheme.of(context); final ButtonTheme buttonTheme = ButtonTheme.of(context);
final double height = config.height ?? buttonTheme.height; final double height = config.height ?? buttonTheme.height;
final int elevation = (_highlight ? config.highlightElevation : config.elevation) ?? 0; final int elevation = (_highlight ? config.highlightElevation : config.elevation) ?? 0;
Widget contents = new IconTheme( Widget contents = new IconTheme.merge(
context: context,
data: new IconThemeData( data: new IconThemeData(
color: textColor color: textColor
), ),

View File

@ -96,7 +96,7 @@ class Chip extends StatelessWidget {
child: new Container( child: new Container(
padding: const EdgeInsets.symmetric(horizontal: 4.0), padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: new Icon( child: new Icon(
icon: Icons.cancel, Icons.cancel,
size: 18.0, size: 18.0,
color: Colors.black54 color: Colors.black54
) )

View File

@ -436,6 +436,7 @@ class DataTable extends StatelessWidget {
} }
Widget _buildDataCell({ Widget _buildDataCell({
BuildContext context,
EdgeInsets padding, EdgeInsets padding,
Widget label, Widget label,
bool numeric, bool numeric,
@ -445,7 +446,7 @@ class DataTable extends StatelessWidget {
VoidCallback onSelectChanged VoidCallback onSelectChanged
}) { }) {
if (showEditIcon) { 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 Flexible(child: label);
label = new Row(children: numeric ? <Widget>[ icon, label ] : <Widget>[ label, icon ]); label = new Row(children: numeric ? <Widget>[ icon, label ] : <Widget>[ label, icon ]);
} }
@ -460,7 +461,8 @@ class DataTable extends StatelessWidget {
fontSize: 13.0, 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 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( data: new IconThemeData(
color: Colors.black54 color: Colors.black54
), ),
@ -561,6 +563,7 @@ class DataTable extends StatelessWidget {
for (DataRow row in rows) { for (DataRow row in rows) {
DataCell cell = row.cells[dataColumnIndex]; DataCell cell = row.cells[dataColumnIndex];
tableRows[rowIndex].children[displayColumnIndex] = _buildDataCell( tableRows[rowIndex].children[displayColumnIndex] = _buildDataCell(
context: context,
padding: padding, padding: padding,
label: cell.widget, label: cell.widget,
numeric: column.numeric, numeric: column.numeric,
@ -765,7 +768,7 @@ class _SortArrowState extends State<_SortArrow> {
..setTranslationRaw(0.0, _kArrowIconBaselineOffset, 0.0), ..setTranslationRaw(0.0, _kArrowIconBaselineOffset, 0.0),
alignment: FractionalOffset.center, alignment: FractionalOffset.center,
child: new Icon( child: new Icon(
icon: Icons.arrow_downward, Icons.arrow_downward,
size: _kArrowIconSize, size: _kArrowIconSize,
color: Colors.black87 color: Colors.black87
) )

View File

@ -12,8 +12,9 @@ import 'package:meta/meta.dart';
import 'colors.dart'; import 'colors.dart';
import 'debug.dart'; import 'debug.dart';
import 'icons.dart'; import 'icon.dart';
import 'icon_button.dart'; import 'icon_button.dart';
import 'icons.dart';
import 'ink_well.dart'; import 'ink_well.dart';
import 'theme.dart'; import 'theme.dart';
import 'typography.dart'; import 'typography.dart';
@ -471,7 +472,7 @@ class _MonthPickerState extends State<MonthPicker> {
top: 0.0, top: 0.0,
left: 8.0, left: 8.0,
child: new IconButton( child: new IconButton(
icon: Icons.chevron_left, icon: new Icon(Icons.chevron_left),
tooltip: 'Previous month', tooltip: 'Previous month',
onPressed: _handlePreviousMonth onPressed: _handlePreviousMonth
) )
@ -480,7 +481,7 @@ class _MonthPickerState extends State<MonthPicker> {
top: 0.0, top: 0.0,
right: 8.0, right: 8.0,
child: new IconButton( child: new IconButton(
icon: Icons.chevron_right, icon: new Icon(Icons.chevron_right),
tooltip: 'Next month', tooltip: 'Next month',
onPressed: _handleNextMonth onPressed: _handleNextMonth
) )

View File

@ -8,7 +8,9 @@ import 'colors.dart';
import 'constants.dart'; import 'constants.dart';
import 'debug.dart'; import 'debug.dart';
import 'icon.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 'ink_well.dart';
import 'theme.dart'; import 'theme.dart';
@ -36,7 +38,13 @@ class DrawerItem extends StatelessWidget {
}) : super(key: key); }) : super(key: key);
/// The icon to display before the child widget. /// 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. /// The widget below this widget in the tree.
final Widget child; final Widget child;
@ -94,9 +102,13 @@ class DrawerItem extends StatelessWidget {
children.add( children.add(
new Padding( new Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0), padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: new Icon( child: new IconTheme.merge(
icon: icon, context: context,
color: _getIconColor(themeData) data: new IconThemeData(
color: _getIconColor(themeData),
size: 24.0
),
child: icon
) )
) )
); );

View File

@ -495,7 +495,7 @@ class _DropDownButtonState<T> extends State<DropDownButton<T>> {
alignment: FractionalOffset.centerLeft, alignment: FractionalOffset.centerLeft,
children: config.items children: config.items
), ),
new Icon(icon: Icons.arrow_drop_down, size: config.iconSize) new Icon(Icons.arrow_drop_down, size: config.iconSize)
] ]
) )
); );

View File

@ -104,13 +104,14 @@ class _FloatingActionButtonState extends State<FloatingActionButton> {
Color iconColor = Colors.white; Color iconColor = Colors.white;
Color materialColor = config.backgroundColor; Color materialColor = config.backgroundColor;
if (materialColor == null) { if (materialColor == null) {
ThemeData themeData = Theme.of(context); final ThemeData themeData = Theme.of(context);
materialColor = themeData.accentColor; materialColor = themeData.accentColor;
iconColor = themeData.accentColorBrightness == Brightness.dark ? Colors.white : Colors.black; iconColor = themeData.accentColorBrightness == Brightness.dark ? Colors.white : Colors.black;
} }
Widget result = new Center( Widget result = new Center(
child: new IconTheme( child: new IconTheme.merge(
context: context,
data: new IconThemeData(color: iconColor), data: new IconThemeData(color: iconColor),
child: config.child child: config.child
) )

View File

@ -118,7 +118,8 @@ class GridTileBar extends StatelessWidget {
return new Container( return new Container(
padding: padding, padding: padding,
decoration: decoration, decoration: decoration,
child: new IconTheme( child: new IconTheme.merge(
context: context,
data: new IconThemeData(color: Colors.white), data: new IconThemeData(color: Colors.white),
child: new Row( child: new Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,

View File

@ -4,10 +4,10 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'colors.dart';
import 'icons.dart'; import 'icons.dart';
import 'icon_button.dart'; import 'icon_button.dart';
import 'icon_theme.dart'; import 'icon_theme.dart';
import 'icon_theme_data.dart';
import 'theme.dart'; import 'theme.dart';
/// A material design icon. /// A material design icon.
@ -29,20 +29,21 @@ import 'theme.dart';
/// * [IconButton], for interactive icons. /// * [IconButton], for interactive icons.
/// * [Icons], for the list of available icons for use with this class. /// * [Icons], for the list of available icons for use with this class.
/// * [IconTheme], which provides ambient configuration for icons. /// * [IconTheme], which provides ambient configuration for icons.
/// * [ImageIcon], for showing icons from [AssetImage]s or other [ImageProvider]s.
class Icon extends StatelessWidget { class Icon extends StatelessWidget {
/// Creates an icon. /// Creates an icon.
/// ///
/// The [size] and [color] default to the value given by the current [IconTheme]. /// The [size] and [color] default to the value given by the current [IconTheme].
const Icon({ const Icon(this.icon, {
Key key, Key key,
this.icon,
this.size, this.size,
this.color this.color
}) : super(key: key); }) : super(key: key);
/// The icon to display. The available icons are described in [Icons]. /// 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; final IconData icon;
/// The size of the icon in logical pixels. /// The size of the icon in logical pixels.
@ -66,30 +67,17 @@ class Icon extends StatelessWidget {
/// [IconTheme], if any. /// [IconTheme], if any.
final Color color; 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 @override
Widget build(BuildContext context) { 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) if (icon == null)
return new SizedBox(width: iconSize, height: iconSize); return new SizedBox(width: iconSize, height: iconSize);
final double iconOpacity = IconTheme.of(context)?.opacity ?? 1.0; final double iconOpacity = iconTheme.opacity;
Color iconColor = color ?? _getDefaultColor(context); Color iconColor = color ?? iconTheme.color;
if (iconOpacity != 1.0) if (iconOpacity != 1.0)
iconColor = iconColor.withOpacity(iconColor.opacity * iconOpacity); iconColor = iconColor.withOpacity(iconColor.opacity * iconOpacity);

View File

@ -7,6 +7,8 @@ import 'package:meta/meta.dart';
import 'debug.dart'; import 'debug.dart';
import 'icon.dart'; import 'icon.dart';
import 'icon_theme.dart';
import 'icon_theme_data.dart';
import 'icons.dart'; import 'icons.dart';
import 'ink_well.dart'; import 'ink_well.dart';
import 'theme.dart'; import 'theme.dart';
@ -40,8 +42,8 @@ class IconButton extends StatelessWidget {
/// The [size], [padding], and [alignment] arguments must not be null (though /// The [size], [padding], and [alignment] arguments must not be null (though
/// they each have default values). /// they each have default values).
/// ///
/// The [icon] argument must be specified. See [Icons] for a list of icons to /// The [icon] argument must be specified, and is typically either an [Icon]
/// use for this argument. /// or an [ImageIcon].
const IconButton({ const IconButton({
Key key, Key key,
this.size: 24.0, this.size: 24.0,
@ -70,13 +72,19 @@ class IconButton extends StatelessWidget {
/// This property must not be null. It defaults to [FractionalOffset.center]. /// This property must not be null. It defaults to [FractionalOffset.center].
final FractionalOffset alignment; 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. /// 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. /// 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. /// The icon is enabled if [onPressed] is not null.
/// ///
@ -117,10 +125,13 @@ class IconButton extends StatelessWidget {
maxHeight: size, maxHeight: size,
child: new Align( child: new Align(
alignment: alignment, alignment: alignment,
child: new Icon( child: new IconTheme.merge(
size: size, context: context,
icon: icon, data: new IconThemeData(
color: currentColor size: size,
color: currentColor
),
child: icon
) )
) )
) )

View File

@ -6,8 +6,11 @@ import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'icon_theme_data.dart'; import 'icon_theme_data.dart';
import 'theme.dart';
/// Controls the default color, opacity, and size of icons in a widget subtree. /// 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 { class IconTheme extends InheritedWidget {
/// Creates an icon theme that controls the color, opacity, and size of /// Creates an icon theme that controls the color, opacity, and size of
/// descendant widgets. /// descendant widgets.
@ -16,19 +19,39 @@ class IconTheme extends InheritedWidget {
IconTheme({ IconTheme({
Key key, Key key,
@required this.data, @required this.data,
Widget child @required Widget child
}) : super(key: key, child: child) { }) : super(key: key, child: child) {
assert(data != null); assert(data != null);
assert(child != 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. /// The color, opacity, and size to use for icons in this subtree.
final IconThemeData data; 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) { static IconThemeData of(BuildContext context) {
IconTheme result = context.inheritFromWidgetOfExactType(IconTheme); IconTheme result = context.inheritFromWidgetOfExactType(IconTheme);
return result?.data; return result?.data ?? Theme.of(context).iconTheme;
} }
@override @override

View File

@ -9,18 +9,61 @@ import 'dart:ui' show Color, hashValues;
/// ///
/// Used by [IconTheme] to control the color, opacity, and size of icons in a /// Used by [IconTheme] to control the color, opacity, and size of icons in a
/// widget subtree. /// 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 { class IconThemeData {
/// Creates an icon theme data. /// Creates an icon theme data.
/// ///
/// The opacity applies to both explicit and default icon colors. The value /// The opacity applies to both explicit and default icon colors. The value
/// is clamped between 0.0 and 1.0. /// 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. /// The default color for icons.
final Color color; final Color color;
/// An opacity to apply to both explicit and default icon colors. /// 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; final double _opacity;
/// The default size for icons. /// The default size for icons.
@ -53,8 +96,8 @@ class IconThemeData {
List<String> result = <String>[]; List<String> result = <String>[];
if (color != null) if (color != null)
result.add('color: $color'); result.add('color: $color');
if (opacity != 1.0) if (_opacity != null)
result.add('opacity: $opacity'); result.add('opacity: $_opacity');
if (size != null) if (size != null)
result.add('size: $size'); result.add('size: $size');
if (result.length == 0) if (result.length == 0)

View File

@ -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<String> description) {
super.debugFillDescription(description);
if (image != null) {
description.add('$image');
} else {
description.add('<empty>');
}
if (size != null)
description.add('size: $size');
if (color != null)
description.add('color: $color');
}
}

View File

@ -8,7 +8,8 @@ import 'package:flutter/widgets.dart';
import 'colors.dart'; import 'colors.dart';
import 'debug.dart'; import 'debug.dart';
import 'icon.dart'; import 'icon.dart';
import 'icons.dart'; import 'icon_theme.dart';
import 'icon_theme_data.dart';
import 'material.dart'; import 'material.dart';
import 'text_selection.dart'; import 'text_selection.dart';
import 'theme.dart'; import 'theme.dart';
@ -50,7 +51,13 @@ class Input extends StatefulWidget {
final KeyboardType keyboardType; final KeyboardType keyboardType;
/// An icon to show adjacent to the input field. /// 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. /// Text to show above the input field.
final String labelText; final String labelText;
@ -224,10 +231,13 @@ class _InputState extends State<Input> {
new Container( new Container(
margin: new EdgeInsets.only(right: 16.0, top: iconTop), margin: new EdgeInsets.only(right: 16.0, top: iconTop),
width: config.isDense ? 40.0 : 48.0, width: config.isDense ? 40.0 : 48.0,
child: new Icon( child: new IconTheme.merge(
icon: config.icon, context: context,
color: focused ? activeColor : Colors.black45, data: new IconThemeData(
size: config.isDense ? 18.0 : 24.0 color: focused ? activeColor : Colors.black45,
size: config.isDense ? 18.0 : 24.0
),
child: config.icon
) )
), ),
new Flexible(child: child) new Flexible(child: child)

View File

@ -14,6 +14,7 @@ import 'card.dart';
import 'data_table.dart'; import 'data_table.dart';
import 'data_table_source.dart'; import 'data_table_source.dart';
import 'drop_down.dart'; import 'drop_down.dart';
import 'icon.dart';
import 'icon_button.dart'; import 'icon_button.dart';
import 'icon_theme.dart'; import 'icon_theme.dart';
import 'icon_theme_data.dart'; import 'icon_theme_data.dart';
@ -333,16 +334,16 @@ class PaginatedDataTableState extends State<PaginatedDataTable> {
), ),
new Container(width: 32.0), new Container(width: 32.0),
new IconButton( new IconButton(
icon: new Icon(Icons.chevron_left),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
icon: Icons.chevron_left,
onPressed: _firstRowIndex <= 0 ? null : () { onPressed: _firstRowIndex <= 0 ? null : () {
pageTo(math.max(_firstRowIndex - config.rowsPerPage, 0)); pageTo(math.max(_firstRowIndex - config.rowsPerPage, 0));
} }
), ),
new Container(width: 24.0), new Container(width: 24.0),
new IconButton( new IconButton(
icon: new Icon(Icons.chevron_right),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
icon: Icons.chevron_right,
onPressed: (!_rowCountApproximate && (_firstRowIndex + config.rowsPerPage >= _rowCount)) ? null : () { onPressed: (!_rowCountApproximate && (_firstRowIndex + config.rowsPerPage >= _rowCount)) ? null : () {
pageTo(_firstRowIndex + config.rowsPerPage); pageTo(_firstRowIndex + config.rowsPerPage);
} }
@ -360,7 +361,8 @@ class PaginatedDataTableState extends State<PaginatedDataTable> {
// See https://www.google.com/design/spec/components/data-tables.html#data-tables-tables-within-cards // 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) style: _selectedRowCount > 0 ? themeData.textTheme.subhead.copyWith(color: themeData.accentColor)
: themeData.textTheme.title.copyWith(fontWeight: FontWeight.w400), : themeData.textTheme.title.copyWith(fontWeight: FontWeight.w400),
child: new IconTheme( child: new IconTheme.merge(
context: context,
data: new IconThemeData( data: new IconThemeData(
opacity: 0.54 opacity: 0.54
), ),
@ -395,7 +397,8 @@ class PaginatedDataTableState extends State<PaginatedDataTable> {
), ),
new DefaultTextStyle( new DefaultTextStyle(
style: footerTextStyle, style: footerTextStyle,
child: new IconTheme( child: new IconTheme.merge(
context: context,
data: new IconThemeData( data: new IconThemeData(
opacity: 0.54 opacity: 0.54
), ),

View File

@ -160,7 +160,8 @@ class _PopupMenuItemState<T extends PopupMenuItem<dynamic>> extends State<T> {
); );
if (!config.enabled) { if (!config.enabled) {
final bool isDark = theme.brightness == Brightness.dark; 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), data: new IconThemeData(opacity: isDark ? 0.5 : 0.38),
child: item child: item
); );
@ -244,7 +245,7 @@ class _CheckedPopupMenuItemState<T> extends _PopupMenuItemState<CheckedPopupMenu
enabled: config.enabled, enabled: config.enabled,
leading: new FadeTransition( leading: new FadeTransition(
opacity: _opacity, opacity: _opacity,
child: new Icon(icon: _controller.isDismissed ? null : Icons.done) child: new Icon(_controller.isDismissed ? null : Icons.done)
), ),
title: config.child title: config.child
); );
@ -526,7 +527,7 @@ class _PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (config.child == null) { if (config.child == null) {
return new IconButton( return new IconButton(
icon: Icons.more_vert, icon: new Icon(Icons.more_vert),
padding: config.padding, padding: config.padding,
tooltip: config.tooltip, tooltip: config.tooltip,
onPressed: () { showButtonMenu(context); } onPressed: () { showButtonMenu(context); }

View File

@ -11,8 +11,9 @@ import 'package:flutter/widgets.dart';
import 'app_bar.dart'; import 'app_bar.dart';
import 'bottom_sheet.dart'; import 'bottom_sheet.dart';
import 'drawer.dart'; import 'drawer.dart';
import 'icons.dart'; import 'icon.dart';
import 'icon_button.dart'; import 'icon_button.dart';
import 'icons.dart';
import 'material.dart'; import 'material.dart';
import 'snack_bar.dart'; import 'snack_bar.dart';
@ -556,7 +557,7 @@ class ScaffoldState extends State<Scaffold> {
if (leading == null) { if (leading == null) {
if (config.drawer != null) { if (config.drawer != null) {
leading = new IconButton( leading = new IconButton(
icon: Icons.menu, icon: new Icon(Icons.menu),
alignment: FractionalOffset.centerLeft, alignment: FractionalOffset.centerLeft,
onPressed: openDrawer, onPressed: openDrawer,
tooltip: 'Open navigation menu' // TODO(ianh): Figure out how to localize this string tooltip: 'Open navigation menu' // TODO(ianh): Figure out how to localize this string
@ -565,7 +566,7 @@ class ScaffoldState extends State<Scaffold> {
_shouldShowBackArrow ??= Navigator.canPop(context); _shouldShowBackArrow ??= Navigator.canPop(context);
if (_shouldShowBackArrow) { if (_shouldShowBackArrow) {
leading = new IconButton( leading = new IconButton(
icon: Icons.arrow_back, icon: new Icon(Icons.arrow_back),
alignment: FractionalOffset.centerLeft, alignment: FractionalOffset.centerLeft,
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
tooltip: 'Back' // TODO(ianh): Figure out how to localize this string tooltip: 'Back' // TODO(ianh): Figure out how to localize this string

View File

@ -14,7 +14,6 @@ import 'app_bar.dart';
import 'colors.dart'; import 'colors.dart';
import 'debug.dart'; import 'debug.dart';
import 'icon.dart'; import 'icon.dart';
import 'icons.dart';
import 'icon_theme.dart'; import 'icon_theme.dart';
import 'icon_theme_data.dart'; import 'icon_theme_data.dart';
import 'ink_well.dart'; import 'ink_well.dart';
@ -312,8 +311,14 @@ class TabLabel {
/// The text to display as the label of the tab. /// The text to display as the label of the tab.
final String text; final String text;
/// Data for an [Icon] to display as the label of the tab. /// The icon to display as the label of the tab.
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;
/// Called if [icon] is null to build an icon as a label for this tab. /// 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. /// Return value must be non-null.
final TabLabelIconBuilder iconBuilder; 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 { class _Tab extends StatelessWidget {
@ -331,7 +342,7 @@ class _Tab extends StatelessWidget {
this.label, this.label,
this.color this.color
}) : super(key: key) { }) : super(key: key) {
assert(label.text != null || label.icon != null || label.iconBuilder != null); assert(label.hasText || label.hasIcon);
} }
final VoidCallback onSelected; final VoidCallback onSelected;
@ -350,9 +361,16 @@ class _Tab extends StatelessWidget {
} }
Widget _buildLabelIcon(BuildContext context) { Widget _buildLabelIcon(BuildContext context) {
assert(label.icon != null || label.iconBuilder != null); assert(label.hasIcon);
if (label.icon != null) { 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 { } else {
return new SizedBox( return new SizedBox(
width: 24.0, width: 24.0,
@ -366,9 +384,9 @@ class _Tab extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context)); assert(debugCheckHasMaterial(context));
Widget labelContent; Widget labelContent;
if (label.icon == null && label.iconBuilder == null) { if (!label.hasIcon) {
labelContent = _buildLabelText(); labelContent = _buildLabelText();
} else if (label.text == null) { } else if (!label.hasText) {
labelContent = _buildLabelIcon(context); labelContent = _buildLabelIcon(context);
} else { } else {
labelContent = new Column( labelContent = new Column(
@ -676,7 +694,7 @@ class TabBar<T> extends Scrollable implements AppBarBottomWidget {
@override @override
double get bottomHeight { double get bottomHeight {
for (TabLabel label in labels.values) { 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 _kTextAndIconTabHeight + _kTabIndicatorHeight;
} }
return _kTabHeight + _kTabIndicatorHeight; return _kTabHeight + _kTabIndicatorHeight;
@ -922,7 +940,6 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
} }
final TextStyle textStyle = themeData.primaryTextTheme.body2; final TextStyle textStyle = themeData.primaryTextTheme.body2;
final IconThemeData iconTheme = themeData.primaryIconTheme;
final Color selectedLabelColor = config.labelColor ?? themeData.primaryTextTheme.body2.color; final Color selectedLabelColor = config.labelColor ?? themeData.primaryTextTheme.body2.color;
final Color labelColor = selectedLabelColor.withAlpha(0xB2); // 70% alpha final Color labelColor = selectedLabelColor.withAlpha(0xB2); // 70% alpha
@ -931,23 +948,20 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
int tabIndex = 0; int tabIndex = 0;
for (TabLabel label in config.labels.values) { for (TabLabel label in config.labels.values) {
tabs.add(_toTab(label, tabIndex++, labelColor, selectedLabelColor)); tabs.add(_toTab(label, tabIndex++, labelColor, selectedLabelColor));
if (label.text != null && (label.icon != null || label.iconBuilder != null)) if (label.hasText && label.hasIcon)
textAndIcons = true; textAndIcons = true;
} }
Widget contents = new IconTheme( Widget contents = new DefaultTextStyle(
data: iconTheme, style: textStyle,
child: new DefaultTextStyle( child: new _TabBarWrapper(
style: textStyle, children: tabs,
child: new _TabBarWrapper( selectedIndex: _selection?.index,
children: tabs, indicatorColor: indicatorColor,
selectedIndex: _selection?.index, indicatorRect: _indicatorRect,
indicatorColor: indicatorColor, textAndIcons: textAndIcons,
indicatorRect: _indicatorRect, isScrollable: config.isScrollable,
textAndIcons: textAndIcons, onLayoutChanged: _layoutChanged
isScrollable: config.isScrollable,
onLayoutChanged: _layoutChanged
)
) )
); );

View File

@ -89,6 +89,7 @@ class ThemeData {
Color errorColor, Color errorColor,
TextTheme textTheme, TextTheme textTheme,
TextTheme primaryTextTheme, TextTheme primaryTextTheme,
IconThemeData iconTheme,
IconThemeData primaryIconTheme IconThemeData primaryIconTheme
}) { }) {
brightness ??= Brightness.light; brightness ??= Brightness.light;
@ -96,6 +97,7 @@ class ThemeData {
primarySwatch ??= Colors.blue; primarySwatch ??= Colors.blue;
primaryColor ??= isDark ? Colors.grey[900] : primarySwatch[500]; primaryColor ??= isDark ? Colors.grey[900] : primarySwatch[500];
primaryColorBrightness ??= Brightness.dark; primaryColorBrightness ??= Brightness.dark;
final bool primaryIsDark = primaryColorBrightness == Brightness.dark;
accentColor ??= isDark ? Colors.tealAccent[200] : primarySwatch[500]; accentColor ??= isDark ? Colors.tealAccent[200] : primarySwatch[500];
accentColorBrightness ??= Brightness.dark; accentColorBrightness ??= Brightness.dark;
canvasColor ??= isDark ? Colors.grey[850] : Colors.grey[50]; canvasColor ??= isDark ? Colors.grey[850] : Colors.grey[50];
@ -115,8 +117,9 @@ class ThemeData {
hintColor ??= isDark ? const Color(0x42FFFFFF) : const Color(0x4C000000); hintColor ??= isDark ? const Color(0x42FFFFFF) : const Color(0x4C000000);
errorColor ??= Colors.red[700]; errorColor ??= Colors.red[700];
textTheme ??= isDark ? Typography.white : Typography.black; textTheme ??= isDark ? Typography.white : Typography.black;
primaryTextTheme ??= primaryColorBrightness == Brightness.dark ? Typography.white : Typography.black; primaryTextTheme ??= primaryIsDark ? Typography.white : Typography.black;
primaryIconTheme ??= primaryColorBrightness == Brightness.dark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.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( return new ThemeData.raw(
brightness: brightness, brightness: brightness,
primaryColor: primaryColor, primaryColor: primaryColor,
@ -141,6 +144,7 @@ class ThemeData {
errorColor: errorColor, errorColor: errorColor,
textTheme: textTheme, textTheme: textTheme,
primaryTextTheme: primaryTextTheme, primaryTextTheme: primaryTextTheme,
iconTheme: iconTheme,
primaryIconTheme: primaryIconTheme primaryIconTheme: primaryIconTheme
); );
} }
@ -175,6 +179,7 @@ class ThemeData {
this.errorColor, this.errorColor,
this.textTheme, this.textTheme,
this.primaryTextTheme, this.primaryTextTheme,
this.iconTheme,
this.primaryIconTheme this.primaryIconTheme
}) { }) {
assert(brightness != null); assert(brightness != null);
@ -200,6 +205,7 @@ class ThemeData {
assert(errorColor != null); assert(errorColor != null);
assert(textTheme != null); assert(textTheme != null);
assert(primaryTextTheme != null); assert(primaryTextTheme != null);
assert(iconTheme != null);
assert(primaryIconTheme != null); assert(primaryIconTheme != null);
} }
@ -307,6 +313,9 @@ class ThemeData {
/// A text theme that contrasts with the primary color. /// A text theme that contrasts with the primary color.
final TextTheme primaryTextTheme; 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. /// An icon theme that contrasts with the primary color.
final IconThemeData primaryIconTheme; final IconThemeData primaryIconTheme;
@ -336,6 +345,7 @@ class ThemeData {
errorColor: Color.lerp(begin.errorColor, end.errorColor, t), errorColor: Color.lerp(begin.errorColor, end.errorColor, t),
textTheme: TextTheme.lerp(begin.textTheme, end.textTheme, t), textTheme: TextTheme.lerp(begin.textTheme, end.textTheme, t),
primaryTextTheme: TextTheme.lerp(begin.primaryTextTheme, end.primaryTextTheme, 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) primaryIconTheme: IconThemeData.lerp(begin.primaryIconTheme, end.primaryIconTheme, t)
); );
} }
@ -368,6 +378,7 @@ class ThemeData {
(otherData.errorColor == errorColor) && (otherData.errorColor == errorColor) &&
(otherData.textTheme == textTheme) && (otherData.textTheme == textTheme) &&
(otherData.primaryTextTheme == primaryTextTheme) && (otherData.primaryTextTheme == primaryTextTheme) &&
(otherData.iconTheme == iconTheme) &&
(otherData.primaryIconTheme == primaryIconTheme); (otherData.primaryIconTheme == primaryIconTheme);
} }
@ -398,6 +409,7 @@ class ThemeData {
errorColor, errorColor,
textTheme, textTheme,
primaryTextTheme, primaryTextTheme,
iconTheme,
primaryIconTheme primaryIconTheme
) )
); );

View File

@ -111,7 +111,8 @@ class _TwoLevelSublistState extends State<TwoLevelSublist> {
), ),
child: new Column( child: new Column(
children: <Widget>[ children: <Widget>[
new IconTheme( new IconTheme.merge(
context: context,
data: new IconThemeData(color: _iconColor.evaluate(_easeInAnimation)), data: new IconThemeData(color: _iconColor.evaluate(_easeInAnimation)),
child: new TwoLevelListItem( child: new TwoLevelListItem(
onTap: _handleOnTap, onTap: _handleOnTap,
@ -122,9 +123,7 @@ class _TwoLevelSublistState extends State<TwoLevelSublist> {
), ),
trailing: new RotationTransition( trailing: new RotationTransition(
turns: _iconTurns, turns: _iconTurns,
child: new Icon( child: new Icon(Icons.expand_more)
icon: Icons.expand_more
)
) )
) )
), ),

View File

@ -85,18 +85,18 @@ class TextStyle {
}) { }) {
return new TextStyle( return new TextStyle(
inherit: inherit, inherit: inherit,
color: color != null ? color : this.color, color: color ?? this.color,
fontFamily: fontFamily != null ? fontFamily : this.fontFamily, fontFamily: fontFamily ?? this.fontFamily,
fontSize: fontSize != null ? fontSize : this.fontSize, fontSize: fontSize ?? this.fontSize,
fontWeight: fontWeight != null ? fontWeight : this.fontWeight, fontWeight: fontWeight ?? this.fontWeight,
fontStyle: fontStyle != null ? fontStyle : this.fontStyle, fontStyle: fontStyle ?? this.fontStyle,
letterSpacing: letterSpacing != null ? letterSpacing : this.letterSpacing, letterSpacing: letterSpacing ?? this.letterSpacing,
wordSpacing: wordSpacing != null ? wordSpacing : this.wordSpacing, wordSpacing: wordSpacing ?? this.wordSpacing,
textBaseline: textBaseline != null ? textBaseline : this.textBaseline, textBaseline: textBaseline ?? this.textBaseline,
height: height != null ? height : this.height, height: height ?? this.height,
decoration: decoration != null ? decoration : this.decoration, decoration: decoration ?? this.decoration,
decorationColor: decorationColor != null ? decorationColor : this.decorationColor, decorationColor: decorationColor ?? this.decorationColor,
decorationStyle: decorationStyle != null ? decorationStyle : this.decorationStyle decorationStyle: decorationStyle ?? this.decorationStyle
); );
} }

View File

@ -11,7 +11,7 @@ void main() {
testWidgets('Icon sizing - no theme, default size', (WidgetTester tester) async { testWidgets('Icon sizing - no theme, default size', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
new Center( new Center(
child: new Icon() child: new Icon(null)
) )
); );
@ -23,6 +23,7 @@ void main() {
await tester.pumpWidget( await tester.pumpWidget(
new Center( new Center(
child: new Icon( child: new Icon(
null,
size: 96.0 size: 96.0
) )
) )
@ -37,7 +38,7 @@ void main() {
new Center( new Center(
child: new IconTheme( child: new IconTheme(
data: new IconThemeData(size: 36.0), data: new IconThemeData(size: 36.0),
child: new Icon() child: new Icon(null)
) )
) )
); );
@ -52,6 +53,7 @@ void main() {
child: new IconTheme( child: new IconTheme(
data: new IconThemeData(size: 36.0), data: new IconThemeData(size: 36.0),
child: new Icon( child: new Icon(
null,
size: 48.0 size: 48.0
) )
) )
@ -67,7 +69,7 @@ void main() {
new Center( new Center(
child: new IconTheme( child: new IconTheme(
data: new IconThemeData(), data: new IconThemeData(),
child: new Icon() child: new Icon(null)
) )
) )
); );

View File

@ -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)));
});
}

View File

@ -13,7 +13,7 @@ void main() {
color: Colors.green[500], color: Colors.green[500],
opacity: 0.5 opacity: 0.5
), ),
child: new Icon(icon: Icons.add) child: new Icon(Icons.add)
) )
); );
Text text = tester.widget(find.byType(Text)); Text text = tester.widget(find.byType(Text));

View File

@ -131,9 +131,9 @@ class AnalyzeCommand extends FlutterCommand {
pubSpecDirectories.add(currentDirectory); pubSpecDirectories.add(currentDirectory);
} }
//TODO (ianh): Fix the intl package resource generator // TODO(ianh): Fix the intl package resource generator
//TODO (pq): extract this regexp from the exclude in options // TODO(pq): extract this regexp from the exclude in options
RegExp stockExampleFiles = new RegExp('examples/stocks/lib/.*\.dart\$'); RegExp stockExampleFiles = new RegExp('examples/stocks/lib/i18n/.*\.dart\$');
if (flutterRepo) { if (flutterRepo) {
for (Directory dir in runner.getRepoPackages()) { for (Directory dir in runner.getRepoPackages()) {

View File

@ -48,9 +48,7 @@ class _FlutterDemoState extends State<FlutterDemo> {
floatingActionButton: new FloatingActionButton( floatingActionButton: new FloatingActionButton(
onPressed: _incrementCounter, onPressed: _incrementCounter,
tooltip: 'Increment', tooltip: 'Increment',
child: new Icon( child: new Icon(Icons.add)
icon: Icons.add
)
) )
); );
} }