123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101 |
- // 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/material.dart';
- import 'package:flutter/rendering.dart';
- import 'package:flyinsonolite/controls/custom/custombuttonstylebutton.dart';
- import 'package:flyinsonolite/controls/custom/customtooltip.dart';
- import 'package:flyinsonolite/infrastructure/scale.dart';
- // Examples can assume:
- // late BuildContext context;
- // Minimum logical pixel size of the IconButton.
- // See: <https://material.io/design/usability/accessibility.html#layout-typography>.
- double get _kMinButtonSize => kMinInteractiveDimension.s;
- /// 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. In Material Design 3,
- /// this requirement no longer exists because this widget builds a subclass of
- /// [CustomButtonStyleButton].
- ///
- /// 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/design/layout/spacing-methods.html#touch-targets)
- /// requirements in the Material Design specification. The [alignment] controls
- /// how the icon itself is positioned within the hit region.
- ///
- /// {@tool dartpad}
- /// This sample shows an [CustomIconButton] that uses the Material icon "volume_up" to
- /// increase the volume.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/material/icon_button.png)
- ///
- /// ** See code in examples/api/lib/material/icon_button/icon_button.0.dart **
- /// {@end-tool}
- ///
- /// ### Icon sizes
- ///
- /// When creating an icon button with an [Icon], do not override the
- /// icon's size with its [Icon.size] parameter, use the icon button's
- /// [iconSize] parameter instead. For example do this:
- ///
- /// ```dart
- /// IconButton(
- /// iconSize: 72,
- /// icon: const Icon(Icons.favorite),
- /// onPressed: () {
- /// // ...
- /// },
- /// ),
- /// ```
- ///
- /// Avoid doing this:
- ///
- /// ```dart
- /// IconButton(
- /// icon: const Icon(Icons.favorite, size: 72),
- /// onPressed: () {
- /// // ...
- /// },
- /// ),
- /// ```
- ///
- /// If you do, the button's size will be based on the default icon
- /// size, not 72, which may produce unexpected layouts and clipping
- /// issues.
- ///
- /// ### 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 dartpad}
- /// In this sample the icon button's background color is defined with an [Ink]
- /// widget whose child is an [CustomIconButton]. 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.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/material/icon_button_background.png)
- ///
- /// ** See code in examples/api/lib/material/icon_button/icon_button.1.dart **
- /// {@end-tool}
- ///
- /// Material Design 3 introduced new types (standard and contained) of [CustomIconButton]s.
- /// The default [CustomIconButton] is the standard type, and contained icon buttons can be produced
- /// by configuring the [CustomIconButton] widget's properties.
- ///
- /// Material Design 3 also treats [CustomIconButton]s as toggle buttons. In order
- /// to not break existing apps, the toggle feature can be optionally controlled
- /// by the [isSelected] property.
- ///
- /// If [isSelected] is null it will behave as a normal button. If [isSelected] is not
- /// null then it will behave as a toggle button. If [isSelected] is true then it will
- /// show [selectedIcon], if it false it will show the normal [icon].
- ///
- /// In Material Design 3, both [IconTheme] and [IconButtonTheme] are used to override the default style
- /// of [CustomIconButton]. If both themes exist, the [IconButtonTheme] will override [IconTheme] no matter
- /// which is closer to the [CustomIconButton]. Each [CustomIconButton]'s property is resolved by the order of
- /// precedence: widget property, [IconButtonTheme] property, [IconTheme] property and
- /// internal default property value.
- ///
- /// In Material Design 3, the [CustomIconButton.visualDensity] defaults to [VisualDensity.standard]
- /// for all platforms; otherwise the button will have a rounded rectangle shape if
- /// the [CustomIconButton.visualDensity] is set to [VisualDensity.compact]. Users can
- /// customize it by using [IconButtonTheme], [CustomIconButton.style] or [CustomIconButton.visualDensity].
- ///
- /// {@tool dartpad}
- /// This sample shows creation of [CustomIconButton] widgets for standard, filled,
- /// filled tonal and outlined types, as described in: https://m3.material.io/components/icon-buttons/overview
- ///
- /// ** See code in examples/api/lib/material/icon_button/icon_button.2.dart **
- /// {@end-tool}
- ///
- /// {@tool dartpad}
- /// This sample shows creation of [CustomIconButton] widgets with toggle feature for
- /// standard, filled, filled tonal and outlined types, as described
- /// in: https://m3.material.io/components/icon-buttons/overview
- ///
- /// ** See code in examples/api/lib/material/icon_button/icon_button.3.dart **
- /// {@end-tool}
- ///
- /// See also:
- ///
- /// * [Icons], the library of Material 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.
- /// * [CustomTextButton], [CustomElevatedButton], [OutlinedButton], for buttons with text labels and an optional icon.
- /// * [InkResponse] and [InkWell], for the ink splash effect itself.
- class CustomIconButton 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. This requirement
- /// no longer exists if [ThemeData.useMaterial3] is set to true.
- ///
- /// [autofocus] argument must not be null (though it has default value).
- ///
- /// The [icon] argument must be specified, and is typically either an [Icon]
- /// or an [ImageIcon].
- const CustomIconButton({
- super.key,
- this.iconSize,
- this.visualDensity,
- this.padding,
- this.alignment,
- this.splashRadius,
- this.color,
- this.focusColor,
- this.hoverColor,
- this.highlightColor,
- this.splashColor,
- this.disabledColor,
- required this.onPressed,
- this.mouseCursor,
- this.focusNode,
- this.autofocus = false,
- this.tooltip,
- this.enableFeedback,
- this.constraints,
- this.style,
- this.isSelected,
- this.selectedIcon,
- required this.icon,
- }) : assert(splashRadius == null || splashRadius > 0),
- assert(autofocus != null),
- assert(icon != null);
- /// The size of the icon inside the button.
- ///
- /// If null, uses [IconThemeData.size]. If it is also null, the default size
- /// is 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 [CustomIconButton] 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 [CustomIconButton] would default to 24.0 and then
- /// the [Icon] itself would likely get clipped.
- ///
- /// If [ThemeData.useMaterial3] is set to true and this is null, the size of the
- /// [CustomIconButton] would default to 24.0. The size given here is passed down to the
- /// [ButtonStyle.iconSize] property.
- final double? iconSize;
- /// Defines how compact the icon button's layout will be.
- ///
- /// {@macro flutter.material.themedata.visualDensity}
- ///
- /// This property can be null. If null, it defaults to [VisualDensity.standard]
- /// in Material Design 3 to make sure the button will be circular on all platforms.
- ///
- /// See also:
- ///
- /// * [ThemeData.visualDensity], which specifies the [visualDensity] 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 can be null. If null, it defaults to 8.0 padding on all sides.
- final EdgeInsetsGeometry? padding;
- /// Defines how the icon is positioned within the IconButton.
- ///
- /// This property can be null. If 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 splash radius.
- ///
- /// If [ThemeData.useMaterial3] is set to true, this will not be used.
- ///
- /// If null, default splash radius of [Material.defaultSplashRadius] is used.
- final double? splashRadius;
- /// 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 when it has the input focus.
- ///
- /// If [ThemeData.useMaterial3] is set to true, this [focusColor] will be mapped
- /// to be the [ButtonStyle.overlayColor] in focused state, which paints on top of
- /// the button, as an overlay. Therefore, using a color with some transparency
- /// is recommended. For example, one could customize the [focusColor] below:
- ///
- /// ```dart
- /// IconButton(
- /// focusColor: Colors.orange.withOpacity(0.3),
- /// icon: const Icon(Icons.sunny),
- /// onPressed: () {
- /// // ...
- /// },
- /// )
- /// ```
- ///
- /// Defaults to [ThemeData.focusColor] of the ambient theme.
- final Color? focusColor;
- /// The color for the button when a pointer is hovering over it.
- ///
- /// If [ThemeData.useMaterial3] is set to true, this [hoverColor] will be mapped
- /// to be the [ButtonStyle.overlayColor] in hovered state, which paints on top of
- /// the button, as an overlay. Therefore, using a color with some transparency
- /// is recommended. For example, one could customize the [hoverColor] below:
- ///
- /// ```dart
- /// IconButton(
- /// hoverColor: Colors.orange.withOpacity(0.3),
- /// icon: const Icon(Icons.ac_unit),
- /// onPressed: () {
- /// // ...
- /// },
- /// )
- /// ```
- ///
- /// 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,
- /// icon: const Icon(Icons.sunny_snowing),
- /// onPressed: () {
- /// // ...
- /// },
- /// )
- /// ```
- 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.
- ///
- /// If [ThemeData.useMaterial3] is set to true, this will not be used. Use
- /// [highlightColor] instead to show the overlay color of the button when the button
- /// is in the pressed state.
- ///
- /// 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.
- ///
- /// If [ThemeData.useMaterial3] is set to true, this [highlightColor] will be mapped
- /// to be the [ButtonStyle.overlayColor] in pressed state, which paints on top
- /// of the button, as an overlay. Therefore, using a color with some transparency
- /// is recommended. For example, one could customize the [highlightColor] below:
- ///
- /// ```dart
- /// IconButton(
- /// highlightColor: Colors.orange.withOpacity(0.3),
- /// icon: const Icon(Icons.question_mark),
- /// onPressed: () {
- /// // ...
- /// },
- /// )
- /// ```
- ///
- /// 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.material.RawMaterialButton.mouseCursor}
- ///
- /// If set to null, will default to
- /// - [SystemMouseCursors.basic], if [onPressed] is null
- /// - [SystemMouseCursors.click], otherwise
- final MouseCursor? mouseCursor;
- /// {@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;
- /// Customizes this button's appearance.
- ///
- /// Non-null properties of this style override the corresponding
- /// properties in [_IconButtonM3.themeStyleOf] and [_IconButtonM3.defaultStyleOf].
- /// [MaterialStateProperty]s that resolve to non-null values will similarly
- /// override the corresponding [MaterialStateProperty]s in [_IconButtonM3.themeStyleOf]
- /// and [_IconButtonM3.defaultStyleOf].
- ///
- /// The [style] is only used for Material 3 [CustomIconButton]. If [ThemeData.useMaterial3]
- /// is set to true, [style] is preferred for icon button customization, and any
- /// parameters defined in [style] will override the same parameters in [CustomIconButton].
- ///
- /// For example, if [CustomIconButton]'s [visualDensity] is set to [VisualDensity.standard]
- /// and [style]'s [visualDensity] is set to [VisualDensity.compact],
- /// the icon button will have [VisualDensity.compact] to define the button's layout.
- ///
- /// Null by default.
- final ButtonStyle? style;
- /// The optional selection state of the icon button.
- ///
- /// If this property is null, the button will behave as a normal push button,
- /// otherwise, the button will toggle between showing [icon] and [selectedIcon]
- /// based on the value of [isSelected]. If true, it will show [selectedIcon],
- /// if false it will show [icon].
- ///
- /// This property is only used if [ThemeData.useMaterial3] is true.
- final bool? isSelected;
- /// The icon to display inside the button when [isSelected] is true. This property
- /// can be null. The original [icon] will be used for both selected and unselected
- /// status if it is null.
- ///
- /// The [Icon.size] and [Icon.color] of the icon is configured automatically
- /// based on the [iconSize] and [color] properties using an [IconTheme] and
- /// therefore should not be explicitly configured in the icon widget.
- ///
- /// This property is only used if [ThemeData.useMaterial3] is true.
- ///
- /// See also:
- ///
- /// * [Icon], for icons based on glyphs from fonts instead of images.
- /// * [ImageIcon], for showing icons from [AssetImage]s or other [ImageProvider]s.
- final Widget? selectedIcon;
- /// A static convenience method that constructs an icon button
- /// [ButtonStyle] given simple values. This method is only used for Material 3.
- ///
- /// The [foregroundColor] color is used to create a [MaterialStateProperty]
- /// [ButtonStyle.foregroundColor] value. Specify a value for [foregroundColor]
- /// to specify the color of the button's icons. The [hoverColor], [focusColor]
- /// and [highlightColor] colors are used to indicate the hover, focus,
- /// and pressed states. Use [backgroundColor] for the button's background
- /// fill color. Use [disabledForegroundColor] and [disabledBackgroundColor]
- /// to specify the button's disabled icon and fill color.
- ///
- /// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
- /// parameters are used to construct [ButtonStyle].mouseCursor.
- ///
- /// All of the other parameters are either used directly or used to
- /// create a [MaterialStateProperty] with a single value for all
- /// states.
- ///
- /// All parameters default to null, by default this method returns
- /// a [ButtonStyle] that doesn't override anything.
- ///
- /// For example, to override the default icon color for a
- /// [CustomIconButton], as well as its overlay color, with all of the
- /// standard opacity adjustments for the pressed, focused, and
- /// hovered states, one could write:
- ///
- /// ```dart
- /// IconButton(
- /// icon: const Icon(Icons.pets),
- /// style: IconButton.styleFrom(foregroundColor: Colors.green),
- /// onPressed: () {
- /// // ...
- /// },
- /// ),
- /// ```
- static ButtonStyle styleFrom({
- Color? foregroundColor,
- Color? backgroundColor,
- Color? disabledForegroundColor,
- Color? disabledBackgroundColor,
- Color? focusColor,
- Color? hoverColor,
- Color? highlightColor,
- Color? shadowColor,
- Color? surfaceTintColor,
- double? elevation,
- Size? minimumSize,
- Size? fixedSize,
- Size? maximumSize,
- double? iconSize,
- BorderSide? side,
- OutlinedBorder? shape,
- EdgeInsetsGeometry? padding,
- MouseCursor? enabledMouseCursor,
- MouseCursor? disabledMouseCursor,
- VisualDensity? visualDensity,
- MaterialTapTargetSize? tapTargetSize,
- Duration? animationDuration,
- bool? enableFeedback,
- AlignmentGeometry? alignment,
- InteractiveInkFeatureFactory? splashFactory,
- }) {
- final MaterialStateProperty<Color?>? buttonBackgroundColor =
- (backgroundColor == null && disabledBackgroundColor == null)
- ? null
- : _IconButtonDefaultBackground(
- backgroundColor, disabledBackgroundColor);
- final MaterialStateProperty<Color?>? buttonForegroundColor =
- (foregroundColor == null && disabledForegroundColor == null)
- ? null
- : _IconButtonDefaultForeground(
- foregroundColor, disabledForegroundColor);
- final MaterialStateProperty<Color?>? overlayColor =
- (foregroundColor == null &&
- hoverColor == null &&
- focusColor == null &&
- highlightColor == null)
- ? null
- : _IconButtonDefaultOverlay(
- foregroundColor, focusColor, hoverColor, highlightColor);
- final MaterialStateProperty<MouseCursor>? mouseCursor =
- (enabledMouseCursor == null && disabledMouseCursor == null)
- ? null
- : _IconButtonDefaultMouseCursor(
- enabledMouseCursor!, disabledMouseCursor!);
- return ButtonStyle(
- backgroundColor: buttonBackgroundColor,
- foregroundColor: buttonForegroundColor,
- overlayColor: overlayColor,
- shadowColor: CustomButtonStyleButton.allOrNull<Color>(shadowColor),
- surfaceTintColor:
- CustomButtonStyleButton.allOrNull<Color>(surfaceTintColor),
- elevation: CustomButtonStyleButton.allOrNull<double>(elevation),
- padding: CustomButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
- minimumSize: CustomButtonStyleButton.allOrNull<Size>(minimumSize),
- fixedSize: CustomButtonStyleButton.allOrNull<Size>(fixedSize),
- maximumSize: CustomButtonStyleButton.allOrNull<Size>(maximumSize),
- iconSize: CustomButtonStyleButton.allOrNull<double>(iconSize),
- side: CustomButtonStyleButton.allOrNull<BorderSide>(side),
- shape: CustomButtonStyleButton.allOrNull<OutlinedBorder>(shape),
- mouseCursor: mouseCursor,
- visualDensity: visualDensity,
- tapTargetSize: tapTargetSize,
- animationDuration: animationDuration,
- enableFeedback: enableFeedback,
- alignment: alignment,
- splashFactory: splashFactory,
- );
- }
- @override
- Widget build(BuildContext context) {
- final ThemeData theme = Theme.of(context);
- if (theme.useMaterial3) {
- final Size? minSize = constraints == null
- ? null
- : Size(constraints!.minWidth, constraints!.minHeight);
- final Size? maxSize = constraints == null
- ? null
- : Size(constraints!.maxWidth, constraints!.maxHeight);
- ButtonStyle adjustedStyle = styleFrom(
- visualDensity: visualDensity,
- foregroundColor: color,
- disabledForegroundColor: disabledColor,
- focusColor: focusColor,
- hoverColor: hoverColor,
- highlightColor: highlightColor,
- padding: padding,
- minimumSize: minSize,
- maximumSize: maxSize,
- iconSize: iconSize,
- alignment: alignment,
- enabledMouseCursor: mouseCursor,
- disabledMouseCursor: mouseCursor,
- enableFeedback: enableFeedback,
- );
- if (style != null) {
- adjustedStyle = style!.merge(adjustedStyle);
- }
- Widget effectiveIcon = icon;
- if ((isSelected ?? false) && selectedIcon != null) {
- effectiveIcon = selectedIcon!;
- }
- Widget iconButton = effectiveIcon;
- if (tooltip != null) {
- iconButton = CustomTooltip(
- message: tooltip,
- child: effectiveIcon,
- );
- }
- return _SelectableIconButton(
- style: adjustedStyle,
- onPressed: onPressed,
- autofocus: autofocus,
- focusNode: focusNode,
- isSelected: isSelected,
- child: iconButton,
- );
- }
- assert(debugCheckHasMaterial(context));
- Color? currentColor;
- if (onPressed != null) {
- currentColor = color;
- } else {
- currentColor = disabledColor ?? theme.disabledColor;
- }
- final VisualDensity effectiveVisualDensity =
- visualDensity ?? theme.visualDensity;
- final BoxConstraints unadjustedConstraints = constraints ??
- BoxConstraints(
- minWidth: _kMinButtonSize,
- minHeight: _kMinButtonSize,
- );
- final BoxConstraints adjustedConstraints =
- effectiveVisualDensity.effectiveConstraints(unadjustedConstraints);
- final double effectiveIconSize =
- iconSize ?? IconTheme.of(context).size ?? 24.s;
- final EdgeInsetsGeometry effectivePadding = padding ?? EdgeInsets.all(8.s);
- final AlignmentGeometry effectiveAlignment = alignment ?? Alignment.center;
- final bool effectiveEnableFeedback = enableFeedback ?? true;
- Widget result = ConstrainedBox(
- constraints: adjustedConstraints,
- child: Padding(
- padding: effectivePadding,
- child: SizedBox(
- height: effectiveIconSize,
- width: effectiveIconSize,
- child: Align(
- alignment: effectiveAlignment,
- child: IconTheme.merge(
- data: IconThemeData(
- size: effectiveIconSize,
- color: currentColor,
- ),
- child: icon,
- ),
- ),
- ),
- ),
- );
- if (tooltip != null) {
- result = CustomTooltip(
- message: tooltip,
- child: result,
- );
- }
- return Semantics(
- button: true,
- enabled: onPressed != null,
- child: InkResponse(
- focusNode: focusNode,
- autofocus: autofocus,
- canRequestFocus: onPressed != null,
- onTap: onPressed,
- mouseCursor: mouseCursor ??
- (onPressed == null
- ? SystemMouseCursors.basic
- : SystemMouseCursors.click),
- enableFeedback: effectiveEnableFeedback,
- focusColor: focusColor ?? theme.focusColor,
- hoverColor: hoverColor ?? theme.hoverColor,
- highlightColor: highlightColor ?? theme.highlightColor,
- splashColor: splashColor ?? theme.splashColor,
- radius: splashRadius ??
- math.max(
- Material.defaultSplashRadius.s,
- (effectiveIconSize +
- math.min(effectivePadding.horizontal,
- effectivePadding.vertical)) *
- 0.7,
- // x 0.5 for diameter -> radius and + 40% overflow derived from other Material apps.
- ),
- child: result,
- ),
- );
- }
- @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));
- }
- }
- class _SelectableIconButton extends StatefulWidget {
- const _SelectableIconButton({
- this.isSelected,
- this.style,
- this.focusNode,
- required this.autofocus,
- required this.onPressed,
- required this.child,
- });
- final bool? isSelected;
- final ButtonStyle? style;
- final FocusNode? focusNode;
- final bool autofocus;
- final VoidCallback? onPressed;
- final Widget child;
- @override
- State<_SelectableIconButton> createState() => _SelectableIconButtonState();
- }
- class _SelectableIconButtonState extends State<_SelectableIconButton> {
- late final MaterialStatesController statesController;
- @override
- void initState() {
- super.initState();
- if (widget.isSelected == null) {
- statesController = MaterialStatesController();
- } else {
- statesController = MaterialStatesController(
- <MaterialState>{if (widget.isSelected!) MaterialState.selected});
- }
- }
- @override
- void didUpdateWidget(_SelectableIconButton oldWidget) {
- super.didUpdateWidget(oldWidget);
- if (widget.isSelected == null) {
- if (statesController.value.contains(MaterialState.selected)) {
- statesController.update(MaterialState.selected, false);
- }
- return;
- }
- if (widget.isSelected != oldWidget.isSelected) {
- statesController.update(MaterialState.selected, widget.isSelected!);
- }
- }
- @override
- Widget build(BuildContext context) {
- return _IconButtonM3(
- statesController: statesController,
- style: widget.style,
- autofocus: widget.autofocus,
- focusNode: widget.focusNode,
- onPressed: widget.onPressed,
- child: widget.child,
- );
- }
- }
- class _IconButtonM3 extends CustomButtonStyleButton {
- const _IconButtonM3({
- required super.onPressed,
- super.style,
- super.focusNode,
- super.autofocus = false,
- super.statesController,
- required Widget super.child,
- }) : super(
- onLongPress: null,
- onHover: null,
- onFocusChange: null,
- clipBehavior: Clip.none);
- /// ## Material 3 defaults
- ///
- /// If [ThemeData.useMaterial3] is set to true the following defaults will
- /// be used:
- ///
- /// * `textStyle` - null
- /// * `backgroundColor` - transparent
- /// * `foregroundColor`
- /// * disabled - Theme.colorScheme.onSurface(0.38)
- /// * selected - Theme.colorScheme.primary
- /// * others - Theme.colorScheme.onSurfaceVariant
- /// * `overlayColor`
- /// * selected
- /// * hovered - Theme.colorScheme.primary(0.08)
- /// * focused or pressed - Theme.colorScheme.primary(0.12)
- /// * hovered or focused - Theme.colorScheme.onSurfaceVariant(0.08)
- /// * pressed - Theme.colorScheme.onSurfaceVariant(0.12)
- /// * others - null
- /// * `shadowColor` - null
- /// * `surfaceTintColor` - null
- /// * `elevation` - 0
- /// * `padding` - all(8)
- /// * `minimumSize` - Size(40, 40)
- /// * `fixedSize` - null
- /// * `maximumSize` - Size.infinite
- /// * `iconSize` - 24
- /// * `side` - null
- /// * `shape` - StadiumBorder()
- /// * `mouseCursor`
- /// * disabled - SystemMouseCursors.basic
- /// * others - SystemMouseCursors.click
- /// * `visualDensity` - VisualDensity.standard
- /// * `tapTargetSize` - theme.materialTapTargetSize
- /// * `animationDuration` - kThemeChangeDuration
- /// * `enableFeedback` - true
- /// * `alignment` - Alignment.center
- /// * `splashFactory` - Theme.splashFactory
- @override
- ButtonStyle defaultStyleOf(BuildContext context) {
- return _IconButtonDefaultsM3(context);
- }
- /// Returns the [IconButtonThemeData.style] of the closest [IconButtonTheme] ancestor.
- /// The color and icon size can also be configured by the [IconTheme] if the same property
- /// has a null value in [IconButtonTheme]. However, if any of the properties exist
- /// in both [IconButtonTheme] and [IconTheme], [IconTheme] will be overridden.
- @override
- ButtonStyle? themeStyleOf(BuildContext context) {
- final IconThemeData iconTheme = IconTheme.of(context);
- final bool isDark = Theme.of(context).brightness == Brightness.dark;
- bool isIconThemeDefault(Color? color) {
- if (isDark) {
- return color == kDefaultIconLightColor;
- }
- return color == kDefaultIconDarkColor;
- }
- final bool isDefaultColor = isIconThemeDefault(iconTheme.color);
- final bool isDefaultSize =
- iconTheme.size == const IconThemeData.fallback().size;
- final ButtonStyle iconThemeStyle = CustomIconButton.styleFrom(
- foregroundColor: isDefaultColor ? null : iconTheme.color,
- iconSize: isDefaultSize ? null : iconTheme.size);
- return IconButtonTheme.of(context).style?.merge(iconThemeStyle) ??
- iconThemeStyle;
- }
- }
- @immutable
- class _IconButtonDefaultBackground extends MaterialStateProperty<Color?> {
- _IconButtonDefaultBackground(this.background, this.disabledBackground);
- final Color? background;
- final Color? disabledBackground;
- @override
- Color? resolve(Set<MaterialState> states) {
- if (states.contains(MaterialState.disabled)) {
- return disabledBackground;
- }
- return background;
- }
- @override
- String toString() {
- return '{disabled: $disabledBackground, otherwise: $background}';
- }
- }
- @immutable
- class _IconButtonDefaultForeground extends MaterialStateProperty<Color?> {
- _IconButtonDefaultForeground(
- this.foregroundColor, this.disabledForegroundColor);
- final Color? foregroundColor;
- final Color? disabledForegroundColor;
- @override
- Color? resolve(Set<MaterialState> states) {
- if (states.contains(MaterialState.disabled)) {
- return disabledForegroundColor;
- }
- return foregroundColor;
- }
- @override
- String toString() {
- return '{disabled: $disabledForegroundColor, otherwise: $foregroundColor}';
- }
- }
- @immutable
- class _IconButtonDefaultOverlay extends MaterialStateProperty<Color?> {
- _IconButtonDefaultOverlay(this.foregroundColor, this.focusColor,
- this.hoverColor, this.highlightColor);
- final Color? foregroundColor;
- final Color? focusColor;
- final Color? hoverColor;
- final Color? highlightColor;
- @override
- Color? resolve(Set<MaterialState> states) {
- if (states.contains(MaterialState.selected)) {
- if (states.contains(MaterialState.pressed)) {
- return highlightColor ?? foregroundColor?.withOpacity(0.12);
- }
- if (states.contains(MaterialState.hovered)) {
- return hoverColor ?? foregroundColor?.withOpacity(0.08);
- }
- if (states.contains(MaterialState.focused)) {
- return focusColor ?? foregroundColor?.withOpacity(0.12);
- }
- }
- if (states.contains(MaterialState.pressed)) {
- return highlightColor ?? foregroundColor?.withOpacity(0.12);
- }
- if (states.contains(MaterialState.hovered)) {
- return hoverColor ?? foregroundColor?.withOpacity(0.08);
- }
- if (states.contains(MaterialState.focused)) {
- return focusColor ?? foregroundColor?.withOpacity(0.08);
- }
- return null;
- }
- @override
- String toString() {
- return '{hovered: $hoverColor, focused: $focusColor, pressed: $highlightColor, otherwise: null}';
- }
- }
- @immutable
- class _IconButtonDefaultMouseCursor extends MaterialStateProperty<MouseCursor>
- with Diagnosticable {
- _IconButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor);
- final MouseCursor enabledCursor;
- final MouseCursor disabledCursor;
- @override
- MouseCursor resolve(Set<MaterialState> states) {
- if (states.contains(MaterialState.disabled)) {
- return disabledCursor;
- }
- return enabledCursor;
- }
- }
- // BEGIN GENERATED TOKEN PROPERTIES - IconButton
- // Do not edit by hand. The code between the "BEGIN GENERATED" and
- // "END GENERATED" comments are generated from data in the Material
- // Design token database by the script:
- // dev/tools/gen_defaults/bin/gen_defaults.dart.
- // Token database version: v0_132
- class _IconButtonDefaultsM3 extends ButtonStyle {
- _IconButtonDefaultsM3(this.context)
- : super(
- animationDuration: kThemeChangeDuration,
- enableFeedback: true,
- alignment: Alignment.center,
- );
- final BuildContext context;
- late final ColorScheme _colors = Theme.of(context).colorScheme;
- // No default text style
- @override
- MaterialStateProperty<Color?>? get backgroundColor =>
- const MaterialStatePropertyAll<Color?>(Colors.transparent);
- @override
- MaterialStateProperty<Color?>? get foregroundColor =>
- MaterialStateProperty.resolveWith((Set<MaterialState> states) {
- if (states.contains(MaterialState.disabled)) {
- return _colors.onSurface.withOpacity(0.38);
- }
- if (states.contains(MaterialState.selected)) {
- return _colors.primary;
- }
- return _colors.onSurfaceVariant;
- });
- @override
- MaterialStateProperty<Color?>? get overlayColor =>
- MaterialStateProperty.resolveWith((Set<MaterialState> states) {
- if (states.contains(MaterialState.selected)) {
- if (states.contains(MaterialState.hovered)) {
- return _colors.primary.withOpacity(0.08);
- }
- if (states.contains(MaterialState.focused)) {
- return _colors.primary.withOpacity(0.12);
- }
- if (states.contains(MaterialState.pressed)) {
- return _colors.primary.withOpacity(0.12);
- }
- }
- if (states.contains(MaterialState.hovered)) {
- return _colors.onSurfaceVariant.withOpacity(0.08);
- }
- if (states.contains(MaterialState.focused)) {
- return _colors.onSurfaceVariant.withOpacity(0.08);
- }
- if (states.contains(MaterialState.pressed)) {
- return _colors.onSurfaceVariant.withOpacity(0.12);
- }
- return null;
- });
- @override
- MaterialStateProperty<double>? get elevation =>
- const MaterialStatePropertyAll<double>(0.0);
- @override
- MaterialStateProperty<Color>? get shadowColor =>
- const MaterialStatePropertyAll<Color>(Colors.transparent);
- @override
- MaterialStateProperty<Color>? get surfaceTintColor =>
- const MaterialStatePropertyAll<Color>(Colors.transparent);
- @override
- MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
- MaterialStatePropertyAll<EdgeInsetsGeometry>(EdgeInsets.all(8.s));
- @override
- MaterialStateProperty<Size>? get minimumSize =>
- MaterialStatePropertyAll<Size>(Size(40.s, 40.s));
- // No default fixedSize
- @override
- MaterialStateProperty<Size>? get maximumSize =>
- const MaterialStatePropertyAll<Size>(Size.infinite);
- @override
- MaterialStateProperty<double>? get iconSize =>
- MaterialStatePropertyAll<double>(24.s);
- // No default side
- @override
- MaterialStateProperty<OutlinedBorder>? get shape =>
- const MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
- @override
- MaterialStateProperty<MouseCursor?>? get mouseCursor =>
- MaterialStateProperty.resolveWith((Set<MaterialState> states) {
- if (states.contains(MaterialState.disabled)) {
- return SystemMouseCursors.basic;
- }
- return SystemMouseCursors.click;
- });
- @override
- VisualDensity? get visualDensity => VisualDensity.standard;
- @override
- MaterialTapTargetSize? get tapTargetSize =>
- Theme.of(context).materialTapTargetSize;
- @override
- InteractiveInkFeatureFactory? get splashFactory =>
- Theme.of(context).splashFactory;
- }
- // END GENERATED TOKEN PROPERTIES - IconButton
|