two_level_list.dart 8.16 KB
Newer Older
Hans Muller's avatar
Hans Muller committed
1 2 3 4 5 6 7
// Copyright 2016 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 'colors.dart';
8
import 'icons.dart';
9
import 'list_tile.dart';
Hans Muller's avatar
Hans Muller committed
10 11 12
import 'theme.dart';
import 'theme_data.dart';

13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
/// This enum is deprecated. Please use [ListTileTheme] instead.
enum MaterialListType {
  /// A list tile that contains a single line of text.
  oneLine,

  /// A list tile that contains a [CircleAvatar] followed by a single line of text.
  oneLineWithAvatar,

  /// A list tile that contains two lines of text.
  twoLine,

  /// A list tile that contains three lines of text.
  threeLine,
}

/// This constant is deprecated. The [ListTile] class sizes itself based on
/// its content and [ListTileTheme].
@deprecated
Map<MaterialListType, double> kListTileExtent = const <MaterialListType, double>{
  MaterialListType.oneLine: 48.0,
  MaterialListType.oneLineWithAvatar: 56.0,
  MaterialListType.twoLine: 72.0,
  MaterialListType.threeLine: 88.0,
};

38
const Duration _kExpand = Duration(milliseconds: 200);
Hans Muller's avatar
Hans Muller committed
39

40 41
/// This class is deprecated. Please use [ListTile] instead.
@deprecated
42
class TwoLevelListItem extends StatelessWidget {
43
  /// Creates an item in a two-level list.
44
  const TwoLevelListItem({
Hans Muller's avatar
Hans Muller committed
45
    Key key,
46
    this.leading,
47
    @required this.title,
48
    this.trailing,
49
    this.enabled = true,
Hans Muller's avatar
Hans Muller committed
50 51
    this.onTap,
    this.onLongPress
52 53
  }) : assert(title != null),
       super(key: key);
Hans Muller's avatar
Hans Muller committed
54

55 56 57
  /// A widget to display before the title.
  ///
  /// Typically a [CircleAvatar] widget.
58
  final Widget leading;
59 60 61 62

  /// The primary content of the list item.
  ///
  /// Typically a [Text] widget.
63
  final Widget title;
64 65 66 67

  /// A widget to display after the title.
  ///
  /// Typically an [Icon] widget.
68
  final Widget trailing;
69 70 71

  /// Whether this list item is interactive.
  ///
72
  /// If false, this list item is styled with the disabled color from the
73 74 75 76 77 78 79
  /// current [Theme] and the [onTap] and [onLongPress] callbacks are
  /// inoperative.
  final bool enabled;

  /// Called when the user taps this list item.
  ///
  /// Inoperative if [enabled] is false.
Hans Muller's avatar
Hans Muller committed
80
  final GestureTapCallback onTap;
81 82 83 84

  /// Called when the user long-presses on this list item.
  ///
  /// Inoperative if [enabled] is false.
Hans Muller's avatar
Hans Muller committed
85 86
  final GestureLongPressCallback onLongPress;

87
  @override
Hans Muller's avatar
Hans Muller committed
88 89 90 91 92
  Widget build(BuildContext context) {
    final TwoLevelList parentList = context.ancestorWidgetOfExactType(TwoLevelList);
    assert(parentList != null);

    return new SizedBox(
93 94
      height: kListTileExtent[parentList.type],
      child: new ListTile(
95 96 97
        leading: leading,
        title: title,
        trailing: trailing,
98
        enabled: enabled,
Hans Muller's avatar
Hans Muller committed
99 100 101 102 103 104 105
        onTap: onTap,
        onLongPress: onLongPress
      )
    );
  }
}

106 107
/// This class is deprecated. Please use [ExpansionTile] instead.
@deprecated
108
class TwoLevelSublist extends StatefulWidget {
109
  /// Creates an item in a two-level list that can expand and collapse.
110
  const TwoLevelSublist({
111 112
    Key key,
    this.leading,
113
    @required this.title,
114
    this.backgroundColor,
115
    this.onOpenChanged,
116
    this.children = const <Widget>[],
117
  }) : super(key: key);
Hans Muller's avatar
Hans Muller committed
118

119 120 121
  /// A widget to display before the title.
  ///
  /// Typically a [CircleAvatar] widget.
122
  final Widget leading;
123 124 125 126

  /// The primary content of the list item.
  ///
  /// Typically a [Text] widget.
127
  final Widget title;
128 129 130 131

  /// Called when the sublist expands or collapses.
  ///
  /// When the sublist starts expanding, this function is called with the value
132
  /// true. When the sublist starts collapsing, this function is called with
133
  /// the value false.
134
  final ValueChanged<bool> onOpenChanged;
135 136 137 138

  /// The widgets that are displayed when the sublist expands.
  ///
  /// Typically [TwoLevelListItem] widgets.
Hans Muller's avatar
Hans Muller committed
139
  final List<Widget> children;
140 141

  /// The color to display behind the sublist when expanded.
142
  final Color backgroundColor;
Hans Muller's avatar
Hans Muller committed
143

144
  @override
Hans Muller's avatar
Hans Muller committed
145 146 147
  _TwoLevelSublistState createState() => new _TwoLevelSublistState();
}

148
@deprecated
149
class _TwoLevelSublistState extends State<TwoLevelSublist> with SingleTickerProviderStateMixin {
Hans Muller's avatar
Hans Muller committed
150 151 152 153 154 155
  AnimationController _controller;
  CurvedAnimation _easeOutAnimation;
  CurvedAnimation _easeInAnimation;
  ColorTween _borderColor;
  ColorTween _headerColor;
  ColorTween _iconColor;
156
  ColorTween _backgroundColor;
Hans Muller's avatar
Hans Muller committed
157 158 159 160
  Animation<double> _iconTurns;

