floating_action_button.dart 31.8 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// 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/rendering.dart';
9
import 'package:flutter/widgets.dart';
10

11
import 'button.dart';
12
import 'color_scheme.dart';
13
import 'floating_action_button_theme.dart';
14
import 'scaffold.dart';
15
import 'text_theme.dart';
16
import 'theme.dart';
17
import 'theme_data.dart';
Adam Barth's avatar
Adam Barth committed
18
import 'tooltip.dart';
19

20 21 22 23 24
class _DefaultHeroTag {
  const _DefaultHeroTag();
  @override
  String toString() => '<default FloatingActionButton tag>';
}
25

26 27 28 29 30 31 32
enum _FloatingActionButtonType {
  regular,
  small,
  large,
  extended,
}

33
/// A material design floating action button.
34 35
///
/// A floating action button is a circular icon button that hovers over content
36 37
/// to promote a primary action in the application. Floating action buttons are
/// most commonly used in the [Scaffold.floatingActionButton] field.
38
///
39 40
/// {@youtube 560 315 https://www.youtube.com/watch?v=2uaoEDOgk_I}
///
41 42
/// Use at most a single floating action button per screen. Floating action
/// buttons should be used for positive actions such as "create", "share", or
43 44 45
/// "navigate". (If more than one floating action button is used within a
/// [Route], then make sure that each button has a unique [heroTag], otherwise
/// an exception will be thrown.)
46
///
47
/// If the [onPressed] callback is null, then the button will be disabled and
48 49 50 51
/// will not react to touch. It is highly discouraged to disable a floating
/// action button as there is no indication to the user that the button is
/// disabled. Consider changing the [backgroundColor] if disabling the floating
/// action button.
52
///
53
/// {@tool dartpad}
54
/// This example shows how to display a [FloatingActionButton] in a
55 56
/// [Scaffold], with a pink [backgroundColor] and a thumbs up [Icon].
///
57
/// ![](https://flutter.github.io/assets-for-api-docs/assets/material/floating_action_button.png)
58
///
59
/// ** See code in examples/api/lib/material/floating_action_button/floating_action_button.0.dart **
60 61
/// {@end-tool}
///
62
/// {@tool dartpad}
63
/// This example shows how to make an extended [FloatingActionButton] in a
64
/// [Scaffold], with a  pink [backgroundColor], a thumbs up [Icon] and a
65
/// [Text] label that reads "Approve".
66
///
67
/// ![](https://flutter.github.io/assets-for-api-docs/assets/material/floating_action_button_label.png)
68
///
69
/// ** See code in examples/api/lib/material/floating_action_button/floating_action_button.1.dart **
70 71
/// {@end-tool}
///
72 73
/// See also:
///
74
///  * [Scaffold], in which floating action buttons typically live.
75
///  * [ElevatedButton], a filled button whose material elevates when pressed.
76
///  * <https://material.io/design/components/buttons-floating-action-button.html>
77
class FloatingActionButton extends StatelessWidget {
78
  /// Creates a circular floating action button.
79
  ///
80
  /// The [mini] and [clipBehavior] arguments must not be null. Additionally,
81 82
  /// [elevation], [highlightElevation], and [disabledElevation] (if specified)
  /// must be non-negative.
83
  const FloatingActionButton({
84
    Key? key,
85
    this.child,
Adam Barth's avatar
Adam Barth committed
86
    this.tooltip,
87
    this.foregroundColor,
88
    this.backgroundColor,
89 90
    this.focusColor,
    this.hoverColor,
91
    this.splashColor,
92
    this.heroTag = const _DefaultHeroTag(),
93
    this.elevation,
94 95
    this.focusElevation,
    this.hoverElevation,
96 97
    this.highlightElevation,
    this.disabledElevation,
98
    required this.onPressed,
99
    this.mouseCursor,
100
    this.mini = false,
101
    this.shape,
102
    this.clipBehavior = Clip.none,
103
    this.focusNode,
104
    this.autofocus = false,
105
    this.materialTapTargetSize,
106
    this.isExtended = false,
107
    this.enableFeedback,
108
  }) : assert(elevation == null || elevation >= 0.0),
109 110
       assert(focusElevation == null || focusElevation >= 0.0),
       assert(hoverElevation == null || hoverElevation >= 0.0),
111
       assert(highlightElevation == null || highlightElevation >= 0.0),
112 113
       assert(disabledElevation == null || disabledElevation >= 0.0),
       assert(mini != null),
114
       assert(clipBehavior != null),
115
       assert(isExtended != null),
116
       assert(autofocus != null),
117 118 119
       _floatingActionButtonType = mini ? _FloatingActionButtonType.small : _FloatingActionButtonType.regular,
       _extendedLabel = null,
       extendedIconLabelSpacing = null,
120
       extendedPadding = null,
121
       extendedTextStyle = null,
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
       super(key: key);

