floating_action_button.dart 4.06 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 23 24 25 26 27 28 29 30 31 32 33
/// A material design "floating action button".
///
/// A floating action button is a circular icon button that hovers over content
/// to promote a primary action in the application.
///
/// 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
/// be disabled, will not react to touch.
///
34
/// See also: <https://www.google.com/design/spec/components/buttons-floating-action-button.html>
35
class FloatingActionButton extends StatefulWidget {
36
  const FloatingActionButton({
37
    Key key,
38
    this.child,
Adam Barth's avatar
Adam Barth committed
39
    this.tooltip,
40
    this.backgroundColor,
41 42
    this.elevation: 6,
    this.highlightElevation: 12,
Devon Carew's avatar
Devon Carew committed
43 44
    this.onPressed,
    this.mini: false
45 46
  }) : super(key: key);

47
  final Widget child;
Adam Barth's avatar
Adam Barth committed
48
  final String tooltip;
49
  final Color backgroundColor;
50 51 52 53

  /// The callback that is invoked when the button is tapped or otherwise activated.
  ///
  /// If this is set to null, the button will be disabled.
54
  final VoidCallback onPressed;
55 56
  final int elevation;
  final int highlightElevation;
Devon Carew's avatar
Devon Carew committed
57
  final bool mini;
58

59
  @override
60
  _FloatingActionButtonState createState() => new _FloatingActionButtonState();
61
}
62

63
class _FloatingActionButtonState extends State<FloatingActionButton> {
64
  Animation<double> _childSegue;
65
  AnimationController _childSegueController;
66

67
  @override
68 69
  void initState() {
    super.initState();
70 71 72 73 74 75 76 77 78
    _childSegueController = new AnimationController(duration: _kChildSegue)
      ..forward();
    _childSegue = new Tween<double>(
      begin: -0.125,
      end: 0.0
    ).animate(new CurvedAnimation(
      parent: _childSegueController,
      curve: _kChildSegueInterval
    ));
79 80
  }

81
  @override
82 83 84 85
  void didUpdateConfig(FloatingActionButton oldConfig) {
    super.didUpdateConfig(oldConfig);
    if (Widget.canUpdate(oldConfig.child, config.child) && config.backgroundColor == oldConfig.backgroundColor)
      return;
86 87 88
    _childSegueController
      ..value = 0.0
      ..forward();
89 90
  }

91 92 93 94 95 96 97 98
  bool _highlight = false;

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

99
  @override
100
  Widget build(BuildContext context) {
Adam Barth's avatar
Adam Barth committed
101
    Color iconColor = Colors.white;
102
    Color materialColor = config.backgroundColor;
103
    if (materialColor == null) {
104
      ThemeData themeData = Theme.of(context);
105
      materialColor = themeData.accentColor;
Adam Barth's avatar
Adam Barth committed
106
      iconColor = themeData.accentColorBrightness == ThemeBrightness.dark ? Colors.white : Colors.black;
107 108
    }

Adam Barth's avatar
Adam Barth committed
109 110
    Widget result = new Center(
      child: new IconTheme(
Adam Barth's avatar
Adam Barth committed
111
        data: new IconThemeData(color: iconColor),
Adam Barth's avatar
Adam Barth committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125
        child: new RotationTransition(
          turns: _childSegue,
          child: config.child
        )
      )
    );

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

126 127 128
    return new Material(
      color: materialColor,
      type: MaterialType.circle,
129 130
      elevation: _highlight ? config.highlightElevation : config.elevation,
      child: new Container(
Devon Carew's avatar
Devon Carew committed
131 132
        width: config.mini ? _kSizeMini : _kSize,
        height: config.mini ? _kSizeMini : _kSize,
133 134 135
        child: new InkWell(
          onTap: config.onPressed,
          onHighlightChanged: _handleHighlightChanged,
Adam Barth's avatar
Adam Barth committed
136
          child: result
137 138 139 140 141
        )
      )
    );
  }
}