inputDecorationTheme static method

InputDecorationTheme inputDecorationTheme({
  1. required ColorScheme colorScheme,
  2. TextTheme? textTheme,
  3. SchemeColor? baseSchemeColor,
  4. double? radius,
  5. FlexInputBorderType? borderType,
  6. bool filled = true,
  7. Color? fillColor,
  8. int? backgroundAlpha,
  9. SchemeColor? prefixIconSchemeColor,
  10. SchemeColor? borderSchemeColor,
  11. double? focusedBorderWidth,
  12. double? unfocusedBorderWidth,
  13. double gapPadding = 4,
  14. bool unfocusedHasBorder = true,
  15. bool focusedHasBorder = true,
  16. bool unfocusedBorderIsColored = true,
  17. bool? tintedInteractions,
  18. bool? tintedDisabled,
  19. bool? useMaterial3,
})

An opinionated InputDecorationTheme, with optional fill color and adjustable corner radius.

Requires a ColorScheme in colorScheme. The color scheme would typically be equal the color scheme also used to define the color scheme for your app theme.

Comes with many parameters to adjust the style of the input decorator. For example the the corner radius can be adjusted. In Material 2 mode it defaults to kInputDecoratorRadius which is 16, in Material 3 mode it defaults to kInputDecoratorM3Radius which is 4, following the Material 3 design specification.

Implementation

