segmentedButtonTheme static method

SegmentedButtonThemeData segmentedButtonTheme({
  1. required ColorScheme colorScheme,
  2. SchemeColor? selectedSchemeColor,
  3. SchemeColor? unselectedSchemeColor,
  4. SchemeColor? unselectedForegroundSchemeColor,
  5. SchemeColor? borderSchemeColor,
  6. double? radius,
  7. double? borderWidth,
  8. EdgeInsetsGeometry? padding,
  9. Size? minButtonSize,
  10. bool? useTintedInteraction,
  11. bool? useTintedDisable,
  12. InteractiveInkFeatureFactory? splashFactory,
  13. MaterialStateProperty<TextStyle?>? textStyle,
  14. bool? useMaterial3,
})

An opinionated SearchBarThemeData theme for the SearchBar. An opinionated SearchViewThemeData theme for the SearchBar. An opinionated SegmentedButtonThemeData theme for the SegmentedButton.

Implementation

// static SearchBarThemeData searchBarThemeData() {
//   return const SearchBarThemeData();
// }

// TODO(rydmike): Add SearchViewThemeData support.
/// An opinionated [SearchViewThemeData] theme for the [SearchBar].
// static SearchViewThemeData searchViewThemeData() {
//   return const SearchViewThemeData();
// }

