expand_icon.dart 3.23 KB
Newer Older
1 2 3 4
// 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.

5
import 'package:flutter/foundation.dart';
6 7 8 9 10
import 'package:flutter/widgets.dart';

import 'colors.dart';
import 'debug.dart';
import 'icon_button.dart';
11
import 'icons.dart';
12 13 14 15 16 17 18 19 20 21 22
import 'theme.dart';

/// A widget representing a rotating expand/collapse button. The icon rotates
/// 180 deg when pressed, then reverts the animation on a second press.
/// The underlying icon is [Icons.expand_more].
///
/// See [IconButton] for a more general implementation of a pressable button
/// with an icon.
class ExpandIcon extends StatefulWidget {
  /// Creates an [ExpandIcon] with the given padding, and a callback that is
  /// triggered when the icon is pressed.
23
  const ExpandIcon({
24
    Key key,
25
    this.isExpanded: false,
26 27 28
    this.size: 24.0,
    @required this.onPressed,
    this.padding: const EdgeInsets.all(8.0)
29 30 31 32
  }) : assert(isExpanded != null),
       assert(size != null),
       assert(padding != null),
       super(key: key);
33

34 35 36 37 38 39
  /// Whether the icon is in an expanded state.
  ///
  /// Rebuilding the widget with a different [isExpanded] value will trigger
  /// the animation, but will not trigger the [onPressed] callback.
  final bool isExpanded;

40 41 42 43 44 45
  /// The size of the icon.
  ///
  /// This property must not be null. It defaults to 24.0.
  final double size;

  /// The callback triggered when the icon is pressed and the state changes
46
  /// between expanded and collapsed. The value passed to the current state.
47 48 49 50
  ///
  /// If this is set to null, the button will be disabled.
  final ValueChanged<bool> onPressed;

51
  /// The padding around the icon. The entire padded icon will react to input
52 53 54
  /// gestures.
  ///
  /// This property must not be null. It defaults to 8.0 padding on all sides.
55
  final EdgeInsetsGeometry padding;
56 57 58 59 60

  @override
  _ExpandIconState createState() => new _ExpandIconState();
}

61
class _ExpandIconState extends State<ExpandIcon> with SingleTickerProviderStateMixin {
62 63 64 65 66 67
  AnimationController _controller;
  Animation<double> _iconTurns;

  @override
  void initState() {
    super.initState();
68
    _controller = new AnimationController(duration: kThemeAnimationDuration, vsync: this);
69 70 71 72 73 74 75 76 77 78 79 80 81 82
    _iconTurns = new Tween<double>(begin: 0.0, end: 0.5).animate(
      new CurvedAnimation(
        parent: _controller,
        curve: Curves.fastOutSlowIn
      )
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

83
  @override
84
  void didUpdateWidget(ExpandIcon oldWidget) {
85
    super.didUpdateWidget(oldWidget);
86 87
    if (widget.isExpanded != oldWidget.isExpanded) {
      if (widget.isExpanded) {
88
        _controller.forward();
89
      } else {
90
        _controller.reverse();
91 92 93
      }
    }
  }
94

95
  void _handlePressed() {
96 97
    if (widget.onPressed != null)
      widget.onPressed(widget.isExpanded);
98 99 100 101 102 103
  }

  @override
  Widget build(BuildContext context) {
    assert(debugCheckHasMaterial(context));
    return new IconButton(
104
      padding: widget.padding,
105
      color: Colors.black38,
106
      onPressed: widget.onPressed == null ? null : _handlePressed,
107 108
      icon: new RotationTransition(
        turns: _iconTurns,
109
        child: const Icon(Icons.expand_more)
110 111 112 113
      )
    );
  }
}