  /// Creates a small circular floating action button.
  ///
  /// This constructor overrides the default size constraints of the floating
  /// action button.
  ///
  /// The [clipBehavior] and [autofocus] arguments must not be null.
  /// Additionally, [elevation], [focusElevation], [hoverElevation],
  /// [highlightElevation], and [disabledElevation] (if specified) must be
  /// non-negative.
  const FloatingActionButton.small({
    Key? key,
    this.child,
    this.tooltip,
    this.foregroundColor,
    this.backgroundColor,
    this.focusColor,
    this.hoverColor,
    this.splashColor,
    this.heroTag = const _DefaultHeroTag(),
    this.elevation,
    this.focusElevation,
    this.hoverElevation,
    this.highlightElevation,
    this.disabledElevation,
    required this.onPressed,
    this.mouseCursor,
    this.shape,
    this.clipBehavior = Clip.none,
    this.focusNode,
    this.autofocus = false,
    this.materialTapTargetSize,
    this.enableFeedback,
  }) : assert(elevation == null || elevation >= 0.0),
       assert(focusElevation == null || focusElevation >= 0.0),
       assert(hoverElevation == null || hoverElevation >= 0.0),
       assert(highlightElevation == null || highlightElevation >= 0.0),
       assert(disabledElevation == null || disabledElevation >= 0.0),
       assert(clipBehavior != null),
       assert(autofocus != null),
       _floatingActionButtonType = _FloatingActionButtonType.small,
       mini = true,
       isExtended = false,
       _extendedLabel = null,
       extendedIconLabelSpacing = null,
168
       extendedPadding = null,
169
       extendedTextStyle = null,
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
       super(key: key);

