list_item.dart 4.98 KB
Newer Older
Adam Barth's avatar
Adam Barth committed
1 2 3 4 5 6
// 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';

7
import 'debug.dart';
Adam Barth's avatar
Adam Barth committed
8
import 'ink_well.dart';
Hans Muller's avatar
Hans Muller committed
9
import 'theme.dart';
Adam Barth's avatar
Adam Barth committed
10

Hans Muller's avatar
Hans Muller committed
11
/// Material List items are one to three lines of text optionally flanked by icons.
12 13
/// Icons are defined with the [leading] and [trailing] parameters. The first line of text
/// is not optional and is specified with [title]. The value of [subtitle] will
Hans Muller's avatar
Hans Muller committed
14
/// occupy the space allocated for an aditional line of text, or two lines if
15
/// isThreeLine: true is specified. If dense: true is specified then the overall
Hans Muller's avatar
Hans Muller committed
16
/// height of this list item and the size of the DefaultTextStyles that wrap
17
/// the [title] and [subtitle] widget are reduced.
18
class ListItem extends StatelessWidget {
Adam Barth's avatar
Adam Barth committed
19 20
  ListItem({
    Key key,
21 22 23 24
    this.leading,
    this.title,
    this.subtitle,
    this.trailing,
Hans Muller's avatar
Hans Muller committed
25
    this.isThreeLine: false,
26 27
    this.dense: false,
    this.enabled: true,
Adam Barth's avatar
Adam Barth committed
28 29 30
    this.onTap,
    this.onLongPress
  }) : super(key: key) {
31
    assert(isThreeLine ? subtitle != null : true);
Adam Barth's avatar
Adam Barth committed
32 33
  }

34 35 36 37
  final Widget leading;
  final Widget title;
  final Widget subtitle;
  final Widget trailing;
Hans Muller's avatar
Hans Muller committed
38
  final bool isThreeLine;
39 40
  final bool dense;
  final bool enabled;
Adam Barth's avatar
Adam Barth committed
41 42 43
  final GestureTapCallback onTap;
  final GestureLongPressCallback onLongPress;

Hans Muller's avatar
Hans Muller committed
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
  /// Add a one pixel border in between each item. If color isn't specified the
  /// dividerColor of the context's theme is used.
  static Iterable<Widget> divideItems({ BuildContext context, Iterable<Widget> items, Color color }) sync* {
    assert(items != null);
    assert(color != null || context != null);

    final Color dividerColor = color ?? Theme.of(context).dividerColor;
    final Iterator<Widget> iterator = items.iterator;
    final bool isNotEmpty = iterator.moveNext();

    Widget item = iterator.current;
    while(iterator.moveNext()) {
      yield new DecoratedBox(
        decoration: new BoxDecoration(
          border: new Border(
            bottom: new BorderSide(color: dividerColor)
          )
        ),
        child: item
      );
      item = iterator.current;
    }
    if (isNotEmpty)
      yield item;
  }

Hans Muller's avatar
Hans Muller committed
70
  TextStyle primaryTextStyle(BuildContext context) {
71
    final ThemeData theme = Theme.of(context);
72
    final TextStyle style = theme.textTheme.subhead;
73
    if (!enabled) {
74
      final Color color = theme.disabledColor;
75
      return dense ? style.copyWith(fontSize: 13.0, color: color) : style.copyWith(color: color);
76
    }
77
    return dense ? style.copyWith(fontSize: 13.0) : style;
Hans Muller's avatar
Hans Muller committed
78 79 80 81
  }

