two_level_list.dart 5.74 KB
Newer Older
Hans Muller's avatar
Hans Muller committed
1 2 3 4 5 6 7 8
// 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';
import 'icon.dart';
9
import 'icons.dart';
10 11
import 'icon_theme.dart';
import 'icon_theme_data.dart';
12
import 'list.dart';
Hans Muller's avatar
Hans Muller committed
13 14 15 16 17 18
import 'list_item.dart';
import 'theme.dart';
import 'theme_data.dart';

const Duration _kExpand = const Duration(milliseconds: 200);

19
class TwoLevelListItem extends StatelessWidget {
Hans Muller's avatar
Hans Muller committed
20 21
  TwoLevelListItem({
    Key key,
22 23 24
    this.leading,
    this.title,
    this.trailing,
Hans Muller's avatar
Hans Muller committed
25 26 27
    this.onTap,
    this.onLongPress
  }) : super(key: key) {
28
    assert(title != null);
Hans Muller's avatar
Hans Muller committed
29 30
  }

31 32 33
  final Widget leading;
  final Widget title;
  final Widget trailing;
Hans Muller's avatar
Hans Muller committed
34 35 36
  final GestureTapCallback onTap;
  final GestureLongPressCallback onLongPress;

37
  @override
Hans Muller's avatar
Hans Muller committed
38 39 40 41 42 43 44
  Widget build(BuildContext context) {
    final TwoLevelList parentList = context.ancestorWidgetOfExactType(TwoLevelList);
    assert(parentList != null);

    return new SizedBox(
      height: kListItemExtent[parentList.type],
      child: new ListItem(
45 46 47
        leading: leading,
        title: title,
        trailing: trailing,
Hans Muller's avatar
Hans Muller committed
48 49 50 51 52 53 54
        onTap: onTap,
        onLongPress: onLongPress
      )
    );
  }
}

55
class TwoLevelSublist extends StatefulWidget {
56 57 58 59
  TwoLevelSublist({
    Key key,
    this.leading,
    this.title,
60
    this.backgroundColor,
61 62 63
    this.onOpenChanged,
    this.children
  }) : super(key: key);
Hans Muller's avatar
Hans Muller committed
64

65 66
  final Widget leading;
  final Widget title;
67
  final ValueChanged<bool> onOpenChanged;
Hans Muller's avatar
Hans Muller committed
68
  final List<Widget> children;
69
  final Color backgroundColor;
Hans Muller's avatar
Hans Muller committed
70

71
  @override
Hans Muller's avatar
Hans Muller committed
72 73 74 75 76 77 78 79 80 81
  _TwoLevelSublistState createState() => new _TwoLevelSublistState();
}

class _TwoLevelSublistState extends State<TwoLevelSublist> {
  AnimationController _controller;
  CurvedAnimation _easeOutAnimation;
  CurvedAnimation _easeInAnimation;
  ColorTween _borderColor;
  ColorTween _headerColor;
  ColorTween _iconColor;
82
  ColorTween _backgroundColor;
Hans Muller's avatar
Hans Muller committed
83 84 85 86
  Animation<double> _iconTurns;

  bool _isExpanded = false;

87
  @override
Hans Muller's avatar
Hans Muller committed
88 89 90 91 92 93 94 95 96
  void initState() {
    super.initState();
    _controller = new AnimationController(duration: _kExpand);
    _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);
97
    _backgroundColor = new ColorTween();
Hans Muller's avatar
Hans Muller committed
98 99 100 101 102 103

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

104 105 106 107 108 109
  @override
  void dispose() {
    _controller.stop();
    super.dispose();
  }

Hans Muller's avatar
Hans Muller committed
110 111 112 113 114 115 116 117 118
  void _handleOnTap() {
    setState(() {
      _isExpanded = !_isExpanded;
      if (_isExpanded)
        _controller.forward();
      else
        _controller.reverse();
      PageStorage.of(context)?.writeState(context, _isExpanded);
    });
119 120
    if (config.onOpenChanged != null)
      config.onOpenChanged(_isExpanded);
Hans Muller's avatar
Hans Muller committed
121 122 123 124 125
  }

  Widget buildList(BuildContext context, Widget child) {
    return new Container(
      decoration: new BoxDecoration(
126
        backgroundColor: _backgroundColor.evaluate(_easeOutAnimation),
Hans Muller's avatar
Hans Muller committed
127 128 129 130 131 132 133
        border: new Border(
          top: new BorderSide(color: _borderColor.evaluate(_easeOutAnimation)),
          bottom: new BorderSide(color: _borderColor.evaluate(_easeOutAnimation))
        )
      ),
      child: new Column(
        children: <Widget>[
Ian Hickson's avatar
Ian Hickson committed
134 135
          new IconTheme.merge(
            context: context,
136 137 138 139
            data: new IconThemeData(color: _iconColor.evaluate(_easeInAnimation)),
            child: new TwoLevelListItem(
              onTap: _handleOnTap,
              leading: config.leading,
140
              title: new DefaultTextStyle(
141 142 143 144 145
                style: Theme.of(context).textTheme.subhead.copyWith(color: _headerColor.evaluate(_easeInAnimation)),
                child: config.title
              ),
              trailing: new RotationTransition(
                turns: _iconTurns,
Ian Hickson's avatar
Ian Hickson committed
146
                child: new Icon(Icons.expand_more)
Hans Muller's avatar
Hans Muller committed
147 148 149 150 151 152 153 154 155 156 157 158 159 160
              )
            )
          ),
          new ClipRect(
            child: new Align(
              heightFactor: _easeInAnimation.value,
              child: new Column(children: config.children)
            )
          )
        ]
      )
    );
  }

161
  @override
Hans Muller's avatar
Hans Muller committed
162 163 164 165
  Widget build(BuildContext context) {
    final ThemeData theme = Theme.of(context);
    _borderColor.end = theme.dividerColor;
    _headerColor
166
      ..begin = theme.textTheme.subhead.color
Hans Muller's avatar
Hans Muller committed
167 168
      ..end = theme.accentColor;
    _iconColor
169
      ..begin = theme.unselectedWidgetColor
Hans Muller's avatar
Hans Muller committed
170
      ..end = theme.accentColor;
171 172 173
    _backgroundColor
      ..begin = Colors.transparent
      ..end = config.backgroundColor ?? Colors.transparent;
Hans Muller's avatar
Hans Muller committed
174 175 176 177 178 179 180 181

    return new AnimatedBuilder(
        animation: _controller.view,
        builder: buildList
    );
  }
}

182
class TwoLevelList extends StatelessWidget {
183 184 185
  TwoLevelList({
    Key key,
    this.scrollableKey,
186
    this.children,
187
    this.type: MaterialListType.twoLine,
188
    this.padding
189
  }) : super(key: key);
Hans Muller's avatar
Hans Muller committed
190

191 192 193 194 195 196
  /// The widgets to display in this list.
  ///
  /// Typically [TwoLevelListItem] or [TwoLevelSublist] widgets.
  final List<Widget> children;

  /// The kind of [ListItem] contained in this list.
Hans Muller's avatar
Hans Muller committed
197
  final MaterialListType type;
198 199

  /// The key to use for the underlying scrollable widget.
200
  final Key scrollableKey;
201 202 203

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

205
  @override
206
  Widget build(BuildContext context) {
207
    return new Block(
208 209
      padding: padding,
      children: KeyedSubtree.ensureUniqueKeysForList(children),
210 211
      scrollableKey: scrollableKey
    );
212
  }
Hans Muller's avatar
Hans Muller committed
213
}