  /// Creates a large circular floating action button.
  ///
  /// This constructor overrides the default size constraints of the floating
  /// action button.
  ///
  /// The [clipBehavior] and [autofocus] arguments must not be null.
  /// Additionally, [elevation], [focusElevation], [hoverElevation],
  /// [highlightElevation], and [disabledElevation] (if specified) must be
  /// non-negative.
  const FloatingActionButton.large({
    Key? key,
    this.child,
    this.tooltip,
    this.foregroundColor,
    this.backgroundColor,
    this.focusColor,
    this.hoverColor,
    this.splashColor,
    this.heroTag = const _DefaultHeroTag(),
    this.elevation,
    this.focusElevation,
    this.hoverElevation,
    this.highlightElevation,
    this.disabledElevation,
    required this.onPressed,
    this.mouseCursor,
    this.shape,
    this.clipBehavior = Clip.none,
    this.focusNode,
    this.autofocus = false,
    this.materialTapTargetSize,
    this.enableFeedback,
  }) : assert(elevation == null || elevation >= 0.0),
       assert(focusElevation == null || focusElevation >= 0.0),
       assert(hoverElevation == null || hoverElevation >= 0.0),
       assert(highlightElevation == null || highlightElevation >= 0.0),
       assert(disabledElevation == null || disabledElevation >= 0.0),
       assert(clipBehavior != null),
       assert(autofocus != null),
       _floatingActionButtonType = _FloatingActionButtonType.large,
       mini = false,
       isExtended = false,
       _extendedLabel = null,
       extendedIconLabelSpacing = null,
216
       extendedPadding = null,
217
       extendedTextStyle = null,
218
       super(key: key);
219

220 221
  /// Creates a wider [StadiumBorder]-shaped floating action button with
  /// an optional [icon] and a [label].
222
  ///
223
  /// The [label], [autofocus], and [clipBehavior] arguments must not be null.
224 225
  /// Additionally, [elevation], [highlightElevation], and [disabledElevation]
  /// (if specified) must be non-negative.
226
  const FloatingActionButton.extended({
227
    Key? key,
228 229 230
    this.tooltip,
    this.foregroundColor,
    this.backgroundColor,
231 232
    this.focusColor,
    this.hoverColor,
233
    this.heroTag = const _DefaultHeroTag(),
234
    this.elevation,
235 236
    this.focusElevation,
    this.hoverElevation,
237
    this.splashColor,
238 239
    this.highlightElevation,
    this.disabledElevation,
240
    required this.onPressed,
241
    this.mouseCursor = SystemMouseCursors.click,
242
    this.shape,
243
    this.isExtended = true,
244
    this.materialTapTargetSize,
245
    this.clipBehavior = Clip.none,
246
    this.focusNode,
247
    this.autofocus = false,
248
    this.extendedIconLabelSpacing,
249
    this.extendedPadding,
250
    this.extendedTextStyle,
251 252
    Widget? icon,
    required Widget label,
253
    this.enableFeedback,
254
  }) : assert(elevation == null || elevation >= 0.0),
255 256
       assert(focusElevation == null || focusElevation >= 0.0),
       assert(hoverElevation == null || hoverElevation >= 0.0),
257
       assert(highlightElevation == null || highlightElevation >= 0.0),
258 259
       assert(disabledElevation == null || disabledElevation >= 0.0),
       assert(isExtended != null),
260
       assert(clipBehavior != null),
261
       assert(autofocus != null),
262
       mini = false,
263 264 265
       _floatingActionButtonType = _FloatingActionButtonType.extended,
       child = icon,
       _extendedLabel = label,
266
       super(key: key);
267

268
  /// The widget below this widget in the tree.
269 270
  ///
  /// Typically an [Icon].
271
  final Widget? child;
272

273 274 275 276
  /// 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.
277
  final String? tooltip;
278

279
  /// The default foreground color for icons and text within the button.
280
  ///
281 282 283 284 285
  /// If this property is null, then the
  /// [FloatingActionButtonThemeData.foregroundColor] of
  /// [ThemeData.floatingActionButtonTheme] is used. If that property is also
  /// null, then the [ColorScheme.onSecondary] color of [ThemeData.colorScheme]
  /// is used.
286
  final Color? foregroundColor;
287

288
  /// The button's background color.
289
  ///
290 291 292
  /// If this property is null, then the
  /// [FloatingActionButtonThemeData.backgroundColor] of
  /// [ThemeData.floatingActionButtonTheme] is used. If that property is also
293
  /// null, then the [Theme]'s [ColorScheme.secondary] color is used.
294
  final Color? backgroundColor;
295

296 297 298
  /// The color to use for filling the button when the button has input focus.
  ///
  /// Defaults to [ThemeData.focusColor] for the current theme.
299
  final Color? focusColor;
300 301 302 303 304

  /// The color to use for filling the button when the button has a pointer
  /// hovering over it.
  ///
  /// Defaults to [ThemeData.hoverColor] for the current theme.
305
  final Color? hoverColor;
306

307 308 309 310
  /// The splash color for this [FloatingActionButton]'s [InkWell].
  ///
  /// If null, [FloatingActionButtonThemeData.splashColor] is used, if that is
  /// null, [ThemeData.splashColor] is used.
311
  final Color? splashColor;
312

313 314 315
  /// The tag to apply to the button's [Hero] widget.
  ///
  /// Defaults to a tag that matches other floating action buttons.
316 317 318 319 320 321 322 323 324
  ///
  /// Set this to null explicitly if you don't want the floating action button to
  /// have a hero tag.
  ///
  /// If this is not explicitly set, then there can only be one
  /// [FloatingActionButton] per route (that is, per screen), since otherwise
  /// there would be a tag conflict (multiple heroes on one route can't have the
  /// same tag). The material design specification recommends only using one
  /// floating action button per screen.
325
  final Object? heroTag;
326

327
  /// The callback that is called when the button is tapped or otherwise activated.
328 329
  ///
  /// If this is set to null, the button will be disabled.
330
  final VoidCallback? onPressed;
331

332
  /// {@macro flutter.material.RawMaterialButton.mouseCursor}
333 334
  ///
  /// If this property is null, [MaterialStateMouseCursor.clickable] will be used.
335
  final MouseCursor? mouseCursor;
336

337
  /// The z-coordinate at which to place this button relative to its parent.
338
  ///
339 340 341 342
  /// This controls the size of the shadow below the floating action button.
  ///
  /// Defaults to 6, the appropriate elevation for floating action buttons. The
  /// value is always non-negative.
343 344 345 346 347
  ///
  /// See also:
  ///
  ///  * [highlightElevation], the elevation when the button is pressed.
  ///  * [disabledElevation], the elevation when the button is disabled.
348
  final double? elevation;
349

350 351 352 353 354 355 356 357 358 359 360 361 362
  /// The z-coordinate at which to place this button relative to its parent when
  /// the button has the input focus.
  ///
  /// This controls the size of the shadow below the floating action button.
  ///
  /// Defaults to 8, the appropriate elevation for floating action buttons
  /// while they have focus. The value is always non-negative.
  ///
  /// See also:
  ///
  ///  * [elevation], the default elevation.
  ///  * [highlightElevation], the elevation when the button is pressed.
  ///  * [disabledElevation], the elevation when the button is disabled.
363
  final double? focusElevation;
364 365 366 367 368 369 370 371 372 373 374 375 376 377