  TextStyle secondaryTextStyle(BuildContext context) {
    final ThemeData theme = Theme.of(context);
82 83
    final Color color = theme.textTheme.caption.color;
    final TextStyle style = theme.textTheme.body1;
84
    return dense ? style.copyWith(color: color, fontSize: 12.0) : style.copyWith(color: color);
Hans Muller's avatar
Hans Muller committed
85 86
  }

87
  @override
Adam Barth's avatar
Adam Barth committed
88
  Widget build(BuildContext context) {
89
    assert(debugCheckHasMaterial(context));
90
    final bool isTwoLine = !isThreeLine && subtitle != null;
Hans Muller's avatar
Hans Muller committed
91 92 93
    final bool isOneLine = !isThreeLine && !isTwoLine;
    double itemHeight;
    if (isOneLine)
94
      itemHeight = dense ? 48.0 : 56.0;
Hans Muller's avatar
Hans Muller committed
95
    else if (isTwoLine)
96
      itemHeight = dense ? 60.0 : 72.0;
Hans Muller's avatar
Hans Muller committed
97
    else
98
      itemHeight = dense ? 76.0 : 88.0;
Hans Muller's avatar
Hans Muller committed
99 100 101

    double iconMarginTop = 0.0;
    if (isThreeLine)
102
      iconMarginTop = dense ? 8.0 : 16.0;
Hans Muller's avatar
Hans Muller committed
103 104 105

    // Overall, the list item is a Row() with these children.
    final List<Widget> children = <Widget>[];
Adam Barth's avatar
Adam Barth committed
106

107
    if (leading != null) {
Adam Barth's avatar
Adam Barth committed
108
      children.add(new Container(
109
        margin: new EdgeInsets.only(right: 16.0, top: iconMarginTop),
Adam Barth's avatar
Adam Barth committed
110
        width: 40.0,
Hans Muller's avatar
Hans Muller committed
111 112
        child: new Align(
          alignment: new FractionalOffset(0.0, isThreeLine ? 0.0 : 0.5),
113
          child: leading
Hans Muller's avatar
Hans Muller committed
114
        )
Adam Barth's avatar
Adam Barth committed
115 116 117
      ));
    }

Hans Muller's avatar
Hans Muller committed
118 119
    final Widget primaryLine = new DefaultTextStyle(
      style: primaryTextStyle(context),
120
      child: title ?? new Container()
Hans Muller's avatar
Hans Muller committed
121 122 123 124
    );
    Widget center = primaryLine;
    if (isTwoLine || isThreeLine) {
      center = new Column(
125 126
        mainAxisAlignment: MainAxisAlignment.collapse,
        crossAxisAlignment: CrossAxisAlignment.start,
Hans Muller's avatar
Hans Muller committed
127 128 129 130
        children: <Widget>[
          primaryLine,
          new DefaultTextStyle(
            style: secondaryTextStyle(context),
131
            child: subtitle
Hans Muller's avatar
Hans Muller committed
132 133 134 135
          )
        ]
      );
    }
Adam Barth's avatar
Adam Barth committed
136 137 138 139
    children.add(new Flexible(
      child: center
    ));

140
    if (trailing != null) {
Adam Barth's avatar
Adam Barth committed
141
      children.add(new Container(
142
        margin: new EdgeInsets.only(left: 16.0, top: iconMarginTop),
Hans Muller's avatar
Hans Muller committed
143 144
        child: new Align(
          alignment: new FractionalOffset(1.0, isThreeLine ? 0.0 : 0.5),
145
          child: trailing
Hans Muller's avatar
Hans Muller committed
146
        )
Adam Barth's avatar
Adam Barth committed
147 148 149
      ));
    }

150
    return new InkWell(
151 152
      onTap: enabled ? onTap : null,
      onLongPress: enabled ? onLongPress : null,
Hans Muller's avatar
Hans Muller committed
153 154
      child: new Container(
        height: itemHeight,
155
        padding: const EdgeInsets.symmetric(horizontal: 16.0),
Hans Muller's avatar
Hans Muller committed
156
        child: new Row(
157
          crossAxisAlignment: CrossAxisAlignment.center,
Hans Muller's avatar
Hans Muller committed
158 159
          children: children
        )
Adam Barth's avatar
Adam Barth committed
160 161 162 163
      )
    );
  }
}