  bool _isExpanded = false;

161
  @override
Hans Muller's avatar
Hans Muller committed
162 163
  void initState() {
    super.initState();
164
    _controller = new AnimationController(duration: _kExpand, vsync: this);
Hans Muller's avatar
Hans Muller committed
165 166 167 168 169 170
    _easeOutAnimation = new CurvedAnimation(parent: _controller, curve: Curves.easeOut);
    _easeInAnimation = new CurvedAnimation(parent: _controller, curve: Curves.easeIn);
    _borderColor = new ColorTween(begin: Colors.transparent);
    _headerColor = new ColorTween();
    _iconColor = new ColorTween();
    _iconTurns = new Tween<double>(begin: 0.0, end: 0.5).animate(_easeInAnimation);
171
    _backgroundColor = new ColorTween();
Hans Muller's avatar
Hans Muller committed
172 173 174 175 176 177

    _isExpanded = PageStorage.of(context)?.readState(context) ?? false;
    if (_isExpanded)
      _controller.value = 1.0;
  }

178 179
  @override
  void dispose() {
180
    _controller.dispose();
181 182 183
    super.dispose();
  }

Hans Muller's avatar
Hans Muller committed
184 185 186 187 188 189 190 191 192
  void _handleOnTap() {
    setState(() {
      _isExpanded = !_isExpanded;
      if (_isExpanded)
        _controller.forward();
      else
        _controller.reverse();
      PageStorage.of(context)?.writeState(context, _isExpanded);
    });
193 194
    if (widget.onOpenChanged != null)
      widget.onOpenChanged(_isExpanded);
Hans Muller's avatar
Hans Muller committed
195 196 197 198 199
  }

  Widget buildList(BuildContext context, Widget child) {
    return new Container(
      decoration: new BoxDecoration(
200
        color: _backgroundColor.evaluate(_easeOutAnimation),
Hans Muller's avatar
Hans Muller committed
201 202 203 204 205 206 207
        border: new Border(
          top: new BorderSide(color: _borderColor.evaluate(_easeOutAnimation)),
          bottom: new BorderSide(color: _borderColor.evaluate(_easeOutAnimation))
        )
      ),
      child: new Column(
        children: <Widget>[
208
          IconTheme.merge(
209 210 211
            data: new IconThemeData(color: _iconColor.evaluate(_easeInAnimation)),
            child: new TwoLevelListItem(
              onTap: _handleOnTap,
212
              leading: widget.leading,
213
              title: new DefaultTextStyle(
214
                style: Theme.of(context).textTheme.subhead.copyWith(color: _headerColor.evaluate(_easeInAnimation)),
215
                child: widget.title
216 217 218
              ),
              trailing: new RotationTransition(
                turns: _iconTurns,
219
                child: const Icon(Icons.expand_more)
Hans Muller's avatar
Hans Muller committed
220 221 222 223 224 225
              )
            )
          ),
          new ClipRect(
            child: new Align(
              heightFactor: _easeInAnimation.value,
226
              child: new Column(children: widget.children)
Hans Muller's avatar
Hans Muller committed
227 228 229 230 231 232 233
            )
          )
        ]
      )
    );
  }

234
  @override
Hans Muller's avatar
Hans Muller committed
235 236 237 238
  Widget build(BuildContext context) {
    final ThemeData theme = Theme.of(context);
    _borderColor.end = theme.dividerColor;
    _headerColor
239
      ..begin = theme.textTheme.subhead.color
Hans Muller's avatar
Hans Muller committed
240 241
      ..end = theme.accentColor;
    _iconColor
242
      ..begin = theme.unselectedWidgetColor
Hans Muller's avatar
Hans Muller committed
243
      ..end = theme.accentColor;
244 245
    _backgroundColor
      ..begin = Colors.transparent
246
      ..end = widget.backgroundColor ?? Colors.transparent;
Hans Muller's avatar
Hans Muller committed
247 248

    return new AnimatedBuilder(
249 250
      animation: _controller.view,
      builder: buildList
Hans Muller's avatar
Hans Muller committed
251 252 253 254
    );
  }
}

255 256
/// This class is deprecated. Please use [ListView] and [ListTileTheme] instead.
@deprecated
257
class TwoLevelList extends StatelessWidget {
258 259 260
  /// Creates a scrollable list of items that can expand and collapse.
  ///
  /// The [type] argument must not be null.
261
  const TwoLevelList({
262
    Key key,
263 264
    this.children = const <Widget>[],
    this.type = MaterialListType.twoLine,
265
    this.padding,
266 267
  }) : assert(type != null),
       super(key: key);
Hans Muller's avatar
Hans Muller committed
268

269 270 271 272 273
  /// The widgets to display in this list.
  ///
  /// Typically [TwoLevelListItem] or [TwoLevelSublist] widgets.
  final List<Widget> children;

274
  /// The kind of [ListTile] contained in this list.
Hans Muller's avatar
Hans Muller committed
275
  final MaterialListType type;
276 277

  /// The amount of space by which to inset the children inside the viewport.
278
  final EdgeInsetsGeometry padding;
Hans Muller's avatar
Hans Muller committed
279

280
  @override
281
  Widget build(BuildContext context) {
282
    return new ListView(
283
      padding: padding,
284
      shrinkWrap: true,
285
      children: KeyedSubtree.ensureUniqueKeysForList(children),
286
    );
287
  }
Hans Muller's avatar
Hans Muller committed
288
}