  /// The z-coordinate at which to place this button relative to its parent when
  /// the button is enabled and has a pointer hovering over it.
  ///
  /// This controls the size of the shadow below the floating action button.
  ///
  /// Defaults to 8, the appropriate elevation for floating action buttons while
  /// they have a pointer hovering over them. The value is always non-negative.
  ///
  /// See also:
  ///
  ///  * [elevation], the default elevation.
  ///  * [highlightElevation], the elevation when the button is pressed.
  ///  * [disabledElevation], the elevation when the button is disabled.
378
  final double? hoverElevation;
379

380 381 382 383
  /// The z-coordinate at which to place this button relative to its parent when
  /// the user is touching the button.
  ///
  /// This controls the size of the shadow below the floating action button.
384 385
  ///
  /// Defaults to 12, the appropriate elevation for floating action buttons
386
  /// while they are being touched. The value is always non-negative.
387 388 389 390
  ///
  /// See also:
  ///
  ///  * [elevation], the default elevation.
391
  final double? highlightElevation;
392

393 394 395 396 397 398
  /// The z-coordinate at which to place this button when the button is disabled
  /// ([onPressed] is null).
  ///
  /// This controls the size of the shadow below the floating action button.
  ///
  /// Defaults to the same value as [elevation]. Setting this to zero makes the
399
  /// floating action button work similar to an [ElevatedButton] but the titular
400 401 402 403 404 405
  /// "floating" effect is lost. The value is always non-negative.
  ///
  /// See also:
  ///
  ///  * [elevation], the default elevation.
  ///  * [highlightElevation], the elevation when the button is pressed.
406
  final double? disabledElevation;
407

408 409 410 411
  /// 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
412
  /// and width of 40.0 logical pixels with a layout width and height of 48.0
413 414 415
  /// logical pixels. (The extra 4 pixels of padding on each side are added as a
  /// result of the floating action button having [MaterialTapTargetSize.padded]
  /// set on the underlying [RawMaterialButton.materialTapTargetSize].)
Devon Carew's avatar
Devon Carew committed
416
  final bool mini;
417

418 419 420 421 422
  /// The shape of the button's [Material].
  ///
  /// The button's highlight and splash are clipped to this shape. If the
  /// button has an elevation, then its drop shadow is defined by this
  /// shape as well.
423
  final ShapeBorder? shape;
424

425
  /// {@macro flutter.material.Material.clipBehavior}
426 427
  ///
  /// Defaults to [Clip.none], and must not be null.
428 429
  final Clip clipBehavior;

430 431 432 433 434 435 436 437 438 439 440
  /// True if this is an "extended" floating action button.
  ///
  /// Typically [extended] buttons have a [StadiumBorder] [shape]
  /// and have been created with the [FloatingActionButton.extended]
  /// constructor.
  ///
  /// The [Scaffold] animates the appearance of ordinary floating
  /// action buttons with scale and rotation transitions. Extended
  /// floating action buttons are scaled and faded in.
  final bool isExtended;

441
  /// {@macro flutter.widgets.Focus.focusNode}
442
  final FocusNode? focusNode;
443

444 445 446
  /// {@macro flutter.widgets.Focus.autofocus}
  final bool autofocus;

447 448 449 450 451 452
  /// Configures the minimum size of the tap target.
  ///
  /// Defaults to [ThemeData.materialTapTargetSize].
  ///
  /// See also:
  ///
453
  ///  * [MaterialTapTargetSize], for a description of how this affects tap targets.
454
  final MaterialTapTargetSize? materialTapTargetSize;
455

456 457 458 459 460 461 462 463 464 465 466 467 468
  /// 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.
  ///
  /// If null, [FloatingActionButtonThemeData.enableFeedback] is used.
  /// If both are null, then default value is true.
  ///
  /// See also:
  ///
  ///  * [Feedback] for providing platform-specific feedback to certain actions.
  final bool? enableFeedback;

469 470 471 472 473 474 475 476

