rydmike / flex_color_scheme

A Flutter package to make and use beautiful color scheme based themes.
Other
887 stars 100 forks source link

When setting up InputDecoration contentPadding, you must set isDense or it won't work #196

Open laterdayi opened 8 months ago

laterdayi commented 8 months ago
super(
          decoration: decoration != null
              ? decoration.copyWith(contentPadding: _contentPadding(size), )
              : InputDecoration(contentPadding: _contentPadding(size),),
        );

  static EdgeInsetsGeometry? _contentPadding(CustomTextFormFieldSize size) {
    switch (size) {
      case CustomTextFormFieldSize.small:
        return const EdgeInsets.symmetric(horizontal: 12, vertical: 6);
      case CustomTextFormFieldSize.middle:
        return const EdgeInsets.symmetric(horizontal: 12, vertical: 12);
      case CustomTextFormFieldSize.large:
        return const EdgeInsets.symmetric(horizontal: 12, vertical: 20);
    }
  }

When set to CustomTextFormFieldSize. Small, contentPadding cannot change, you must specify isDense will only take effect, when CustomTextFormFieldSize. Large when it is normal

rydmike commented 8 months ago

Hi @laterdayi,

Interesting find 👍🏻 Yes indeed if I make a larger padding, like this:

final ThemeData lightTheme = FlexThemeData.light(
  scheme: FlexScheme.indigoM3,
  subThemesData: const FlexSubThemesData(
    useM2StyleDividerInM3: true,
    inputDecoratorRadius: 12.0,
    popupMenuRadius: 10.0,
    menuRadius: 12.0,
    menuPadding: EdgeInsetsDirectional.fromSTEB(10, 20, 10, 16),
    menuIndicatorRadius: 8.0,
  ),
  keyColors: const FlexKeyColors(),
  tones: FlexTones.chroma(Brightness.light),
  useMaterial3: true,
);

and this

final InputDecorationTheme inputDecorationThemeLight =
    lightTheme.inputDecorationTheme.copyWith(
  isDense: false,
  contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 30),
);

and in MaterialApp set light theme to:

      theme: lightTheme.copyWith(
        inputDecorationTheme: inputDecorationThemeLight,
      ),

Screenshot 2023-10-14 at 23 53 26

Then if I use:

final InputDecorationTheme inputDecorationThemeLight =
    lightTheme.inputDecorationTheme.copyWith(
  isDense: true,
  contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 30),
);

or

final InputDecorationTheme inputDecorationThemeLight =
    lightTheme.inputDecorationTheme.copyWith(
  contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 30),
);

The result is identical.


However, you are right if I use:

final InputDecorationTheme inputDecorationThemeLight =
    lightTheme.inputDecorationTheme.copyWith(
  isDense: true,
  contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
);

I get this:

Screenshot 2023-10-14 at 23 57 53

But if I use:

final InputDecorationTheme inputDecorationThemeLight =
    lightTheme.inputDecorationTheme.copyWith(
  isDense: false,
  contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
);

or

final InputDecorationTheme inputDecorationThemeLight =
    lightTheme.inputDecorationTheme.copyWith(
  contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
);

The result is this:

Screenshot 2023-10-15 at 00 04 21

It appears the contentPadding will grow but not shrink beyond a certain limit, if isDense is not set to true.

Let's check the docs:

  /// Whether the input decorator's child is part of a dense form (i.e., uses
  /// less vertical space).
  ///
  /// Defaults to false.
  final bool isDense;

  /// The padding for the input decoration's container.
  ///
  /// The decoration's container is the area which is filled if
  /// [InputDecoration.filled] is true and bordered per the [border].
  /// It's the area adjacent to [InputDecoration.icon] and above the
  /// [InputDecoration.icon] and above the widgets that contain
  /// [InputDecoration.helperText], [InputDecoration.errorText], and
  /// [InputDecoration.counterText].
  ///
  /// By default the [contentPadding] reflects [isDense] and the type of the
  /// [border]. If [isCollapsed] is true then [contentPadding] is
  /// [EdgeInsets.zero].
  final EdgeInsetsGeometry? contentPadding;

No clue there why this is so. Let's check the relevant Flutter code:

    // The _Decoration widget and _RenderDecoration assume that contentPadding
    // has been resolved to EdgeInsets.
    final TextDirection textDirection = Directionality.of(context);
    final EdgeInsets? decorationContentPadding = decoration.contentPadding?.resolve(textDirection);

    final EdgeInsets contentPadding;
    final double floatingLabelHeight;
    if (decoration.isCollapsed) {
      floatingLabelHeight = 0.0;
      contentPadding = decorationContentPadding ?? EdgeInsets.zero;
    } else if (!border.isOutline) {
      // 4.0: the vertical gap between the inline elements and the floating label.
      floatingLabelHeight = (4.0 + 0.75 * labelStyle.fontSize!) * MediaQuery.textScaleFactorOf(context);
      if (decoration.filled ?? false) {
        contentPadding = decorationContentPadding ?? (decorationIsDense
          ? const EdgeInsets.fromLTRB(12.0, 8.0, 12.0, 8.0)
          : const EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 12.0));
      } else {
        // Not left or right padding for underline borders that aren't filled
        // is a small concession to backwards compatibility. This eliminates
        // the most noticeable layout change introduced by #13734.
        contentPadding = decorationContentPadding ?? (decorationIsDense
          ? const EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 8.0)
          : const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 12.0));
      }
    } else {
      floatingLabelHeight = 0.0;
      contentPadding = decorationContentPadding ?? (decorationIsDense
        ? const EdgeInsets.fromLTRB(12.0, 20.0, 12.0, 12.0)
        : const EdgeInsets.fromLTRB(12.0, 24.0, 12.0, 16.0));
    }

Gotta say it looks like it with the outline border, it should be hitting that last else, that is what I originally thought and thus we should get whatever content padding value is defined as padding, regardless of the isDense setting, BUT we do not, it does not shrink as much as it is set to.


However, if you do something like this:

final InputDecorationTheme inputDecorationThemeLight =
    lightTheme.inputDecorationTheme.copyWith(
  isDense: false,
  constraints: BoxConstraints.tight(const Size(double.infinity, 20)),
  contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 0),
);

The isDense won't matter, and you get this:

Screenshot 2023-10-15 at 00 33 10

You can make it super slim, so slim it becomes useless. With content padding only you cannot do that, so you cannot shoot yourself in the foot with it.


Of course none of these things are related to FlexColorScheme, this is just vanilla Flutter SDK behavior with these properties, but certainly an interesting find. From FCS point of view, it does whatever the props in Flutter do, even if they area bit odd 😄

Later I might dig into why it actually behaves like this by debugging it further. It could be some default min constraints, but at quick glance I did not find where it would set that to be tighter when using isDense true, but it might be in there somewhere, or it might be something else.


EDIT:

I will add this find to the property docs in FlexColorScheme for version 7.4.0 though, as it is useful info that the Flutter docs do not mention anywhere.

laterdayi commented 8 months ago

I am also confused about this part, it is not clear why flutter is designed this way, if only set the constraints: BoxConstraints.tight(const Size(double-infinity, 60)) without setting the contentPadding then the input box will be very small and the cursor will be very short

rydmike commented 8 months ago

While this is technically not a FlexColorScheme issue, I'm keeping this issue open to remind myself to dig a bit deeper into why this works the way it does. Maybe it is as intended in Flutter too, maybe not. If not, I might open an issue about it in Flutter SDK. I already updated the doc for the properties in FCS docs for next version a bit.

Good find! 👍🏻 😄