expand_icon.dart 3.16 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// 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 'package:meta/meta.dart';

import 'colors.dart';
import 'debug.dart';
import 'icon.dart';
import 'icons.dart';
import 'icon_button.dart';
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.
  ExpandIcon({
    Key key,
26
    this.isExpanded: false,
27 28 29 30
    this.size: 24.0,
    @required this.onPressed,
    this.padding: const EdgeInsets.all(8.0)
  }) : super(key: key) {
31
    assert(this.isExpanded != null);
32 33 34 35
    assert(this.size != null);
    assert(this.padding != null);
  }

36 37 38 39 40 41
  /// 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;

42 43 44 45 46 47
  /// 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
48
  /// between expanded and collapsed. The value passed to the current state.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
  ///
  /// If this is set to null, the button will be disabled.
  final ValueChanged<bool> onPressed;

  /// The padding around the icon. The entire padded icon will reactb to input
  /// gestures.
  ///
  /// This property must not be null. It defaults to 8.0 padding on all sides.
  final EdgeInsets padding;

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

class _ExpandIconState extends State<ExpandIcon> {
  AnimationController _controller;
  Animation<double> _iconTurns;

  @override
  void initState() {
    super.initState();
    _controller = new AnimationController(duration: kThemeAnimationDuration);
    _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();
  }

85 86 87 88
  @override
  void didUpdateConfig(ExpandIcon oldConfig) {
    if (config.isExpanded != oldConfig.isExpanded) {
      if (config.isExpanded) {
89
        _controller.forward();
90
      } else {
91
        _controller.reverse();
92 93 94
      }
    }
  }
95

96
  void _handlePressed() {
97
    if (config.onPressed != null)
98
      config.onPressed(config.isExpanded);
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
  }

  @override
  Widget build(BuildContext context) {
    assert(debugCheckHasMaterial(context));
    return new IconButton(
      padding: config.padding,
      color: Colors.black38,
      onPressed: config.onPressed == null ? null : _handlePressed,
      icon: new RotationTransition(
        turns: _iconTurns,
        child: new Icon(Icons.expand_more)
      )
    );
  }
}