  /// The spacing between the icon and the label for an extended
  /// [FloatingActionButton].
  ///
  /// If null, [FloatingActionButtonThemeData.extendedIconLabelSpacing] is used.
  /// If that is also null, the default is 8.0.
  final double? extendedIconLabelSpacing;

477 478 479 480 481 482 483 484
  /// The padding for an extended [FloatingActionButton]'s content.
  ///
  /// If null, [FloatingActionButtonThemeData.extendedPadding] is used. If that
  /// is also null, the default is
  /// `EdgeInsetsDirectional.only(start: 16.0, end: 20.0)` if an icon is
  /// provided, and `EdgeInsetsDirectional.only(start: 20.0, end: 20.0)` if not.
  final EdgeInsetsGeometry? extendedPadding;

485 486 487 488 489 490 491
  /// The text style for an extended [FloatingActionButton]'s label.
  ///
  /// If null, [FloatingActionButtonThemeData.extendedTextStyle] is used. If
  /// that is also null, then [TextTheme.button] with a letter spacing of 1.2
  /// is used.
  final TextStyle? extendedTextStyle;

492 493 494
  final _FloatingActionButtonType _floatingActionButtonType;

  final Widget? _extendedLabel;
495

496
  @override
497
  Widget build(BuildContext context) {
498
    final ThemeData theme = Theme.of(context);
499
    final FloatingActionButtonThemeData floatingActionButtonTheme = theme.floatingActionButtonTheme;
500 501 502
    final FloatingActionButtonThemeData defaults = theme.useMaterial3
      ? _M3Defaults(context, _floatingActionButtonType, child != null)
      : _M2Defaults(context, _floatingActionButtonType, child != null);
503 504 505

    final Color foregroundColor = this.foregroundColor
      ?? floatingActionButtonTheme.foregroundColor
506
      ?? defaults.foregroundColor!;
507 508
    final Color backgroundColor = this.backgroundColor
      ?? floatingActionButtonTheme.backgroundColor
509
      ?? defaults.backgroundColor!;
510 511
    final Color focusColor = this.focusColor
      ?? floatingActionButtonTheme.focusColor
512
      ?? defaults.focusColor!;
513 514
    final Color hoverColor = this.hoverColor
      ?? floatingActionButtonTheme.hoverColor
515
      ?? defaults.hoverColor!;
516 517
    final Color splashColor = this.splashColor
      ?? floatingActionButtonTheme.splashColor
518
      ?? defaults.splashColor!;
519 520
    final double elevation = this.elevation
      ?? floatingActionButtonTheme.elevation
521
      ?? defaults.elevation!;
522 523
    final double focusElevation = this.focusElevation
      ?? floatingActionButtonTheme.focusElevation
524
      ?? defaults.focusElevation!;
525 526
    final double hoverElevation = this.hoverElevation
      ?? floatingActionButtonTheme.hoverElevation
527
      ?? defaults.hoverElevation!;
528 529
    final double disabledElevation = this.disabledElevation
      ?? floatingActionButtonTheme.disabledElevation
530
      ?? defaults.disabledElevation
531 532 533
      ?? elevation;
    final double highlightElevation = this.highlightElevation
      ?? floatingActionButtonTheme.highlightElevation
534
      ?? defaults.highlightElevation!;
535 536
    final MaterialTapTargetSize materialTapTargetSize = this.materialTapTargetSize
      ?? theme.materialTapTargetSize;
537
    final bool enableFeedback = this.enableFeedback
538 539 540 541
      ?? floatingActionButtonTheme.enableFeedback
      ?? defaults.enableFeedback!;
    final double iconSize = floatingActionButtonTheme.iconSize
      ?? defaults.iconSize!;
542
    final TextStyle extendedTextStyle = (this.extendedTextStyle
543 544
      ?? floatingActionButtonTheme.extendedTextStyle
      ?? defaults.extendedTextStyle!).copyWith(color: foregroundColor);
545 546
    final ShapeBorder shape = this.shape
      ?? floatingActionButtonTheme.shape
547
      ?? defaults.shape!;
548

549
    BoxConstraints sizeConstraints;
550 551 552 553
    Widget? resolvedChild = child != null ? IconTheme.merge(
      data: IconThemeData(size: iconSize),
      child: child!,
    ) : child;
554 555
    switch(_floatingActionButtonType) {
      case _FloatingActionButtonType.regular:
556
        sizeConstraints = floatingActionButtonTheme.sizeConstraints ?? defaults.sizeConstraints!;
557 558
        break;
      case _FloatingActionButtonType.small:
559
        sizeConstraints = floatingActionButtonTheme.smallSizeConstraints ?? defaults.smallSizeConstraints!;
560 561
        break;
      case _FloatingActionButtonType.large:
562
        sizeConstraints = floatingActionButtonTheme.largeSizeConstraints ?? defaults.largeSizeConstraints!;
563 564
        break;
      case _FloatingActionButtonType.extended:
565
        sizeConstraints = floatingActionButtonTheme.extendedSizeConstraints ?? defaults.extendedSizeConstraints!;
566
        final double iconLabelSpacing = extendedIconLabelSpacing ?? floatingActionButtonTheme.extendedIconLabelSpacing ?? 8.0;
567 568
        final EdgeInsetsGeometry padding = extendedPadding
            ?? floatingActionButtonTheme.extendedPadding
569
            ?? defaults.extendedPadding!;
570
        resolvedChild = _ChildOverflowBox(
571 572 573 574 575 576 577 578 579 580 581 582 583
          child: Padding(
            padding: padding,
            child: Row(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  if (child != null)
                    child!,
                  if (child != null && isExtended)
                    SizedBox(width: iconLabelSpacing),
                  if (isExtended)
                    _extendedLabel!,
                ],
            ),
584 585 586 587 588
          ),
        );
        break;
    }

589
    Widget result = RawMaterialButton(
590
      onPressed: onPressed,
591
      mouseCursor: mouseCursor,
592
      elevation: elevation,
593 594
      focusElevation: focusElevation,
      hoverElevation: hoverElevation,
595 596
      highlightElevation: highlightElevation,
      disabledElevation: disabledElevation,
597
      constraints: sizeConstraints,
598 599
      materialTapTargetSize: materialTapTargetSize,
      fillColor: backgroundColor,
600 601
      focusColor: focusColor,
      hoverColor: hoverColor,
602
      splashColor: splashColor,
603
      textStyle: extendedTextStyle,
604
      shape: shape,
605
      clipBehavior: clipBehavior,
606
      focusNode: focusNode,
607
      autofocus: autofocus,
608
      enableFeedback: enableFeedback,
609
      child: resolvedChild,
610 611
    );

612
    if (tooltip != null) {
613
      result = Tooltip(
614
        message: tooltip,
615
        child: result,
616 617 618
      );
    }

619
    if (heroTag != null) {
620
      result = Hero(
621
        tag: heroTag!,
622 623 624 625
        child: result,
      );
    }

626
    return MergeSemantics(child: result);
627
  }
