Commit 284eaa9c authored by Adam Barth's avatar Adam Barth

Add a MaterialList

A MaterialList understands the sizing, padding, and scrollbar features of
Material Design lists.

Also, add CircleAvatar for showing the circular avatars that are commonly used
in material lists.
parent 780ee181
......@@ -9,6 +9,7 @@ library material;
export 'src/material/card.dart';
export 'src/material/checkbox.dart';
export 'src/material/circle_avatar.dart';
export 'src/material/colors.dart';
export 'src/material/constants.dart';
export 'src/material/date_picker.dart';
......@@ -20,14 +21,17 @@ export 'src/material/drawer_item.dart';
export 'src/material/edges.dart';
export 'src/material/flat_button.dart';
export 'src/material/floating_action_button.dart';
export 'src/material/icon_button.dart';
export 'src/material/icon.dart';
export 'src/material/icon_button.dart';
export 'src/material/icon_theme.dart';
export 'src/material/icon_theme_data.dart';
export 'src/material/ink_well.dart';
export 'src/material/input.dart';
export 'src/material/list_item.dart';
export 'src/material/material.dart';
export 'src/material/material_app.dart';
export 'src/material/material_button.dart';
export 'src/material/material_list.dart';
export 'src/material/popup_menu_item.dart';
export 'src/material/popup_menu.dart';
export 'src/material/progress_indicator.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/painting.dart';
import 'package:flutter/widgets.dart';
import 'constants.dart';
import 'theme.dart';
import 'typography.dart';
class CircleAvatar extends StatelessComponent {
CircleAvatar({
Key key,
this.label,
this.backgroundColor,
this.textTheme
}) : super(key: key);
final String label;
final Color backgroundColor;
final TextTheme textTheme;
Widget build(BuildContext context) {
Color color = backgroundColor;
TextStyle style = textTheme?.title;
if (color == null || style == null) {
ThemeData themeData = Theme.of(context);
color ??= themeData.primaryColor;
style ??= themeData.primaryTextTheme.title;
}
return new AnimatedContainer(
duration: kThemeChangeDuration,
decoration: new BoxDecoration(
backgroundColor: color,
shape: Shape.circle
),
width: 40.0,
height: 40.0,
child: new Center(
child: new Text(label, style: style)
)
);
}
}
......@@ -19,7 +19,11 @@ const double kSnackBarHeight = 52.0;
// https://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-keylines-spacing
const double kListTitleHeight = 72.0;
const double kListSubtitleHeight = 48.0;
const double kListItemHeight = 72.0;
const double kOneLineListItemHeight = 48.0;
const double kOneLineListItemWithAvatarHeight = 56.0;
const double kTwoLineListItemHeight = 72.0;
const double kThreeLineListItemHeight = 88.0;
const double kMaterialDrawerHeight = 140.0;
const double kScrollbarSize = 10.0;
......
......@@ -122,17 +122,15 @@ class _DatePickerHeader extends StatelessComponent {
Widget build(BuildContext context) {
ThemeData theme = Theme.of(context);
TextTheme headerTheme;
TextTheme headerTheme = theme.primaryTextTheme;
Color dayColor;
Color yearColor;
switch(theme.primaryColorBrightness) {
case ThemeBrightness.light:
headerTheme = Typography.black;
dayColor = mode == DatePickerMode.day ? Colors.black87 : Colors.black54;
yearColor = mode == DatePickerMode.year ? Colors.black87 : Colors.black54;
break;
case ThemeBrightness.dark:
headerTheme = Typography.white;
dayColor = mode == DatePickerMode.day ? Colors.white : Colors.white70;
yearColor = mode == DatePickerMode.year ? Colors.white : Colors.white70;
break;
......
......@@ -4,7 +4,8 @@
import 'package:flutter/widgets.dart';
import 'icon.dart';
import 'icon_theme.dart';
import 'icon_theme_data.dart';
import 'ink_well.dart';
import 'material.dart';
import 'theme.dart';
......
......@@ -8,50 +8,8 @@ import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'theme.dart';
enum IconThemeColor { white, black }
class IconThemeData {
const IconThemeData({ this.color });
final IconThemeColor color;
bool operator ==(dynamic other) {
if (other is! IconThemeData)
return false;
final IconThemeData typedOther = other;
return color == typedOther;
}
int get hashCode => color.hashCode;
String toString() => '$color';
}
class IconTheme extends InheritedWidget {
IconTheme({
Key key,
this.data,
Widget child
}) : super(key: key, child: child) {
assert(data != null);
assert(child != null);
}
final IconThemeData data;
static IconThemeData of(BuildContext context) {
IconTheme result = context.inheritedWidgetOfType(IconTheme);
return result?.data;
}
bool updateShouldNotify(IconTheme old) => data != old.data;
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('$data');
}
}
import 'icon_theme.dart';
import 'icon_theme_data.dart';
AssetBundle _initIconBundle() {
if (rootBundle != null)
......
......@@ -7,6 +7,7 @@ import 'dart:ui' as ui;
import 'package:flutter/widgets.dart';
import 'icon.dart';
import 'icon_theme_data.dart';
class IconButton extends StatelessComponent {
const IconButton({
......
// 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/widgets.dart';
import 'icon_theme_data.dart';
class IconTheme extends InheritedWidget {
IconTheme({
Key key,
this.data,
Widget child
}) : super(key: key, child: child) {
assert(data != null);
assert(child != null);
}
final IconThemeData data;
static IconThemeData of(BuildContext context) {
IconTheme result = context.inheritedWidgetOfType(IconTheme);
return result?.data;
}
bool updateShouldNotify(IconTheme old) => data != old.data;
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('$data');
}
}
// 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.
enum IconThemeColor { white, black }
class IconThemeData {
const IconThemeData({ this.color });
final IconThemeColor color;
bool operator ==(dynamic other) {
if (other is! IconThemeData)
return false;
final IconThemeData typedOther = other;
return color == typedOther;
}
int get hashCode => color.hashCode;
String toString() => '$color';
}
......@@ -5,7 +5,6 @@
import 'package:flutter/widgets.dart';
import 'ink_well.dart';
import 'constants.dart';
class ListItem extends StatelessComponent {
ListItem({
......@@ -42,14 +41,12 @@ class ListItem extends StatelessComponent {
if (right != null) {
children.add(new Container(
margin: new EdgeDims.only(left: 8.0),
width: 40.0,
margin: new EdgeDims.only(left: 16.0),
child: right
));
}
return new Container(
height: kListItemHeight,
return new Padding(
padding: const EdgeDims.symmetric(horizontal: 16.0),
child: new InkWell(
onTap: onTap,
......
// 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/widgets.dart';
import 'constants.dart';
import 'scrollbar_painter.dart';
enum MaterialListType {
oneLine,
oneLineWithAvatar,
twoLine,
threeLine
}
Map<MaterialListType, double> _kItemExtent = const <MaterialListType, double>{
MaterialListType.oneLine: kOneLineListItemHeight,
MaterialListType.oneLineWithAvatar: kOneLineListItemWithAvatarHeight,
MaterialListType.twoLine: kTwoLineListItemHeight,
MaterialListType.threeLine: kThreeLineListItemHeight,
};
class MaterialList<T> extends StatefulComponent {
MaterialList({
Key key,
this.initialScrollOffset,
this.onScroll,
this.items,
this.itemBuilder,
this.type: MaterialListType.twoLine
}) : super(key: key);
final double initialScrollOffset;
final ScrollListener onScroll;
final List<T> items;
final ItemBuilder<T> itemBuilder;
final MaterialListType type;
_MaterialListState<T> createState() => new _MaterialListState<T>();
}
class _MaterialListState<T> extends State<MaterialList<T>> {
void initState() {
super.initState();
_scrollbarPainter = new ScrollbarPainter();
}
ScrollbarPainter _scrollbarPainter;
Widget build(BuildContext context) {
return new ScrollableList<T>(
initialScrollOffset: config.initialScrollOffset,
scrollDirection: ScrollDirection.vertical,
onScroll: config.onScroll,
items: config.items,
itemBuilder: config.itemBuilder,
itemExtent: _kItemExtent[config.type],
padding: const EdgeDims.symmetric(vertical: 8.0),
scrollableListPainter: _scrollbarPainter
);
}
}
......@@ -13,9 +13,10 @@ import 'package:flutter/widgets.dart';
import 'colors.dart';
import 'constants.dart';
import 'icon.dart';
import 'icon_theme.dart';
import 'icon_theme_data.dart';
import 'ink_well.dart';
import 'theme.dart';
import 'typography.dart';
typedef void TabSelectedIndexChanged(int selectedIndex);
typedef void TabLayoutChanged(Size size, List<double> widths);
......@@ -509,18 +510,8 @@ class _TabBarState extends ScrollableState<TabBar> {
indicatorColor = Colors.white;
}
TextStyle textStyle;
IconThemeColor iconThemeColor;
switch (themeData.primaryColorBrightness) {
case ThemeBrightness.light:
textStyle = Typography.black.body1;
iconThemeColor = IconThemeColor.black;
break;
case ThemeBrightness.dark:
textStyle = Typography.white.body1;
iconThemeColor = IconThemeColor.white;
break;
}
TextStyle textStyle = themeData.primaryTextTheme.body1;
IconThemeData iconTheme = themeData.primaryIconTheme;
List<Widget> tabs = <Widget>[];
bool textAndIcons = false;
......@@ -532,7 +523,7 @@ class _TabBarState extends ScrollableState<TabBar> {
}
Widget content = new IconTheme(
data: new IconThemeData(color: iconThemeColor),
data: iconTheme,
child: new DefaultTextStyle(
style: textStyle,
child: new BuilderTransition(
......
......@@ -4,8 +4,9 @@
import 'dart:ui' show Color;
import 'typography.dart';
import 'colors.dart';
import 'icon_theme_data.dart';
import 'typography.dart';
enum ThemeBrightness { dark, light }
......@@ -82,6 +83,19 @@ class ThemeData {
/// icons placed on top of the primary color (e.g. toolbar text).
final ThemeBrightness primaryColorBrightness;
/// A text theme that contrasts with the primary color.
TextTheme get primaryTextTheme {
if (primaryColorBrightness == ThemeBrightness.dark)
return Typography.white;
return Typography.black;
}
IconThemeData get primaryIconTheme {
if (primaryColorBrightness == ThemeBrightness.dark)
return const IconThemeData(color: IconThemeColor.white);
return const IconThemeData(color: IconThemeColor.black);
}
/// The foreground color for widgets (knobs, text, etc)
Color get accentColor => _accentColor;
Color _accentColor;
......
......@@ -5,7 +5,8 @@
import 'package:flutter/widgets.dart';
import 'constants.dart';
import 'icon.dart';
import 'icon_theme.dart';
import 'icon_theme_data.dart';
import 'shadows.dart';
import 'theme.dart';
import 'typography.dart';
......@@ -17,7 +18,8 @@ class ToolBar extends StatelessComponent {
this.center,
this.right,
this.level: 2,
this.backgroundColor
this.backgroundColor,
this.textTheme
}) : super(key: key);
final Widget left;
......@@ -25,22 +27,22 @@ class ToolBar extends StatelessComponent {
final List<Widget> right;
final int level;
final Color backgroundColor;
final TextTheme textTheme;
Widget build(BuildContext context) {
Color color = backgroundColor;
IconThemeData iconThemeData;
TextStyle centerStyle = Typography.white.title;
TextStyle sideStyle = Typography.white.body1;
if (color == null) {
TextStyle centerStyle = textTheme?.title;
TextStyle sideStyle = textTheme?.body1;
if (color == null || iconThemeData == null || textTheme == null) {
ThemeData themeData = Theme.of(context);
color = themeData.primaryColor;
if (themeData.primaryColorBrightness == ThemeBrightness.light) {
centerStyle = Typography.black.title;
sideStyle = Typography.black.body2;
iconThemeData = const IconThemeData(color: IconThemeColor.black);
} else {
iconThemeData = const IconThemeData(color: IconThemeColor.white);
}
color ??= themeData.primaryColor;
iconThemeData ??= themeData.primaryIconTheme;
TextTheme primaryTextTheme = themeData.primaryTextTheme;
centerStyle ??= primaryTextTheme.title;
sideStyle ??= primaryTextTheme.body2;
}
List<Widget> children = new List<Widget>();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment