Merge pull request #1627 from abarth/material_list
Add a MaterialList
diff --git a/sky/packages/sky/lib/material.dart b/sky/packages/sky/lib/material.dart
index 51bcfaf..40c668c 100644
--- a/sky/packages/sky/lib/material.dart
+++ b/sky/packages/sky/lib/material.dart
@@ -9,6 +9,7 @@
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/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';
diff --git a/sky/packages/sky/lib/src/material/circle_avatar.dart b/sky/packages/sky/lib/src/material/circle_avatar.dart
new file mode 100644
index 0000000..7895b87
--- /dev/null
+++ b/sky/packages/sky/lib/src/material/circle_avatar.dart
@@ -0,0 +1,47 @@
+// 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)
+ )
+ );
+ }
+}
diff --git a/sky/packages/sky/lib/src/material/constants.dart b/sky/packages/sky/lib/src/material/constants.dart
index 9b50a30..6824793 100644
--- a/sky/packages/sky/lib/src/material/constants.dart
+++ b/sky/packages/sky/lib/src/material/constants.dart
@@ -19,7 +19,11 @@
// 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;
diff --git a/sky/packages/sky/lib/src/material/date_picker.dart b/sky/packages/sky/lib/src/material/date_picker.dart
index e9d5759..c2bdc57 100644
--- a/sky/packages/sky/lib/src/material/date_picker.dart
+++ b/sky/packages/sky/lib/src/material/date_picker.dart
@@ -122,17 +122,15 @@
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;
diff --git a/sky/packages/sky/lib/src/material/floating_action_button.dart b/sky/packages/sky/lib/src/material/floating_action_button.dart
index b7883a7..1a6d7d3 100644
--- a/sky/packages/sky/lib/src/material/floating_action_button.dart
+++ b/sky/packages/sky/lib/src/material/floating_action_button.dart
@@ -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';
diff --git a/sky/packages/sky/lib/src/material/icon.dart b/sky/packages/sky/lib/src/material/icon.dart
index 46b46ab..0257564 100644
--- a/sky/packages/sky/lib/src/material/icon.dart
+++ b/sky/packages/sky/lib/src/material/icon.dart
@@ -8,50 +8,8 @@
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)
diff --git a/sky/packages/sky/lib/src/material/icon_button.dart b/sky/packages/sky/lib/src/material/icon_button.dart
index 4ea19a3..f7dd289 100644
--- a/sky/packages/sky/lib/src/material/icon_button.dart
+++ b/sky/packages/sky/lib/src/material/icon_button.dart
@@ -7,6 +7,7 @@
import 'package:flutter/widgets.dart';
import 'icon.dart';
+import 'icon_theme_data.dart';
class IconButton extends StatelessComponent {
const IconButton({
diff --git a/sky/packages/sky/lib/src/material/icon_theme.dart b/sky/packages/sky/lib/src/material/icon_theme.dart
new file mode 100644
index 0000000..38723ff
--- /dev/null
+++ b/sky/packages/sky/lib/src/material/icon_theme.dart
@@ -0,0 +1,32 @@
+// 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');
+ }
+}
diff --git a/sky/packages/sky/lib/src/material/icon_theme_data.dart b/sky/packages/sky/lib/src/material/icon_theme_data.dart
new file mode 100644
index 0000000..9ba71ab
--- /dev/null
+++ b/sky/packages/sky/lib/src/material/icon_theme_data.dart
@@ -0,0 +1,21 @@
+// 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';
+}
diff --git a/sky/packages/sky/lib/src/material/list_item.dart b/sky/packages/sky/lib/src/material/list_item.dart
index 9f59a7a..0352f30 100644
--- a/sky/packages/sky/lib/src/material/list_item.dart
+++ b/sky/packages/sky/lib/src/material/list_item.dart
@@ -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 @@
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,
diff --git a/sky/packages/sky/lib/src/material/material_list.dart b/sky/packages/sky/lib/src/material/material_list.dart
new file mode 100644
index 0000000..105a8b7
--- /dev/null
+++ b/sky/packages/sky/lib/src/material/material_list.dart
@@ -0,0 +1,64 @@
+// 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
+ );
+ }
+}
diff --git a/sky/packages/sky/lib/src/material/tabs.dart b/sky/packages/sky/lib/src/material/tabs.dart
index 3a9d40e..798f336 100644
--- a/sky/packages/sky/lib/src/material/tabs.dart
+++ b/sky/packages/sky/lib/src/material/tabs.dart
@@ -13,9 +13,10 @@
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 @@
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 @@
}
Widget content = new IconTheme(
- data: new IconThemeData(color: iconThemeColor),
+ data: iconTheme,
child: new DefaultTextStyle(
style: textStyle,
child: new BuilderTransition(
diff --git a/sky/packages/sky/lib/src/material/theme_data.dart b/sky/packages/sky/lib/src/material/theme_data.dart
index 3b789d0..8e79255 100644
--- a/sky/packages/sky/lib/src/material/theme_data.dart
+++ b/sky/packages/sky/lib/src/material/theme_data.dart
@@ -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 @@
/// 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;
diff --git a/sky/packages/sky/lib/src/material/tool_bar.dart b/sky/packages/sky/lib/src/material/tool_bar.dart
index b2f5daa..c4ac519 100644
--- a/sky/packages/sky/lib/src/material/tool_bar.dart
+++ b/sky/packages/sky/lib/src/material/tool_bar.dart
@@ -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 @@
this.center,
this.right,
this.level: 2,
- this.backgroundColor
+ this.backgroundColor,
+ this.textTheme
}) : super(key: key);
final Widget left;
@@ -25,22 +27,22 @@
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>();