628 629 630 631 632 633

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(ObjectFlagProperty<VoidCallback>('onPressed', onPressed, ifNull: 'disabled'));
    properties.add(StringProperty('tooltip', tooltip, defaultValue: null));
634 635 636 637
    properties.add(ColorProperty('foregroundColor', foregroundColor, defaultValue: null));
    properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
    properties.add(ColorProperty('focusColor', focusColor, defaultValue: null));
    properties.add(ColorProperty('hoverColor', hoverColor, defaultValue: null));
638
    properties.add(ColorProperty('splashColor', splashColor, defaultValue: null));
639
    properties.add(ObjectFlagProperty<Object>('heroTag', heroTag, ifPresent: 'hero'));
640 641 642 643 644
    properties.add(DoubleProperty('elevation', elevation, defaultValue: null));
    properties.add(DoubleProperty('focusElevation', focusElevation, defaultValue: null));
    properties.add(DoubleProperty('hoverElevation', hoverElevation, defaultValue: null));
    properties.add(DoubleProperty('highlightElevation', highlightElevation, defaultValue: null));
    properties.add(DoubleProperty('disabledElevation', disabledElevation, defaultValue: null));
645 646 647 648 649
    properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
    properties.add(DiagnosticsProperty<FocusNode>('focusNode', focusNode, defaultValue: null));
    properties.add(FlagProperty('isExtended', value: isExtended, ifTrue: 'extended'));
    properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize, defaultValue: null));
  }