/// An opinionated [SegmentedButtonThemeData] theme for the [SegmentedButton].
static SegmentedButtonThemeData segmentedButtonTheme({
  /// Typically the same [ColorScheme] that is also use for your [ThemeData].
  required final ColorScheme colorScheme,

  /// Selects which color from the passed in colorScheme to use as background
  /// color for the selected button.
  ///
  /// Defines the background color for selected button, and
  /// it's onColor pair defines the foreground for selected button.
  ///
  /// If not defined, [SchemeColor.secondaryContainer] will be used.
  final SchemeColor? selectedSchemeColor,

  /// Selects which color from the passed in colorScheme to use as the
  /// background color for unselected segmented button.
  ///
  /// If not defined, [SchemeColor.surface] will be used.
  final SchemeColor? unselectedSchemeColor,

  /// Selects which color from the passed in colorScheme to use as the
  /// foreground color for unselected segmented button.
  ///
  /// If not defined, [SchemeColor.onSurface] will be used.
  final SchemeColor? unselectedForegroundSchemeColor,

  /// Selects which color from the passed in colorScheme to use as the border
  /// color for the toggle buttons.
  ///
  /// If not defined, [ColorScheme.outline] will be the effective result.
  final SchemeColor? borderSchemeColor,

  /// The button corner radius.
  ///
  /// If not defined, defaults to defaults to Stadium border.
  final double? radius,

  /// The width of the borders around the segmented button.
  ///
  /// If null, defaults to [kThinBorderWidth] = 1.0.
  final double? borderWidth,

  /// Padding for the individual segment buttons.
  ///
  /// Defaults to null and uses M3's default scaled padding function.
  final EdgeInsetsGeometry? padding,

  /// Minimum button size.
  ///
  /// If null, defaults to [kButtonMinSize] (`const Size(40.0, 40.0)`) when
  /// [useMaterial3] is false and to `const Size(64.0, 40.0)` when
  /// [useMaterial3] is true, via M3 built in defaults.
  final Size? minButtonSize,

  /// Defines if the theme uses tinted interaction effects.
  ///
  /// If undefined, defaults to false.
  final bool? useTintedInteraction,

  /// Defines if the theme uses tinted disabled color.
  ///
  /// If undefined, defaults to false.
  final bool? useTintedDisable,

  /// Creates the [InkWell] splash factory, which defines the appearance of
  /// "ink" splashes that occur in response to taps.
  ///
  /// In M2 mode FlexColorScheme passes in the effective splashFactory
  /// from splashFactory override value or the result from
  /// [FlexSubThemesData] adaptive splash settings. In M3 mode it is kept
  /// null and the default comes via ThemeData.splashFactory, that is has
  /// also defined.
  final InteractiveInkFeatureFactory? splashFactory,

  /// The style for the segmented button's [Text] widget descendants.
  ///
  /// The color of the [textStyle] is typically not used directly, the
  /// [selectedSchemeColor] and [unselectedForegroundSchemeColor] are
  /// used instead.
  final MaterialStateProperty<TextStyle?>? textStyle,

  /// A temporary flag used to opt-in to Material 3 features.
  ///
  /// If set to true, the theme will use Material3 default styles when
  /// properties are undefined, if false defaults will use FlexColorScheme's
  /// own opinionated default values.
  ///
  /// The M2/M3 defaults will only be used for properties that are not
  /// defined, if defined they keep their defined values.
  ///
  /// If undefined, defaults to false.
  final bool? useMaterial3,
}) {
  final bool useM3 = useMaterial3 ?? false;
  final bool tintInteract = useTintedInteraction ?? false;
  final bool tintDisable = useTintedDisable ?? false;
  // We are using a light colorScheme.
  final bool isLight = colorScheme.brightness == Brightness.light;

  // Get selected background color, defaults to secondaryContainer.
  final SchemeColor selectedScheme = selectedSchemeColor ??
      (useM3 ? SchemeColor.secondaryContainer : SchemeColor.primary);
  final Color selectedColor = schemeColor(selectedScheme, colorScheme);
  final Color onSelectedColor = schemeColorPair(selectedScheme, colorScheme);

  final Color unselectedColor =
      schemeColor(unselectedSchemeColor ?? SchemeColor.surface, colorScheme);
  final Color onUnselectedColor = schemeColor(
      unselectedForegroundSchemeColor ??
          onSchemeColor(unselectedSchemeColor ?? SchemeColor.surface),
      colorScheme);

  // Using these tinted overlay variable in all themes for ease of
  // reasoning and duplication.
  final Color overlay = onSelectedColor;
  final Color tint = selectedColor;
  // Get brightness of selectedColor color.
  final bool selectedBgIsLight =
      ThemeData.estimateBrightnessForColor(selectedColor) == Brightness.light;
  // We use surface mode tint factor, if it is light theme and selectedColor
  // is light OR if it is a dark theme and background is dark.
  final bool selectedSurfaceMode =
      (isLight && selectedBgIsLight) || (!isLight && !selectedBgIsLight);
  final double factor =
      _tintAlphaFactor(tint, colorScheme.brightness, selectedSurfaceMode);

  final Color unOverlay = unselectedColor;
  final Color unTint = unselectedSchemeColor == null ||
          unselectedSchemeColor == SchemeColor.surface
      ? selectedColor
      : onUnselectedColor;
  // Get brightness of unselectedColor color.
  final bool unSelectedBgIsLight =
      ThemeData.estimateBrightnessForColor(unselectedColor) ==
          Brightness.light;
  // We use surface mode tint factor, if it is light theme and unselectedColor
  // is light OR if it is a dark theme and background is dark.
  final bool unSelectedSurfaceMode =
      (isLight && unSelectedBgIsLight) || (!isLight && !unSelectedBgIsLight);
  final double unFactor =
      _tintAlphaFactor(unTint, colorScheme.brightness, unSelectedSurfaceMode);

  final Color disableTint = unselectedSchemeColor == null ||
          unselectedSchemeColor == SchemeColor.surface
      ? selectedColor
      : onUnselectedColor;

  final Color borderColor = schemeColor(
      borderSchemeColor ??
          (useM3 ? SchemeColor.outline : SchemeColor.primary),
      colorScheme);
  // Effective border width.
  final double effectiveWidth = borderWidth ?? kThinBorderWidth;

  final Color disableBorderTint = (borderSchemeColor == null && useM3) ||
          unselectedSchemeColor == SchemeColor.outline
      ? selectedColor
      : borderColor;

  final Color disabledForeground = unselectedSchemeColor == null
      ? colorScheme.onSurface
      : onUnselectedColor;

  return SegmentedButtonThemeData(
    style: ButtonStyle(
      textStyle: textStyle,
      splashFactory: splashFactory,
      // TODO(rydmike): Issue, minimumSize property does nothing.
      // https://github.com/flutter/flutter/issues/121493
      minimumSize: ButtonStyleButton.allOrNull<Size>(
          minButtonSize ?? (useM3 ? null : kButtonMinSize)),
      padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
      backgroundColor:
          MaterialStateProperty.resolveWith((Set<MaterialState> states) {
        if (states.contains(MaterialState.disabled)) {
          return unselectedSchemeColor == null ? null : unselectedColor;
        }
        if (states.contains(MaterialState.selected)) {
          return selectedColor;
        }
        return unselectedSchemeColor == null ? null : unselectedColor;
      }),
      foregroundColor:
          MaterialStateProperty.resolveWith((Set<MaterialState> states) {
        if (states.contains(MaterialState.disabled)) {
          if (tintDisable) {
            return tintedDisable(disabledForeground, disableTint);
          }
          return disabledForeground.withAlpha(kAlphaDisabled);
        }
        if (states.contains(MaterialState.selected)) {
          if (states.contains(MaterialState.pressed)) {
            return onSelectedColor;
          }
          if (states.contains(MaterialState.hovered)) {
            return onSelectedColor;
          }
          if (states.contains(MaterialState.focused)) {
            return onSelectedColor;
          }
          return onSelectedColor;
        } else {
          return onUnselectedColor;
        }
      }),
      // TODO(Rydmike): Issue: Flutter SDK SegmentedButton overlayColor bug.
      // https://github.com/flutter/flutter/issues/123308
      // SegmentedButton triggers overlay 3 times in Selected mode, 1st
      // time it is selected, next time it is no longer selected,
      // even it if actually is. This results is that we never see the
      // selected overlay state. It is also triggered 3 times when not
      // selected, but there we get the unselected mode all times, so
      // it is not noticed, still one call would be enough.
      overlayColor:
          MaterialStateProperty.resolveWith((Set<MaterialState> states) {
        // This nicer overlay for selected overlay never gets seen due
        // to above mentioned Flutter SDK bug. But if it is ever fixed it
        // will get used automatically, via code below.
        if (states.contains(MaterialState.selected)) {
          if (states.contains(MaterialState.hovered)) {
            if (tintInteract) return tintedHovered(overlay, tint, factor);
            return unselectedColor.withAlpha(kAlphaHovered);
          }
          if (states.contains(MaterialState.focused)) {
            if (tintInteract) return tintedFocused(overlay, tint, factor);
            return unselectedColor.withAlpha(kAlphaFocused);
          }
          if (states.contains(MaterialState.pressed)) {
            if (tintInteract) return tintedPressed(overlay, tint, factor);
            return unselectedColor.withAlpha(kAlphaPressed);
          }
        } else {
          if (states.contains(MaterialState.hovered)) {
            if (tintInteract) {
              return tintedHovered(unOverlay, unTint, unFactor);
            }
            return unselectedSchemeColor == null
                ? colorScheme.onSurface.withAlpha(kAlphaHovered)
                : onUnselectedColor.withAlpha(kAlphaHovered);
          }
          if (states.contains(MaterialState.focused)) {
            if (tintInteract) {
              return tintedFocused(unOverlay, unTint, unFactor);
            }
            return unselectedSchemeColor == null
                ? colorScheme.onSurface.withAlpha(kAlphaFocused)
                : onUnselectedColor.withAlpha(kAlphaFocused);
          }
          if (states.contains(MaterialState.pressed)) {
            if (tintInteract) {
              return tintedPressed(unOverlay, unTint, unFactor);
            }
            return unselectedSchemeColor == null
                ? colorScheme.onSurface.withAlpha(kAlphaPressed)
                : onUnselectedColor.withAlpha(kAlphaPressed);
          }
        }
        return null;
      }),
      side: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
        if (states.contains(MaterialState.disabled)) {
          if (tintDisable) {
            return BorderSide(
              color: tintedDisable(colorScheme.onSurface, disableBorderTint)
                  .withAlpha(kAlphaLowDisabled),
              width: effectiveWidth,
            );
          }
          return BorderSide(
            color: colorScheme.onSurface.withAlpha(kAlphaVeryLowDisabled),
            width: effectiveWidth,
          );
        }
        return BorderSide(
          color: borderColor,
          width: effectiveWidth,
        );
      }),
      shape: radius == null
          ? null
          : ButtonStyleButton.allOrNull<OutlinedBorder>(
              RoundedRectangleBorder(
                borderRadius: BorderRadius.all(
                  Radius.circular(radius),
                ),
              ),
            ),
    ),
  );
}