mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

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.
652 lines
19 KiB
Dart
652 lines
19 KiB
Dart
// Copyright 2015 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/scheduler.dart' show timeDilation;
|
|
|
|
void main() {
|
|
runApp(
|
|
new ComplexLayoutApp()
|
|
);
|
|
}
|
|
|
|
class ComplexLayoutApp extends StatefulWidget {
|
|
@override
|
|
ComplexLayoutAppState createState() => new ComplexLayoutAppState();
|
|
|
|
static ComplexLayoutAppState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher<ComplexLayoutAppState>());
|
|
}
|
|
|
|
class ComplexLayoutAppState extends State<ComplexLayoutApp> {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new MaterialApp(
|
|
theme: lightTheme ? new ThemeData.light() : new ThemeData.dark(),
|
|
title: 'Advanced Layout',
|
|
home: new ComplexLayout()
|
|
);
|
|
}
|
|
|
|
bool _lightTheme = true;
|
|
bool get lightTheme => _lightTheme;
|
|
set lightTheme(bool value) {
|
|
setState(() {
|
|
_lightTheme = value;
|
|
});
|
|
}
|
|
|
|
void toggleAnimationSpeed() {
|
|
setState(() {
|
|
timeDilation = (timeDilation != 1.0) ? 1.0 : 5.0;
|
|
});
|
|
}
|
|
}
|
|
|
|
class ComplexLayout extends StatefulWidget {
|
|
ComplexLayout({ Key key }) : super(key: key);
|
|
|
|
@override
|
|
ComplexLayoutState createState() => new ComplexLayoutState();
|
|
|
|
static ComplexLayoutState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher<ComplexLayoutState>());
|
|
|
|
}
|
|
|
|
class FancyItemDelegate extends LazyBlockDelegate {
|
|
@override
|
|
Widget buildItem(BuildContext context, int index) {
|
|
if (index % 2 == 0)
|
|
return new FancyImageItem(index, key: new Key('Item $index'));
|
|
else
|
|
return new FancyGalleryItem(index, key: new Key('Item $index'));
|
|
}
|
|
|
|
@override
|
|
bool shouldRebuild(FancyItemDelegate oldDelegate) => false;
|
|
}
|
|
|
|
class ComplexLayoutState extends State<ComplexLayout> {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
|
|
return new Scaffold(
|
|
appBar: new AppBar(
|
|
title: new Text('Advanced Layout'),
|
|
actions: <Widget>[
|
|
new IconButton(
|
|
icon: new Icon(Icons.create),
|
|
tooltip: 'Search',
|
|
onPressed: () {
|
|
print('Pressed search');
|
|
}
|
|
),
|
|
new TopBarMenu()
|
|
]
|
|
),
|
|
body: new Column(
|
|
children: <Widget>[
|
|
new Flexible(
|
|
child: new LazyBlock(
|
|
key: new Key('main-scroll'),
|
|
delegate: new FancyItemDelegate()
|
|
)
|
|
),
|
|
new BottomBar()
|
|
]
|
|
),
|
|
drawer: new GalleryDrawer()
|
|
);
|
|
}
|
|
}
|
|
|
|
class TopBarMenu extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new PopupMenuButton<String>(
|
|
onSelected: (String value) { print('Selected: $value'); },
|
|
itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
|
|
new PopupMenuItem<String>(
|
|
value: 'Friends',
|
|
child: new MenuItemWithIcon(Icons.people, 'Friends', '5 new')
|
|
),
|
|
new PopupMenuItem<String>(
|
|
value: 'Events',
|
|
child: new MenuItemWithIcon(Icons.event, 'Events', '12 upcoming')
|
|
),
|
|
new PopupMenuItem<String>(
|
|
value: 'Events',
|
|
child: new MenuItemWithIcon(Icons.group, 'Groups', '14')
|
|
),
|
|
new PopupMenuItem<String>(
|
|
value: 'Events',
|
|
child: new MenuItemWithIcon(Icons.image, 'Pictures', '12')
|
|
),
|
|
new PopupMenuItem<String>(
|
|
value: 'Events',
|
|
child: new MenuItemWithIcon(Icons.near_me, 'Nearby', '33')
|
|
),
|
|
new PopupMenuItem<String>(
|
|
value: 'Friends',
|
|
child: new MenuItemWithIcon(Icons.people, 'Friends', '5')
|
|
),
|
|
new PopupMenuItem<String>(
|
|
value: 'Events',
|
|
child: new MenuItemWithIcon(Icons.event, 'Events', '12')
|
|
),
|
|
new PopupMenuItem<String>(
|
|
value: 'Events',
|
|
child: new MenuItemWithIcon(Icons.group, 'Groups', '14')
|
|
),
|
|
new PopupMenuItem<String>(
|
|
value: 'Events',
|
|
child: new MenuItemWithIcon(Icons.image, 'Pictures', '12')
|
|
),
|
|
new PopupMenuItem<String>(
|
|
value: 'Events',
|
|
child: new MenuItemWithIcon(Icons.near_me, 'Nearby', '33')
|
|
)
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
class MenuItemWithIcon extends StatelessWidget {
|
|
MenuItemWithIcon(this.icon, this.title, this.subtitle);
|
|
|
|
final IconData icon;
|
|
final String title;
|
|
final String subtitle;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Row(
|
|
children: <Widget>[
|
|
new Icon(icon),
|
|
new Padding(
|
|
padding: new EdgeInsets.only(left: 8.0, right: 8.0),
|
|
child: new Text(title)
|
|
),
|
|
new Text(subtitle, style: Theme.of(context).textTheme.caption)
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
class FancyImageItem extends StatelessWidget {
|
|
FancyImageItem(this.index, {Key key}) : super(key: key);
|
|
|
|
final int index;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new BlockBody(
|
|
children: <Widget>[
|
|
new UserHeader('Ali Connors $index'),
|
|
new ItemDescription(),
|
|
new ItemImageBox(),
|
|
new InfoBar(),
|
|
new Padding(
|
|
padding: new EdgeInsets.symmetric(horizontal: 8.0),
|
|
child: new Divider()
|
|
),
|
|
new IconBar(),
|
|
new FatDivider()
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
class FancyGalleryItem extends StatelessWidget {
|
|
FancyGalleryItem(this.index, {Key key}) : super(key: key);
|
|
|
|
final int index;
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new BlockBody(
|
|
children: <Widget>[
|
|
new UserHeader('Ali Connors'),
|
|
new ItemGalleryBox(index),
|
|
new InfoBar(),
|
|
new Padding(
|
|
padding: new EdgeInsets.symmetric(horizontal: 8.0),
|
|
child: new Divider()
|
|
),
|
|
new IconBar(),
|
|
new FatDivider()
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
class InfoBar extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Padding(
|
|
padding: new EdgeInsets.all(8.0),
|
|
child: new Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: <Widget>[
|
|
new MiniIconWithText(Icons.thumb_up, '42'),
|
|
new Text('3 Comments', style: Theme.of(context).textTheme.caption)
|
|
]
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
class IconBar extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Padding(
|
|
padding: new EdgeInsets.only(left: 16.0, right: 16.0),
|
|
child: new Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: <Widget>[
|
|
new IconWithText(Icons.thumb_up, 'Like'),
|
|
new IconWithText(Icons.comment, 'Comment'),
|
|
new IconWithText(Icons.share, 'Share'),
|
|
]
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
class IconWithText extends StatelessWidget {
|
|
IconWithText(this.icon, this.title);
|
|
|
|
final IconData icon;
|
|
final String title;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Row(
|
|
mainAxisAlignment: MainAxisAlignment.collapse,
|
|
children: <Widget>[
|
|
new IconButton(
|
|
icon: new Icon(icon),
|
|
onPressed: () { print('Pressed $title button'); }
|
|
),
|
|
new Text(title)
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
class MiniIconWithText extends StatelessWidget {
|
|
MiniIconWithText(this.icon, this.title);
|
|
|
|
final IconData icon;
|
|
final String title;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Row(
|
|
mainAxisAlignment: MainAxisAlignment.collapse,
|
|
children: <Widget>[
|
|
new Padding(
|
|
padding: new EdgeInsets.only(right: 8.0),
|
|
child: new Container(
|
|
width: 16.0,
|
|
height: 16.0,
|
|
decoration: new BoxDecoration(
|
|
backgroundColor: Theme.of(context).primaryColor,
|
|
shape: BoxShape.circle
|
|
),
|
|
child: new Icon(icon, color: Colors.white, size: 12.0)
|
|
)
|
|
),
|
|
new Text(title, style: Theme.of(context).textTheme.caption)
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
class FatDivider extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Container(
|
|
height: 8.0,
|
|
decoration: new BoxDecoration(
|
|
backgroundColor: Theme.of(context).dividerColor
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
class UserHeader extends StatelessWidget {
|
|
UserHeader(this.userName);
|
|
|
|
final String userName;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Padding(
|
|
padding: new EdgeInsets.all(8.0),
|
|
child: new Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: <Widget>[
|
|
new Padding(
|
|
padding: new EdgeInsets.only(right: 8.0),
|
|
child: new Image(
|
|
image: new AssetImage('packages/flutter_gallery_assets/ali_connors_sml.png'),
|
|
width: 32.0,
|
|
height: 32.0
|
|
)
|
|
),
|
|
new Flexible(
|
|
child: new Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: <Widget>[
|
|
new RichText(text: new TextSpan(
|
|
style: Theme.of(context).textTheme.body1,
|
|
children: <TextSpan>[
|
|
new TextSpan(text: userName, style: new TextStyle(fontWeight: FontWeight.bold)),
|
|
new TextSpan(text: ' shared a new '),
|
|
new TextSpan(text: 'photo', style: new TextStyle(fontWeight: FontWeight.bold))
|
|
]
|
|
)),
|
|
new Row(
|
|
children: <Widget>[
|
|
new Text('Yesterday at 11:55 • ', style: Theme.of(context).textTheme.caption),
|
|
new Icon(Icons.people, size: 16.0, color: Theme.of(context).textTheme.caption.color)
|
|
]
|
|
)
|
|
]
|
|
)
|
|
),
|
|
new TopBarMenu()
|
|
]
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
class ItemDescription extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Padding(
|
|
padding: new EdgeInsets.all(8.0),
|
|
child: new Text('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.')
|
|
);
|
|
}
|
|
}
|
|
|
|
class ItemImageBox extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Padding(
|
|
padding: new EdgeInsets.all(8.0),
|
|
child: new Card(
|
|
child: new Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: <Widget>[
|
|
new Stack(
|
|
children: <Widget>[
|
|
new SizedBox(
|
|
height: 230.0,
|
|
child: new Image(
|
|
image: new AssetImage('packages/flutter_gallery_assets/top_10_australian_beaches.png')
|
|
)
|
|
),
|
|
new Theme(
|
|
data: new ThemeData.dark(),
|
|
child: new Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: <Widget>[
|
|
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'); }
|
|
),
|
|
]
|
|
)
|
|
),
|
|
new Positioned(
|
|
bottom: 4.0,
|
|
left: 4.0,
|
|
child: new Container(
|
|
decoration: new BoxDecoration(
|
|
backgroundColor: Colors.black54,
|
|
borderRadius: 2.0
|
|
),
|
|
padding: new EdgeInsets.all(4.0),
|
|
child: new RichText(
|
|
text: new TextSpan(
|
|
style: new TextStyle(color: Colors.white),
|
|
children: <TextSpan>[
|
|
new TextSpan(
|
|
text: 'Photo by '
|
|
),
|
|
new TextSpan(
|
|
style: new TextStyle(fontWeight: FontWeight.bold),
|
|
text: 'Magic Mike'
|
|
)
|
|
]
|
|
)
|
|
)
|
|
)
|
|
)
|
|
]
|
|
)
|
|
,
|
|
new Padding(
|
|
padding: new EdgeInsets.all(8.0),
|
|
child: new Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: <Widget>[
|
|
new Text('Where can you find that amazing sunset?', style: Theme.of(context).textTheme.body2),
|
|
new Text('The sun sets over stinson beach', style: Theme.of(context).textTheme.body1),
|
|
new Text('flutter.io/amazingsunsets', style: Theme.of(context).textTheme.caption)
|
|
]
|
|
)
|
|
)
|
|
]
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
class ItemGalleryBox extends StatelessWidget {
|
|
ItemGalleryBox(this.index);
|
|
|
|
final int index;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
List<String> tabNames = <String>[
|
|
'A', 'B', 'C', 'D'
|
|
];
|
|
|
|
return new SizedBox(
|
|
height: 200.0,
|
|
child: new TabBarSelection<String>(
|
|
values: tabNames,
|
|
child: new Column(
|
|
children: <Widget>[
|
|
new Flexible(
|
|
child: new TabBarView<String>(
|
|
children: tabNames.map((String tabName) {
|
|
return new Container(
|
|
key: new Key('Tab $index - $tabName'),
|
|
child: new Padding(
|
|
padding: new EdgeInsets.all(8.0),
|
|
child: new Card(
|
|
child: new Column(
|
|
children: <Widget>[
|
|
new Flexible(
|
|
child: new Container(
|
|
decoration: new BoxDecoration(
|
|
backgroundColor: Theme.of(context).primaryColor
|
|
),
|
|
child: new Center(
|
|
child: new Text(tabName, style: Theme.of(context).textTheme.headline.copyWith(color: Colors.white))
|
|
)
|
|
)
|
|
),
|
|
new Row(
|
|
children: <Widget>[
|
|
new IconButton(
|
|
icon: new Icon(Icons.share),
|
|
onPressed: () { print('Pressed share'); }
|
|
),
|
|
new IconButton(
|
|
icon: new Icon(Icons.event),
|
|
onPressed: () { print('Pressed event'); }
|
|
),
|
|
new Flexible(
|
|
child: new Padding(
|
|
padding: new EdgeInsets.only(left: 8.0),
|
|
child: new Text('This is item $tabName')
|
|
)
|
|
)
|
|
]
|
|
)
|
|
]
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}).toList()
|
|
)
|
|
),
|
|
new Container(
|
|
child: new TabPageSelector<String>()
|
|
)
|
|
]
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
class BottomBar extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Container(
|
|
decoration: new BoxDecoration(
|
|
border: new Border(
|
|
top: new BorderSide(
|
|
color: Theme.of(context).dividerColor,
|
|
width: 1.0
|
|
)
|
|
)
|
|
),
|
|
child: new Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: <Widget>[
|
|
new BottomBarButton(Icons.new_releases, 'News'),
|
|
new BottomBarButton(Icons.people, 'Requests'),
|
|
new BottomBarButton(Icons.chat, 'Messenger'),
|
|
new BottomBarButton(Icons.bookmark, 'Bookmark'),
|
|
new BottomBarButton(Icons.alarm, 'Alarm')
|
|
]
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
class BottomBarButton extends StatelessWidget {
|
|
BottomBarButton(this.icon, this.title);
|
|
|
|
final IconData icon;
|
|
final String title;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Padding(
|
|
padding: new EdgeInsets.all(8.0),
|
|
child: new Column(
|
|
children: <Widget>[
|
|
new IconButton(
|
|
icon: new Icon(icon),
|
|
onPressed: () { print('Pressed: $title'); }
|
|
),
|
|
new Text(title, style: Theme.of(context).textTheme.caption)
|
|
]
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
class GalleryDrawer extends StatelessWidget {
|
|
GalleryDrawer({ Key key }) : super(key: key);
|
|
|
|
void _changeTheme(BuildContext context, bool value) {
|
|
ComplexLayoutApp.of(context).lightTheme = value;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Drawer(
|
|
child: new Block(
|
|
children: <Widget>[
|
|
new FancyDrawerHeader(),
|
|
new DrawerItem(
|
|
icon: new Icon(Icons.brightness_5),
|
|
onPressed: () { _changeTheme(context, true); },
|
|
selected: ComplexLayoutApp.of(context).lightTheme,
|
|
child: new Row(
|
|
children: <Widget>[
|
|
new Flexible(child: new Text('Light')),
|
|
new Radio<bool>(
|
|
value: true,
|
|
groupValue: ComplexLayoutApp.of(context).lightTheme,
|
|
onChanged: (bool value) { _changeTheme(context, value); }
|
|
)
|
|
]
|
|
)
|
|
),
|
|
new DrawerItem(
|
|
icon: new Icon(Icons.brightness_7),
|
|
onPressed: () { _changeTheme(context, false); },
|
|
selected: !ComplexLayoutApp.of(context).lightTheme,
|
|
child: new Row(
|
|
children: <Widget>[
|
|
new Flexible(child: new Text('Dark')),
|
|
new Radio<bool>(
|
|
value: false,
|
|
groupValue: ComplexLayoutApp.of(context).lightTheme,
|
|
onChanged: (bool value) { _changeTheme(context, value); }
|
|
)
|
|
]
|
|
)
|
|
),
|
|
new Divider(),
|
|
new DrawerItem(
|
|
icon: new Icon(Icons.hourglass_empty),
|
|
selected: timeDilation != 1.0,
|
|
onPressed: () { ComplexLayoutApp.of(context).toggleAnimationSpeed(); },
|
|
child: new Row(
|
|
children: <Widget>[
|
|
new Flexible(child: new Text('Animate Slowly')),
|
|
new Checkbox(
|
|
value: timeDilation != 1.0,
|
|
onChanged: (bool value) { ComplexLayoutApp.of(context).toggleAnimationSpeed(); }
|
|
)
|
|
]
|
|
)
|
|
)
|
|
]
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
class FancyDrawerHeader extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Container(
|
|
decoration: new BoxDecoration(
|
|
backgroundColor: Colors.purple[500]
|
|
),
|
|
height: 200.0
|
|
);
|
|
}
|
|
}
|