650
}
651 652 653 654 655 656 657 658

// This widget's size matches its child's size unless its constraints
// force it to be larger or smaller. The child is centered.
//
// Used to encapsulate extended FABs whose size is fixed, using Row
// and MainAxisSize.min, to be as wide as their label and icon.
class _ChildOverflowBox extends SingleChildRenderObjectWidget {
  const _ChildOverflowBox({
659 660
    Key? key,
    Widget? child,
661 662 663 664
  }) : super(key: key, child: child);

  @override
  _RenderChildOverflowBox createRenderObject(BuildContext context) {
665
    return _RenderChildOverflowBox(
666 667 668 669 670 671
      textDirection: Directionality.of(context),
    );
  }

  @override
  void updateRenderObject(BuildContext context, _RenderChildOverflowBox renderObject) {
672
    renderObject.textDirection = Directionality.of(context);
673 674 675 676 677
  }
}

class _RenderChildOverflowBox extends RenderAligningShiftedBox {
  _RenderChildOverflowBox({
678 679
    RenderBox? child,
    TextDirection? textDirection,
680 681 682 683 684 685 686 687
  }) : super(child: child, alignment: Alignment.center, textDirection: textDirection);

  @override
  double computeMinIntrinsicWidth(double height) => 0.0;

  @override
  double computeMinIntrinsicHeight(double width) => 0.0;

688 689 690 691 692 693 694 695 696 697 698 699 700
  @override
  Size computeDryLayout(BoxConstraints constraints) {
    if (child != null) {
      final Size childSize = child!.getDryLayout(const BoxConstraints());
      return Size(
        math.max(constraints.minWidth, math.min(constraints.maxWidth, childSize.width)),
        math.max(constraints.minHeight, math.min(constraints.maxHeight, childSize.height)),
      );
    } else {
      return constraints.biggest;
    }
  }

701 702
  @override
  void performLayout() {
703
    final BoxConstraints constraints = this.constraints;
704
    if (child != null) {
705
      child!.layout(const BoxConstraints(), parentUsesSize: true);
706
      size = Size(
707 708
        math.max(constraints.minWidth, math.min(constraints.maxWidth, child!.size.width)),
        math.max(constraints.minHeight, math.min(constraints.maxHeight, child!.size.height)),
709 710 711 712 713 714 715
      );
      alignChild();
    } else {
      size = constraints.biggest;
    }
  }
}
716 717 718 719 720 721 722 723 724

// Generate a FloatingActionButtonThemeData that represents
// the M2 default values. This was generated by hand from the
// previous hand coded defaults for M2. It uses get method overrides
// instead of properties to avoid computing values that we may not
// need upfront.
class _M2Defaults extends FloatingActionButtonThemeData {
  _M2Defaults(BuildContext context, this.type, this.hasChild)
      : _theme = Theme.of(context),
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
        _colors = Theme.of(context).colorScheme,
        super(
          elevation: 6,
          focusElevation: 6,
          hoverElevation: 8,
          highlightElevation: 12,
          enableFeedback: true,
          sizeConstraints: const BoxConstraints.tightFor(
            width: 56.0,
            height: 56.0,
          ),
          smallSizeConstraints: const BoxConstraints.tightFor(
            width: 40.0,
            height: 40.0,
          ),
          largeSizeConstraints: const BoxConstraints.tightFor(
            width: 96.0,
            height: 96.0,
          ),
          extendedSizeConstraints: const BoxConstraints.tightFor(
            height: 48.0,
          ),
          extendedIconLabelSpacing: 8.0,
        );
749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775

