-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathicon_button.dart
396 lines (367 loc) · 13.9 KB
/
icon_button.dart
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
// Copyright 2014 The Flutter 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 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'constants.dart';
import 'debug.dart';
import 'icons.dart';
import 'ink_well.dart';
import 'material.dart';
import 'theme.dart';
import 'theme_data.dart';
import 'tooltip.dart';
// Minimum logical pixel size of the IconButton.
// See: <https://material.io/design/usability/accessibility.html#layout-typography>.
const double _kMinButtonSize = kMinInteractiveDimension;
/// A material design icon button.
///
/// An icon button is a picture printed on a [Material] widget that reacts to
/// touches by filling with color (ink).
///
/// Icon buttons are commonly used in the [AppBar.actions] field, but they can
/// be used in many other places as well.
///
/// If the [onPressed] callback is null, then the button will be disabled and
/// will not react to touch.
///
/// Requires one of its ancestors to be a [Material] widget.
///
/// The hit region of an icon button will, if possible, be at least
/// kMinInteractiveDimension pixels 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.
///
/// {@tool sample --template=stateful_widget_scaffold_center}
///
/// This sample shows an `IconButton` that uses the Material icon "volume_up" to
/// increase the volume.
///
/// 
///
/// ```dart preamble
/// double _volume = 0.0;
/// ```
///
/// ```dart
/// Widget build(BuildContext context) {
/// return Column(
/// mainAxisSize: MainAxisSize.min,
/// children: <Widget>[
/// IconButton(
/// icon: Icon(Icons.volume_up),
/// tooltip: 'Increase volume by 10',
/// onPressed: () {
/// setState(() {
/// _volume += 10;
/// });
/// },
/// ),
/// Text('Volume : $_volume')
/// ],
/// );
/// }
/// ```
/// {@end-tool}
///
/// ### Adding a filled background
///
/// Icon buttons don't support specifying a background color or other
/// background decoration because typically the icon is just displayed
/// on top of the parent widget's background. Icon buttons that appear
/// in [AppBar.actions] are an example of this.
///
/// It's easy enough to create an icon button with a filled background
/// using the [Ink] widget. The [Ink] widget renders a decoration on
/// the underlying [Material] along with the splash and highlight
/// [InkResponse] contributed by descendant widgets.
///
/// {@tool sample --template=stateless_widget_scaffold}
///
/// In this sample the icon button's background color is defined with an [Ink]
/// widget whose child is an [IconButton]. The icon button's filled background
/// is a light shade of blue, it's a filled circle, and it's as big as the
/// button is.
///
/// 
///
/// ```dart
/// Widget build(BuildContext context) {
/// return Material(
/// color: Colors.white,
/// child: Center(
/// child: Ink(
/// decoration: const ShapeDecoration(
/// color: Colors.lightBlue,
/// shape: CircleBorder(),
/// ),
/// child: IconButton(
/// icon: Icon(Icons.android),
/// color: Colors.white,
/// onPressed: () {},
/// ),
/// ),
/// ),
/// );
/// }
/// ```
/// {@end-tool}
///
/// See also:
///
/// * [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.
class IconButton extends StatelessWidget {
/// 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.
///
/// The [iconSize], [padding], [autofocus], and [alignment] arguments must not
/// be null (though they each have default values).
///
/// The [icon] argument must be specified, and is typically either an [Icon]
/// or an [ImageIcon].
const IconButton({
Key key,
this.iconSize = 24.0,
this.visualDensity,
this.padding = const EdgeInsets.all(8.0),
this.alignment = Alignment.center,
@required this.icon,
this.color,
this.focusColor,
this.hoverColor,
this.highlightColor,
this.splashColor,
this.disabledColor,
@required this.onPressed,
this.focusNode,
this.autofocus = false,
this.tooltip,
this.enableFeedback = true,
this.constraints,
}) : assert(iconSize != null),
assert(padding != null),
assert(alignment != null),
assert(autofocus != null),
assert(icon != null),
super(key: key);
/// The size of the icon inside the button.
///
/// This property must not be null. It defaults to 24.0.
///
/// 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.
final double iconSize;
/// Defines how compact the icon button's layout will be.
///
/// {@macro flutter.material.themedata.visualDensity}
///
/// See also:
///
/// * [ThemeData.visualDensity], which specifies the [density] for all widgets
/// within a [Theme].
final VisualDensity visualDensity;
/// The padding around the button's icon. The entire padded icon will react
/// to input gestures.
///
/// This property must not be null. It defaults to 8.0 padding on all sides.
final EdgeInsetsGeometry padding;
/// Defines how the icon is positioned within the IconButton.
///
/// This property must not be null. It defaults to [Alignment.center].
///
/// See also:
///
/// * [Alignment], a class with convenient constants typically used to
/// specify an [AlignmentGeometry].
/// * [AlignmentDirectional], like [Alignment] for specifying alignments
/// relative to text direction.
final AlignmentGeometry alignment;
/// The icon to display inside the button.
///
/// The [Icon.size] and [Icon.color] of the icon is configured automatically
/// based on the [iconSize] and [color] properties of _this_ widget using an
/// [IconTheme] and therefore should not be explicitly given in the icon
/// widget.
///
/// This property must not be null.
///
/// See [Icon], [ImageIcon].
final Widget icon;
/// The color for the button's icon when it has the input focus.
///
/// Defaults to [ThemeData.focusColor] of the ambient theme.
final Color focusColor;
/// The color for the button's icon when a pointer is hovering over it.
///
/// Defaults to [ThemeData.hoverColor] of the ambient theme.
final Color hoverColor;
/// The color to use for the icon inside the button, if the icon is enabled.
/// Defaults to leaving this up to the [icon] widget.
///
/// The icon is enabled if [onPressed] is not null.
///
/// ```dart
/// IconButton(
/// color: Colors.blue,
/// onPressed: _handleTap,
/// icon: Icons.widgets,
/// )
/// ```
final Color color;
/// 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)
/// state. The highlight color is represented as a solid color that is overlaid over the
/// 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;
/// 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.
final Color disabledColor;
/// The callback that is called when the button is tapped or otherwise activated.
///
/// If this is set to null, the button will be disabled.
final VoidCallback onPressed;
/// {@macro flutter.widgets.Focus.focusNode}
final FocusNode focusNode;
/// {@macro flutter.widgets.Focus.autofocus}
final bool autofocus;
/// 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.
final String tooltip;
/// Whether detected gestures should provide acoustic and/or haptic feedback.
///
/// For example, on Android a tap will produce a clicking sound and a
/// long-press will produce a short vibration, when feedback is enabled.
///
/// See also:
///
/// * [Feedback] for providing platform-specific feedback to certain actions.
final bool enableFeedback;
/// Optional size constraints for the button.
///
/// When unspecified, defaults to:
/// ```dart
/// const BoxConstraints(
/// minWidth: kMinInteractiveDimension,
/// minHeight: kMinInteractiveDimension,
/// )
/// ```
/// where [kMinInteractiveDimension] is 48.0, and then with visual density
/// applied.
///
/// The default constraints ensure that the button is accessible.
/// Specifying this parameter enables creation of buttons smaller than
/// the minimum size, but it is not recommended.
///
/// The visual density uses the [visualDensity] parameter if specified,
/// and `Theme.of(context).visualDensity` otherwise.
final BoxConstraints constraints;
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
final ThemeData theme = Theme.of(context);
Color currentColor;
if (onPressed != null)
currentColor = color;
else
currentColor = disabledColor ?? theme.disabledColor;
final VisualDensity effectiveVisualDensity = visualDensity ?? theme.visualDensity;
final BoxConstraints unadjustedConstraints = constraints ?? const BoxConstraints(
minWidth: _kMinButtonSize,
minHeight: _kMinButtonSize,
);
final BoxConstraints adjustedConstraints = effectiveVisualDensity.effectiveConstraints(unadjustedConstraints);
Widget result = ConstrainedBox(
constraints: adjustedConstraints,
child: Padding(
padding: padding,
child: SizedBox(
height: iconSize,
width: iconSize,
child: Align(
alignment: alignment,
child: IconTheme.merge(
data: IconThemeData(
size: iconSize,
color: currentColor,
),
child: icon,
),
),
),
),
);
if (tooltip != null) {
result = Tooltip(
message: tooltip,
child: result,
);
}
return Semantics(
button: true,
enabled: onPressed != null,
child: InkResponse(
focusNode: focusNode,
autofocus: autofocus,
canRequestFocus: onPressed != null,
onTap: onPressed,
enableFeedback: enableFeedback,
child: result,
focusColor: focusColor ?? Theme.of(context).focusColor,
hoverColor: hoverColor ?? Theme.of(context).hoverColor,
highlightColor: highlightColor ?? Theme.of(context).highlightColor,
splashColor: splashColor ?? Theme.of(context).splashColor,
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.
),
),
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<Widget>('icon', icon, showName: false));
properties.add(StringProperty('tooltip', tooltip, defaultValue: null, quoted: false));
properties.add(ObjectFlagProperty<VoidCallback>('onPressed', onPressed, ifNull: 'disabled'));
properties.add(ColorProperty('color', color, defaultValue: null));
properties.add(ColorProperty('disabledColor', disabledColor, defaultValue: null));
properties.add(ColorProperty('focusColor', focusColor, defaultValue: null));
properties.add(ColorProperty('hoverColor', hoverColor, defaultValue: null));
properties.add(ColorProperty('highlightColor', highlightColor, defaultValue: null));
properties.add(ColorProperty('splashColor', splashColor, defaultValue: null));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
properties.add(DiagnosticsProperty<FocusNode>('focusNode', focusNode, defaultValue: null));
}
}