floating_action_button.dart 5.25 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/widgets.dart';
6

Adam Barth's avatar
Adam Barth committed
7
import 'colors.dart';
Adam Barth's avatar
Adam Barth committed
8
import 'icon_theme_data.dart';
Adam Barth's avatar
Adam Barth committed
9
import 'icon_theme.dart';
10 11 12
import 'ink_well.dart';
import 'material.dart';
import 'theme.dart';
Adam Barth's avatar
Adam Barth committed
13
import 'tooltip.dart';
14 15 16 17

// TODO(eseidel): This needs to change based on device size?
// http://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-keylines-spacing
const double _kSize = 56.0;
Devon Carew's avatar
Devon Carew committed
18
const double _kSizeMini = 40.0;
19 20
const Duration _kChildSegue = const Duration(milliseconds: 400);
const Interval _kChildSegueInterval = const Interval(0.65, 1.0);
21

22
/// A material design floating action button.
23 24
///
/// A floating action button is a circular icon button that hovers over content
25 26
/// to promote a primary action in the application. Floating action buttons are
/// most commonly used in the [Scaffold.floatingActionButton] field.
27 28 29 30 31 32
///
/// Use at most a single floating action button per screen. Floating action
/// buttons should be used for positive actions such as "create", "share", or
/// "navigate".
///
/// If the [onPressed] callback is not specified or null, then the button will
33
/// be disabled and will not react to touch.
34
///
35 36
/// See also:
///
37
///  * [Scaffold]
38 39 40
///  * [RaisedButton]
///  * [FlatButton]
///  * <https://www.google.com/design/spec/components/buttons-floating-action-button.html>
41
class FloatingActionButton extends StatefulWidget {
42 43 44
  /// Creates a floating action button.
  ///
  /// Most commonly used in the [Scaffold.floatingActionButton] field.
45
  const FloatingActionButton({
46
    Key key,
47
    this.child,
Adam Barth's avatar
Adam Barth committed
48
    this.tooltip,
49
    this.backgroundColor,
50 51
    this.elevation: 6,
    this.highlightElevation: 12,
Devon Carew's avatar
Devon Carew committed
52 53
    this.onPressed,
    this.mini: false
54 55
  }) : super(key: key);

56
  /// The widget below this widget in the tree.
57
  final Widget child;
58

59 60 61 62
  /// Text that describes the action that will occur when the button is pressed.
  ///
  /// This text is displayed when the user long-presses on the button and is
  /// used for accessibility.
Adam Barth's avatar
Adam Barth committed
63
  final String tooltip;
64 65 66 67

  /// The color to use when filling the button.
  ///
  /// Defaults to the accent color of the current theme.
68
  final Color backgroundColor;
69 70 71 72

  /// The callback that is invoked when the button is tapped or otherwise activated.
  ///
  /// If this is set to null, the button will be disabled.
73
  final VoidCallback onPressed;
74

75
  /// The z-coordinate at which to place this button.
76 77
  ///
  /// The following elevations have defined shadows: 1, 2, 3, 4, 6, 8, 9, 12, 16, 24
78
  final int elevation;
79

80
  /// The z-coordinate at which to place this button when the user is touching the button.
81
  final int highlightElevation;
82

83 84 85 86 87
  /// Controls the size of this button.
  ///
  /// By default, floating action buttons are non-mini and have a height and
  /// width of 56.0 logical pixels. Mini floating action buttons have a height
  /// and width of 40.0 logical pixels.
Devon Carew's avatar
Devon Carew committed
88
  final bool mini;
89

90
  @override
91
  _FloatingActionButtonState createState() => new _FloatingActionButtonState();
92
}
93

94
class _FloatingActionButtonState extends State<FloatingActionButton> {
95
  Animation<double> _childSegue;
96
  AnimationController _childSegueController;
97

98
  @override
99 100
  void initState() {
    super.initState();
101 102 103 104 105 106 107 108 109
    _childSegueController = new AnimationController(duration: _kChildSegue)
      ..forward();
    _childSegue = new Tween<double>(
      begin: -0.125,
      end: 0.0
    ).animate(new CurvedAnimation(
      parent: _childSegueController,
      curve: _kChildSegueInterval
    ));
110 111
  }

112 113 114 115 116 117
  @override
  void dispose() {
    _childSegueController.dispose();
    super.dispose();
  }

118
  @override
119 120 121 122
  void didUpdateConfig(FloatingActionButton oldConfig) {
    super.didUpdateConfig(oldConfig);
    if (Widget.canUpdate(oldConfig.child, config.child) && config.backgroundColor == oldConfig.backgroundColor)
      return;
123 124 125
    _childSegueController
      ..value = 0.0
      ..forward();
126 127
  }

128 129 130 131 132 133 134 135
  bool _highlight = false;

  void _handleHighlightChanged(bool value) {
    setState(() {
      _highlight = value;
    });
  }

136
  @override
137
  Widget build(BuildContext context) {
Adam Barth's avatar
Adam Barth committed
138
    Color iconColor = Colors.white;
139
    Color materialColor = config.backgroundColor;
140
    if (materialColor == null) {
141
      ThemeData themeData = Theme.of(context);
142
      materialColor = themeData.accentColor;
Adam Barth's avatar
Adam Barth committed
143
      iconColor = themeData.accentColorBrightness == ThemeBrightness.dark ? Colors.white : Colors.black;
144 145
    }

Adam Barth's avatar
Adam Barth committed
146 147
    Widget result = new Center(
      child: new IconTheme(
Adam Barth's avatar
Adam Barth committed
148
        data: new IconThemeData(color: iconColor),
Adam Barth's avatar
Adam Barth committed
149 150 151 152 153 154 155 156 157 158 159 160 161 162
        child: new RotationTransition(
          turns: _childSegue,
          child: config.child
        )
      )
    );

    if (config.tooltip != null) {
      result = new Tooltip(
        message: config.tooltip,
        child: result
      );
    }

163 164 165
    return new Material(
      color: materialColor,
      type: MaterialType.circle,
166 167
      elevation: _highlight ? config.highlightElevation : config.elevation,
      child: new Container(
Devon Carew's avatar
Devon Carew committed
168 169
        width: config.mini ? _kSizeMini : _kSize,
        height: config.mini ? _kSizeMini : _kSize,
170 171 172
        child: new InkWell(
          onTap: config.onPressed,
          onHighlightChanged: _handleHighlightChanged,
Adam Barth's avatar
Adam Barth committed
173
          child: result
174 175 176 177 178
        )
      )
    );
  }
}