  final _FloatingActionButtonType type;
  final bool hasChild;
  final ThemeData _theme;
  final ColorScheme _colors;

  bool get _isExtended => type == _FloatingActionButtonType.extended;
  bool get _isLarge => type == _FloatingActionButtonType.large;

  @override Color? get foregroundColor => _colors.onSecondary;
  @override Color? get backgroundColor => _colors.secondary;
  @override Color? get focusColor => _theme.focusColor;
  @override Color? get hoverColor => _theme.hoverColor;
  @override Color? get splashColor => _theme.splashColor;
  @override ShapeBorder? get shape => _isExtended ? const StadiumBorder() : const CircleBorder();
  @override double? get iconSize => _isLarge ? 36.0 : 24.0;

  @override EdgeInsetsGeometry? get extendedPadding => EdgeInsetsDirectional.only(start: hasChild && _isExtended ? 16.0 : 20.0, end: 20.0);
  @override TextStyle? get extendedTextStyle => _theme.textTheme.button!.copyWith(letterSpacing: 1.2);
}

// BEGIN GENERATED TOKEN PROPERTIES

// Generated code to the end of this file. Do not edit by hand.
// These defaults are generated from the Material Design Token
// database by the script dev/tools/gen_defaults/bin/gen_defaults.dart.

776
// Generated version v0_76
777 778 779
class _M3Defaults extends FloatingActionButtonThemeData {
  _M3Defaults(this.context, this.type, this.hasChild)
    : _colors = Theme.of(context).colorScheme,
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
      _textTheme = Theme.of(context).textTheme,
      super(
        elevation: 6.0,
        focusElevation: 6.0,
        hoverElevation: 8.0,
        highlightElevation: 6.0,
        enableFeedback: true,
        sizeConstraints: const BoxConstraints.tightFor(
          width: 56.0,
          height: 56.0,
        ),
        smallSizeConstraints: const BoxConstraints.tightFor(
          width: 40.0,
          height: 40.0,
        ),
        largeSizeConstraints: const BoxConstraints.tightFor(
          width: 96.0,
          height: 96.0,
        ),
        extendedSizeConstraints: const BoxConstraints.tightFor(
          height: 56.0,
        ),
        extendedIconLabelSpacing: 8.0,
      );
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822

  final BuildContext context;
  final _FloatingActionButtonType type;
  final bool hasChild;
  final ColorScheme _colors;
  final TextTheme _textTheme;

  bool get _isExtended => type == _FloatingActionButtonType.extended;

  @override Color? get foregroundColor => _colors.onPrimaryContainer;
  @override Color? get backgroundColor => _colors.primaryContainer;
  @override Color? get splashColor => _colors.onPrimaryContainer.withOpacity(0.12);
  @override Color? get focusColor => _colors.onPrimaryContainer.withOpacity(0.12);
  @override Color? get hoverColor => _colors.onPrimaryContainer.withOpacity(0.08);

  @override
  ShapeBorder? get shape {
    switch (type) {
      case _FloatingActionButtonType.regular:
823
       return const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0), bottomLeft: Radius.circular(16.0), bottomRight: Radius.circular(16.0)));
824
      case _FloatingActionButtonType.small:
825
       return const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(12.0), topRight: Radius.circular(12.0), bottomLeft: Radius.circular(12.0), bottomRight: Radius.circular(12.0)));
826
      case _FloatingActionButtonType.large:
827
       return const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(28.0), topRight: Radius.circular(28.0), bottomLeft: Radius.circular(28.0), bottomRight: Radius.circular(28.0)));
828
      case _FloatingActionButtonType.extended:
829
       return const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0), bottomLeft: Radius.circular(16.0), bottomRight: Radius.circular(16.0)));
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847
     }
  }

  @override
  double? get iconSize {
    switch (type) {
      case _FloatingActionButtonType.regular: return 24.0;
      case _FloatingActionButtonType.small: return  24.0;
      case _FloatingActionButtonType.large: return 36.0;
      case _FloatingActionButtonType.extended: return 24.0;
    }
  }

  @override EdgeInsetsGeometry? get extendedPadding => EdgeInsetsDirectional.only(start: hasChild && _isExtended ? 16.0 : 20.0, end: 20.0);
  @override TextStyle? get extendedTextStyle => _textTheme.labelLarge;
}

// END GENERATED TOKEN PROPERTIES