Skip to content

fix: keep borderless form widgets borderless under an app's theme - #160

Merged
christian2denker merged 2 commits into
mainfrom
fix/borderless-widgets-vs-app-theme
Sep 13, 2026
Merged

christian2denker merged 2 commits into
mainfrom
fix/borderless-widgets-vs-app-theme

Conversation

@christian2denker

Copy link
Copy Markdown
Contributor

Symptom

Seit die Labels über den Feldern stehen (2.4.0), hat das Thema von ApptiveTeams wieder eine Linie im Ruhezustand — und die Checkbox „Discussed with Team?" im Urlaubsantrag sitzt seitdem in einem Kasten. Die App kann das nicht abfangen: das InputDecorationTheme gilt für alle Felder oder für keins.

Ursache

Die betroffenen Widgets setzen InputDecoration.border auf InputBorder.none, teils auch errorBorder, aber nie die übrigen Zustände. Ein InputDecorator wählt aber nach Zustand aus und greift auf border nur zurück, wo der passende Eintrag null ist:

// flutter/lib/src/material/input_decorator.dart
if (!decoration.enabled) {
  border = _hasError ? decoration.errorBorder : decoration.disabledBorder;
} else if (isFocused) {
  border = _hasError ? decoration.focusedErrorBorder : decoration.focusedBorder;
} else {
  border = _hasError ? decoration.errorBorder : decoration.enabledBorder;
}
border ??= _getDefaultBorder(themeData, defaults);

Und InputDecoration.applyDefaults füllt genau diese Lücken vorher aus dem InputDecorationTheme der einbettenden App. border: InputBorder.none allein deckt also keinen der sechs Zustände ab, sobald eine App die Einzelzustände stylt. Flutters Doku sagt es wörtlich: „all five borders … must be set."

Änderung

Ein Helfer statt sechs Zeilen achtmal:

extension InputDecorationX on InputDecoration {
  InputDecoration borderInEveryState(InputBorder border) => copyWith(
        border: border, enabledBorder: border, disabledBorder: border,
        focusedBorder: border, errorBorder: border, focusedErrorBorder: border,
      );
  InputDecoration get withoutBorder => borderInEveryState(InputBorder.none);
}

Angewandt in CheckBoxFormWidget, AttachmentFormWidget, GeolocationFormWidget, AddressFormWidget, EnumFormWidget, EnumCollectionFormWidget und den beiden inneren Feldern von DateTimeFormWidget.

Abgrenzung

Betroffen sind nur die Widgets, die ihr eigenes Layout mitbringen. Ein singleSelect als Dropdown ist ein ganz normales Feld und behält den Rahmen der App — EnumFormWidget nutzt den rahmenlosen InputDecorator nur bei type == 'selectList'. Ein eigener Test hält das fest, damit später nicht zu viel unterdrückt wird.

Testing

  • 11 neue Tests in test/border_suppression_test.dart, davon einer, der die Ursache selbst festnagelt: InputDecoration(border: none).applyDefaults(theme) liefert weiterhin den sichtbaren enabledBorder.
  • flutter test in apptive_grid_form: 345/345
  • melos run lint:all: SUCCESS
  • flutter pub publish --dry-run: 0 Warnings

melos run test:all meldet einen Fehlschlag in apptive_grid_theme (Golden-Tests der macOS-Variante). Vorbestehend — schlägt auf dem 2.4.0-Stand cb8efec in einem frischen Worktree genauso fehl, und dieser Branch fasst kein File in dem Paket an.

Gegengeprüft

In ApptiveTeams per Path-Override gebaut, Team ZWEIDENKER → Urlaubsantrag: Checkbox ohne Kasten, Textfelder mit Rahmen. Override wieder entfernt.

Nicht Teil dieses PRs

SignatureFormWidget hat denselben Defekt mit umgekehrtem Vorzeichen: es setzt border und errorBorder auf OutlineInputBorder(), aber nicht die anderen drei, bekommt im Ruhezustand also den Rahmen der App und im Fehlerzustand seinen eigenen. Inkonsistent, aber nicht kaputt — es vollständig zu setzen würde den fokussierten Zustand sichtbar verändern, und das ist eine Design-Entscheidung.

🤖 Generated with Claude Code

christian2denker and others added 2 commits September 13, 2026 08:22
Widgets that bring their own layout set `InputDecoration.border` to
`InputBorder.none`, some of them `errorBorder` as well, but never the
remaining state borders. An `InputDecorator` picks `enabledBorder`,
`disabledBorder`, `focusedBorder`, `errorBorder` or `focusedErrorBorder`
depending on the field's state and only falls back to `border` where the
matching one is `null`. `InputDecoration.applyDefaults` fills those
missing slots from the `InputDecorationTheme` of the embedding app, so
an app that styles the individual states — a visible line in the resting
state, for example — drew a box around a checkbox or an option list.
The app cannot work around it either: the theme applies to every field
or to none.

Sets all five in `CheckBoxFormWidget`, `AttachmentFormWidget`,
`GeolocationFormWidget`, `AddressFormWidget`, `EnumFormWidget`,
`EnumCollectionFormWidget` and the two inner fields of
`DateTimeFormWidget`, through a new `InputDecorationX.withoutBorder`
rather than repeating six lines eight times. `borderInEveryState` covers
the general case.

Only the Widgets that wrap their own content are affected. A single
select rendered as a dropdown is an ordinary field and keeps the border
the app asked for, which a test pins down next to the borderless ones.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the p:form Changes to apptive_grid_form label Sep 13, 2026
@christian2denker
christian2denker merged commit 6227920 into main Sep 13, 2026
6 checks passed
@christian2denker
christian2denker deleted the fix/borderless-widgets-vs-app-theme branch September 13, 2026 06:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

p:form Changes to apptive_grid_form

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant