icon_button.dart 8.62 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 6
import 'dart:math' as math;

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

10
import 'debug.dart';
11
import 'icons.dart';
12
import 'ink_well.dart';
13
import 'material.dart';
14
import 'theme.dart';
Hixie's avatar
Hixie committed
15
import 'tooltip.dart';
16

17
// Minimum logical pixel size of the IconButton.
18
// See: <https://material.io/guidelines/layout/metrics-keylines.html#metrics-keylines-touch-target-size>
19
const double _kMinButtonSize = 48.0;
20

21
/// A material design icon button.
22 23
///
/// An icon button is a picture printed on a [Material] widget that reacts to
24
/// touches by filling with color (ink).
25
///
26 27
/// Icon buttons are commonly used in the [AppBar.actions] field, but they can
/// be used in many other places as well.
28
///
29 30
/// If the [onPressed] callback is null, then the button will be disabled and
/// will not react to touch.
31
///
32 33
/// Requires one of its ancestors to be a [Material] widget.
///
34
/// The hit region of an icon button will, if possible, be at least 48.0 pixels
35 36 37 38
/// in size, regardless of the actual [iconSize], to satisfy the [touch target
/// size](https://material.io/guidelines/layout/metrics-keylines.html#metrics-keylines-touch-target-size)
/// requirements in the Material Design specification. The [alignment] controls
/// how the icon itself is positioned within the hit region.
39
///
40 41 42 43 44 45 46 47 48 49
/// ## Sample code
///
/// ```dart
/// new IconButton(
///   icon: new Icon(Icons.volume_up),
///   tooltip: 'Increase volume by 10%',
///   onPressed: () { setState(() { _volume *= 1.1; }); },
/// )
/// ```
///
50 51
/// See also:
///
52 53 54 55 56 57 58
///  * [Icons], a library of predefined icons.
///  * [BackButton], an icon button for a "back" affordance which adapts to the
///    current platform's conventions.
///  * [CloseButton], an icon button for closing pages.
///  * [AppBar], to show a toolbar at the top of an application.
///  * [RaisedButton] and [FlatButton], for buttons with text in them.
///  * [InkResponse] and [InkWell], for the ink splash effect itself.
59
class IconButton extends StatelessWidget {
60 61 62 63 64 65
  /// Creates an icon button.
  ///
  /// Icon buttons are commonly used in the [AppBar.actions] field, but they can
  /// be used in many other places as well.
  ///
  /// Requires one of its ancestors to be a [Material] widget.
66
  ///
67
  /// The [iconSize], [padding], and [alignment] arguments must not be null (though
68 69
  /// they each have default values).
  ///
Ian Hickson's avatar
Ian Hickson committed
70 71
  /// The [icon] argument must be specified, and is typically either an [Icon]
  /// or an [ImageIcon].
72 73
  const IconButton({
    Key key,
74
    this.iconSize: 24.0,
75
    this.padding: const EdgeInsets.all(8.0),
76
    this.alignment: Alignment.center,
77
    @required this.icon,
78
    this.color,
79 80
    this.highlightColor,
    this.splashColor,
81
    this.disabledColor,
82
    @required this.onPressed,
Hixie's avatar
Hixie committed
83
    this.tooltip
84 85 86 87 88
  }) : assert(iconSize != null),
       assert(padding != null),
       assert(alignment != null),
       assert(icon != null),
       super(key: key);
89

Adam Barth's avatar
Adam Barth committed
90
  /// The size of the icon inside the button.
91 92
  ///
  /// This property must not be null. It defaults to 24.0.
93 94 95 96 97 98 99
  ///
  /// The size given here is passed down to the widget in the [icon] property
  /// via an [IconTheme]. Setting the size here instead of in, for example, the
  /// [Icon.size] property allows the [IconButton] to size the splash area to
  /// fit the [Icon]. If you were to set the size of the [Icon] using
  /// [Icon.size] instead, then the [IconButton] would default to 24.0 and then
  /// the [Icon] itself would likely get clipped.
100
  final double iconSize;
Adam Barth's avatar
Adam Barth committed
101

102 103
  /// The padding around the button's icon. The entire padded icon will react
  /// to input gestures.
104 105
  ///
  /// This property must not be null. It defaults to 8.0 padding on all sides.
106
  final EdgeInsetsGeometry padding;
107 108