static InputDecorationTheme inputDecorationTheme({
  /// Typically the same [ColorScheme] that is also use for your [ThemeData].
  required final ColorScheme colorScheme,

  /// Optional provided effective TextTheme to use as base for the
  /// input decorations.
  ///
  /// A default one is used if not provided.
  final TextTheme? textTheme,

  /// Selects which color from the passed in colorScheme to use for the border
  /// and fill color of the input decorator.
  ///
  /// All colors in the color scheme are not good choices, but some work well.
  /// You can give the border a separate color value by defining
  /// [borderSchemeColor].
  ///
  /// If not defined, [colorScheme.primary] will be used.
  final SchemeColor? baseSchemeColor,

  /// The decorated input fields corner border radius.
  ///
  /// If not defined, in Material 2 mode defaults to [kInputDecoratorRadius]
  /// which is 16, in Material 3 mode it defaults to
  /// [kInputDecoratorM3Radius] which is 4, following the Material
  /// 3 design specification.
  final double? radius,

  /// Selects input border type.
  ///
  /// If undefined, defaults to [FlexInputBorderType.outline].
  final FlexInputBorderType? borderType,

  /// If true the decoration's container is filled with [fillColor].
  ///
  /// Typically this field set to true if [border] is an
  /// [UnderlineInputBorder].
  ///
  /// The decoration's container is the area, defined by the border's
  /// [InputBorder.getOuterPath], which is filled if [filled] is
  /// true and bordered per the [border].
  ///
  /// Defaults to true.
  final bool filled = true,

  /// An optional totally custom fill color used to fill the
  /// `InputDecorator` background with, when `filled` is true.
  ///
  /// If null, defaults to color scheme color defined by `baseColor`
  /// withAlpha(0x0D) (5%) if color scheme is light and withAlpha(0x14) (8%)
  /// if color scheme is dark.
  final Color? fillColor,

  /// Defines the alpha, opacity channel value used as opacity on effective
  /// [InputDecorator] background color.
  ///
  /// If defined, the valid range is 0 to 255 (0x00 to 0xFF), if out of bounds
  /// it is capped to closest valid value.
  ///
  /// If not defined, in M3 mode it defaults to 0xFF fully opaque. In M2 mode
  /// defaults to [kFillColorAlphaLight] (0x0D = 5% opacity) in light theme
  /// and to [kFillColorAlphaDark] (0x14 = 8% opacity) in dark mode.
  final int? backgroundAlpha,

  /// The icon color of the prefixIcon in a focused [InputDecoration].
  ///
  /// If not defined defaults to [baseSchemeColor] in FCS M2 and to
  /// [SchemeColor.onSurface] in FCS M3.
  final SchemeColor? prefixIconSchemeColor,

  /// Selects which color from the passed in colorScheme to use as the border
  /// color of the input decorator.
  ///
  /// The color is used by the focused border, but also as slight opacity
  /// based color on unfocused border, when [unfocusedHasBorder] and
  /// [unfocusedBorderIsColored] are true.
  ///
  /// All colors in the color scheme are not good choices, but some work well.
  ///
  /// If not defined, [baseSchemeColor] will be used.
  final SchemeColor? borderSchemeColor,

  /// The border width when the input is selected.
  ///
  /// If null, defaults to [kThickBorderWidth] = 2.
  final double? focusedBorderWidth,

  /// The border width when the input is unselected or disabled.
  ///
  /// If null, defaults to [kThinBorderWidth] = 1.
  final double? unfocusedBorderWidth,

  /// Horizontal padding on either side of the border's
  /// [InputDecoration.labelText] width gap.
  ///
  /// Defaults to 4, which is also the default in SDK default input decorator.
  final double gapPadding = 4,

  /// Unfocused input decoration has a border.
  ///
  /// Defaults to true.
  ///
  /// Applies to both outline and underline mode. You would typically
  /// use this in a design where you use a fill color and want unfocused
  /// input fields to only be highlighted by the fill color and not even
  /// have an unfocused input border style.
  ///
  /// When set to false, there is no border bored on states enabledBorder and
  /// disabledBorder, there is a border on focusedBorder, focusedErrorBorder
  /// and errorBorder, so error thus has a border also when it is not focused.
  final bool unfocusedHasBorder = true,

  /// Focused input decoration has a border.
  ///
  /// Defaults to true.
  ///
  /// Applies to both outline and underline mode. You would typically
  /// use this in a design where you use a fill color and want focused
  /// input fields to only be highlighted by the fill color and not even
  /// have an unfocused input border style.
  ///
  /// When set to false, there is no border bored on states enabledBorder and
  /// disabledBorder, there is a border on focusedBorder, focusedErrorBorder
  /// and errorBorder, so error thus has a border also when it is not focused.
  final bool focusedHasBorder = true,

  /// Unfocused input decoration border uses the color baseScheme color.
  ///
  /// Applies to both outline and underline mode.
  ///
  /// When set to true, the unfocused borders also uses the [baseSchemeColor]
  /// as its border color, but with alpha [kEnabledBorderAlpha] (90%).
  ///
  /// If set to false, the color uses the SDK default unselected border color,
  /// which is [ColorScheme.onSurface] with 38% opacity.
  ///
  /// The unfocused border color selection also applies to it hovered state.
  ///
  /// Defaults to true.
  final bool unfocusedBorderIsColored = true,

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

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

  /// 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 = tintedInteractions ?? false;
  final bool tintDisable = tintedDisabled ?? false;
  // Used color scheme is for dark mode.
  final bool isDark = colorScheme.brightness == Brightness.dark;

  // Get selected color, defaults to primary.
  final Color baseColor =
      schemeColor(baseSchemeColor ?? SchemeColor.primary, colorScheme);

  // Used border color, for focus and unfocused when that option is used.
  final Color borderColor = schemeColor(
      borderSchemeColor ?? baseSchemeColor ?? SchemeColor.primary,
      colorScheme);

  // Get effective alpha value for background color.
  final int effectiveAlpha = backgroundAlpha?.clamp(0, 255) ??
      (useM3
          ? 0xFF
          : isDark
              ? kFillColorAlphaDark
              : kFillColorAlphaLight);

  // Tinted disabled colors
  final Color tintDisabledColor =
      tintedDisable(colorScheme.onSurface, fillColor ?? baseColor);
  final Color tintDisabledUltraLowColor =
      tintedDisable(colorScheme.onSurface, fillColor ?? baseColor)
          .withAlpha(kAlphaUltraLowDisabled);

  // Effective used fill color, can also be a totally custom color value.
  // These alpha blends remove the actual opacity and create a none opaque
  // color of the result of as if InputDecorator was used on surface color.
  // Usually this is close enough. This operations makes it possible to
  // use a simple lighter and dark color color of this net effect for
  // the hover effect, which is also actually not transparent.
  final Color usedFillColor = fillColor != null
      ? Color.alphaBlend(
          fillColor.withAlpha(effectiveAlpha), colorScheme.surface)
      : MaterialStateColor.resolveWith((Set<MaterialState> states) {
          if (states.contains(MaterialState.disabled)) {
            return tintDisable
                ? tintDisabledUltraLowColor
                : colorScheme.onSurface
                    .withAlpha(kAlphaUltraLowDisabled); // M3 spec, 4%, 0x0A
          }
          return baseSchemeColor == null && useM3
              ? Color.alphaBlend(
                  colorScheme.surfaceVariant.withAlpha(effectiveAlpha),
                  colorScheme.surface)
              : Color.alphaBlend(
                  baseColor.withAlpha(effectiveAlpha), colorScheme.surface);
        });

  // A custom lighter and darker version of the effective background
  // color on the input decorator as hover color. This is a different formula
  // than otherwise used for the overall based hover colors by FCS. It was the
  // only way it was possible to make a nice tinted version that worked well
  // with any config that it is possible to create for the background on
  // the InputDecorator.
  final Color tintedHover =
      ThemeData.estimateBrightnessForColor(usedFillColor) == Brightness.light
          ? usedFillColor.darken(kInputDecoratorLightBgDarken)
          : usedFillColor.lighten(kInputDecoratorDarkBgLighten);

  // PrefixIconColor
  final SchemeColor prefixFallback =
      useM3 ? SchemeColor.onSurface : baseSchemeColor ?? SchemeColor.primary;
  final Color prefixIconColor =
      schemeColor(prefixIconSchemeColor ?? prefixFallback, colorScheme);

  // Flutter SDK "magic" theme colors from ThemeData, with old M1/M2 roots.
  final Color hintColorM2 =
      isDark ? Colors.white60 : Colors.black.withAlpha(kTintHover); // 60%
  final Color suffixIconColorM2 = isDark ? Colors.white70 : Colors.black45;
  final Color disabledColorM2 = isDark ? Colors.white38 : Colors.black38;

  // Enabled border color.
  final Color enabledBorder = unfocusedBorderIsColored
      ? borderColor.withAlpha(kEnabledBorderAlpha)
      : useM3
          ? borderType == FlexInputBorderType.underline
              ? colorScheme.onSurfaceVariant
              : colorScheme.outline
          : colorScheme.onSurface.withAlpha(kAlphaDisabled);

  // Default border radius.
  final double effectiveRadius =
      radius ?? (useM3 ? kInputDecoratorM3Radius : kInputDecoratorRadius);

  // Default outline widths.
  final double unfocusedWidth = unfocusedBorderWidth ?? kThinBorderWidth;
  final double focusedWidth = focusedBorderWidth ?? kThickBorderWidth;

  final InputBorder effectiveInputBorder =
      borderType == FlexInputBorderType.underline
          ? UnderlineInputBorder(
              borderRadius: BorderRadius.only(
                topLeft: Radius.circular(effectiveRadius),
                topRight: Radius.circular(effectiveRadius),
              ),
            )
          : OutlineInputBorder(
              gapPadding: gapPadding,
              borderRadius: BorderRadius.all(
                Radius.circular(effectiveRadius),
              ),
            );

  return InputDecorationTheme(
    labelStyle:
        MaterialStateTextStyle.resolveWith((Set<MaterialState> states) {
      if (states.contains(MaterialState.error)) {
        if (states.contains(MaterialState.focused)) {
          return TextStyle(color: colorScheme.error);
        }
        if (states.contains(MaterialState.hovered)) {
          return TextStyle(color: colorScheme.onErrorContainer);
        }
        return TextStyle(color: colorScheme.error);
      }
      if (states.contains(MaterialState.focused)) {
        return TextStyle(color: baseColor);
      }
      if (states.contains(MaterialState.hovered)) {
        return TextStyle(color: colorScheme.onSurfaceVariant);
      }
      if (states.contains(MaterialState.disabled)) {
        return tintDisable
            ? TextStyle(color: tintDisabledColor)
            : TextStyle(
                color: colorScheme.onSurface.withAlpha(kAlphaDisabled));
      }
      return TextStyle(
          color: useM3 ? colorScheme.onSurfaceVariant : hintColorM2);
    }),
    floatingLabelStyle:
        MaterialStateTextStyle.resolveWith((Set<MaterialState> states) {
      if (states.contains(MaterialState.error)) {
        if (states.contains(MaterialState.focused)) {
          return TextStyle(color: colorScheme.error);
        }

        if (states.contains(MaterialState.hovered)) {
          // TODO(rydmike): Info: M3 uses onErrorContainer.
          // TextStyle(color: colorScheme.onErrorContainer)
          // Excluding it, prefer error as float label color, FCS opinionated.
          return TextStyle(
            color: colorScheme.error.withAlpha(kEnabledBorderAlpha),
          );
        }
        return TextStyle(color: colorScheme.error);
      }
      if (states.contains(MaterialState.focused)) {
        return TextStyle(color: borderColor);
      }
      if (states.contains(MaterialState.hovered)) {
        return TextStyle(color: colorScheme.onSurfaceVariant);
      }
      if (states.contains(MaterialState.disabled)) {
        return tintDisable
            ? TextStyle(color: tintDisabledColor)
            : TextStyle(
                color: useM3
                    ? colorScheme.onSurface.withAlpha(kAlphaDisabled)
                    : disabledColorM2);
      }
      return TextStyle(
          color: useM3 ? colorScheme.onSurfaceVariant : hintColorM2);
    }),
    helperStyle:
        MaterialStateTextStyle.resolveWith((Set<MaterialState> states) {
      if (states.contains(MaterialState.disabled)) {
        return tintDisable
            ? TextStyle(color: tintDisabledColor)
            : TextStyle(
                color: useM3
                    ? colorScheme.onSurface.withAlpha(kAlphaDisabled)
                    : Colors.transparent);
      }
      return TextStyle(
          color: useM3 ? colorScheme.onSurfaceVariant : hintColorM2);
    }),
    hintStyle:
        MaterialStateTextStyle.resolveWith((Set<MaterialState> states) {
      if (states.contains(MaterialState.disabled)) {
        return TextStyle(
            color: tintDisable ? tintDisabledColor : disabledColorM2);
      }
      return TextStyle(color: hintColorM2);
    }),
    iconColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
      if (states.contains(MaterialState.disabled)) {
        return tintDisable
            ? tintDisabledColor
            : colorScheme.onSurface.withAlpha(kAlphaDisabled);
      }
      if (states.contains(MaterialState.focused)) {
        return useM3 ? colorScheme.onSurfaceVariant : baseColor;
      }
      return useM3 ? colorScheme.onSurfaceVariant : suffixIconColorM2;
    }),
    prefixIconColor:
        MaterialStateColor.resolveWith((Set<MaterialState> states) {
      if (states.contains(MaterialState.disabled)) {
        return tintDisable
            ? tintDisabledColor
            : colorScheme.onSurface.withAlpha(kAlphaDisabled);
      }
      if (states.contains(MaterialState.focused)) {
        return prefixIconColor;
      }
      return useM3 ? colorScheme.onSurfaceVariant : suffixIconColorM2;
    }),
    suffixIconColor:
        MaterialStateColor.resolveWith((Set<MaterialState> states) {
      if (states.contains(MaterialState.error)) {
        // TODO(rydmike): Info: M3 uses onErrorContainer.
        // if (states.contains(MaterialState.hovered)) {
        //   return colorScheme.onErrorContainer;
        // }
        // Excluding it, prefer suffix icon as error color, FCS opinionated.
        return colorScheme.error;
      }
      if (states.contains(MaterialState.disabled)) {
        return tintDisable
            ? tintDisabledColor
            : colorScheme.onSurface.withAlpha(kAlphaDisabled);
      }
      if (states.contains(MaterialState.focused)) {
        return useM3 ? colorScheme.onSurfaceVariant : baseColor;
      }
      return useM3 ? colorScheme.onSurfaceVariant : suffixIconColorM2;
    }),
    filled: filled,
    fillColor: usedFillColor,
    hoverColor: tintInteract ? tintedHover : null,
    focusedBorder: effectiveInputBorder.copyWith(
      borderSide: focusedHasBorder
          ? BorderSide(
              color: borderColor,
              width: focusedWidth,
            )
          : BorderSide.none,
    ),
    enabledBorder: effectiveInputBorder.copyWith(
      borderSide: unfocusedHasBorder
          ? BorderSide(
              color: enabledBorder,
              width: unfocusedWidth,
            )
          : BorderSide.none,
    ),
    disabledBorder: effectiveInputBorder.copyWith(
      borderSide: unfocusedHasBorder
          ? BorderSide(
              color: tintDisable
                  ? tintDisabledColor.withAlpha(kAlphaLowDisabled)
                  : borderType == FlexInputBorderType.underline
                      ? colorScheme.onSurface.withAlpha(kAlphaDisabled)
                      : colorScheme.onSurface
                          .withAlpha(kAlphaVeryLowDisabled),
              width: unfocusedWidth,
            )
          : BorderSide.none,
    ),
    focusedErrorBorder: effectiveInputBorder.copyWith(
      borderSide: focusedHasBorder
          ? BorderSide(
              color: colorScheme.error,
              width: focusedWidth,
            )
          : BorderSide.none,
    ),
    errorBorder: effectiveInputBorder.copyWith(
        borderSide: focusedHasBorder
            ? BorderSide(
                color: colorScheme.error
                    .withAlpha(useM3 ? 0xFF : kEnabledBorderAlpha),
                width: unfocusedWidth,
              )
            : BorderSide.none),
  );
}