  /// Defines how the icon is positioned within the IconButton.
109
  ///
110
  /// This property must not be null. It defaults to [Alignment.center].
111 112 113 114 115 116 117
  ///
  /// See also:
  ///
  ///  * [Alignment], a class with convenient constants typically used to
  ///    specify an [AlignmentGeometry].
  ///  * [AlignmentDirectional], like [Alignment] for specifying alignments
  ///    relative to text direction.
118
  final AlignmentGeometry alignment;
119

Ian Hickson's avatar
Ian Hickson committed
120 121
  /// The icon to display inside the button.
  ///
122
  /// The [Icon.size] and [Icon.color] of the icon is configured automatically
123
  /// based on the [iconSize] and [color] properties of _this_ widget using an
124 125
  /// [IconTheme] and therefore should not be explicitly given in the icon
  /// widget.
126 127
  ///
  /// This property must not be null.
Ian Hickson's avatar
Ian Hickson committed
128 129 130
  ///
  /// See [Icon], [ImageIcon].
  final Widget icon;
Adam Barth's avatar
Adam Barth committed
131

132
  /// The color to use for the icon inside the button, if the icon is enabled.
Ian Hickson's avatar
Ian Hickson committed
133
  /// Defaults to leaving this up to the [icon] widget.
134 135 136 137
  ///
  /// The icon is enabled if [onPressed] is not null.
  ///
  /// See also [disabledColor].
138 139 140
  ///
  /// ```dart
  ///  new IconButton(
141
  ///    color: Colors.blue,
142 143 144 145
  ///    onPressed: _handleTap,
  ///    icon: Icons.widgets,
  ///  ),
  /// ```
Adam Barth's avatar
Adam Barth committed
146
  final Color color;
147

148 149 150 151 152 153 154 155 156 157 158
  /// The primary color of the button when the button is in the down (pressed) state.
  /// The splash is represented as a circular overlay that appears above the
  /// [highlightColor] overlay. The splash overlay has a center point that matches
  /// the hit point of the user touch event. The splash overlay will expand to
  /// fill the button area if the touch is held for long enough time. If the splash
  /// color has transparency then the highlight and button color will show through.
  ///
  /// Defaults to the Theme's splash color, [ThemeData.splashColor].
  final Color splashColor;

  /// The secondary color of the button when the button is in the down (pressed)
159
  /// state. The highlight color is represented as a solid color that is overlaid over the
160 161 162 163 164 165
  /// button color (if any). If the highlight color has transparency, the button color
  /// will show through. The highlight fades in quickly as the button is held down.
  ///
  /// Defaults to the Theme's highlight color, [ThemeData.highlightColor].
  final Color highlightColor;

166 167 168 169 170 171 172 173
  /// The color to use for the icon inside the button, if the icon is disabled.
  /// Defaults to the [ThemeData.disabledColor] of the current [Theme].
  ///
  /// The icon is disabled if [onPressed] is null.
  ///
  /// See also [color].
  final Color disabledColor;

174
  /// The callback that is called when the button is tapped or otherwise activated.
175 176
  ///
  /// If this is set to null, the button will be disabled.
177
  final VoidCallback onPressed;
Adam Barth's avatar
Adam Barth committed
178 179 180 181 182

  /// 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.
Hixie's avatar
Hixie committed
183
  final String tooltip;
184

185
  @override
186
  Widget build(BuildContext context) {
187
    assert(debugCheckHasMaterial(context));
188 189 190 191 192
    Color currentColor;
    if (onPressed != null)
      currentColor = color;
    else
      currentColor = disabledColor ?? Theme.of(context).disabledColor;
193

194 195
    Widget result = new Semantics(
      button: true,
196
      enabled: onPressed != null,
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
      child: new ConstrainedBox(
        constraints: const BoxConstraints(minWidth: _kMinButtonSize, minHeight: _kMinButtonSize),
        child: new Padding(
          padding: padding,
          child: new SizedBox(
            height: iconSize,
            width: iconSize,
            child: new Align(
              alignment: alignment,
              child: IconTheme.merge(
                data: new IconThemeData(
                  size: iconSize,
                  color: currentColor
                ),
                child: icon
212
              ),
213 214 215 216
            ),
          ),
        ),
      ),
217
    );
218

Hixie's avatar
Hixie committed
219 220 221 222 223 224
    if (tooltip != null) {
      result = new Tooltip(
        message: tooltip,
        child: result
      );
    }
Hixie's avatar
Hixie committed
225 226
    return new InkResponse(
      onTap: onPressed,
227
      child: result,
228 229
      highlightColor: highlightColor ?? Theme.of(context).highlightColor,
      splashColor: splashColor ?? Theme.of(context).splashColor,
230 231 232 233 234
      radius: math.max(
        Material.defaultSplashRadius,
        (iconSize + math.min(padding.horizontal, padding.vertical)) * 0.7,
        // x 0.5 for diameter -> radius and + 40% overflow derived from other Material apps.
      ),
Hixie's avatar
Hixie committed
235
    );
236
  }
Hixie's avatar
Hixie committed
237

238
  @override
239 240 241 242 243
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(new DiagnosticsProperty<Widget>('icon', icon, showName: false));
    properties.add(new ObjectFlagProperty<VoidCallback>('onPressed', onPressed, ifNull: 'disabled'));
    properties.add(new StringProperty('tooltip', tooltip, defaultValue: null, quoted: false));
Hixie's avatar
Hixie committed
244
  }
245
}