Skip to content

feat(remix): add textarea component - #107

Merged
tilucasoli merged 11 commits into
mainfrom
feat/text-area
Aug 5, 2026
Merged

feat(remix): add textarea component#107
tilucasoli merged 11 commits into
mainfrom
feat/text-area

Conversation

@leoafarias

@leoafarias leoafarias commented Aug 4, 2026

Copy link
Copy Markdown
Member

Description

Adds RemixTextArea as a constructor-only multiline facade over RemixTextField. It shares the same TextFieldSpec, TextFieldStyler, NakedTextField, controller, focus, editing, selection, and semantics implementation—there is no second editable pipeline or TextArea-specific generated style machinery.

Component API

RemixTextArea supplies safe multiline defaults:

  • minLines: 2
  • maxLines: null
  • keyboardType: TextInputType.multiline
  • textInputAction: TextInputAction.newline
  • expands: false
  • obscureText: false

It forwards the supported TextField controller, focus, editing, selection, scrolling, restoration, IME, accessory, semantics, style, and raw style-spec surface.

Shared TextField corrections

  • Multiline hints align to directional top-start while single-line hints remain centered.
  • Label, hint, helper, and error semantics are announced once.
  • Interactive leading/trailing accessories remain separate semantic nodes.
  • Whole-control fallback taps preserve focus and press behavior without duplicating semantic actions.
  • Editable selection and scrolling remain owned by NakedTextField.
  • Input-row CrossAxisAlignment.baseline uses Flutter's alphabetic baseline and renders real text/accessories without the prior Row assertion.

Mix/code-generation surface

  • container is now StyleSpec<BoxSpec> and accepts BoxStyler, matching the actual outer renderer.
  • Input-row spacing and crossAxisAlignment are generated top-level fields.
  • The outer label/input/helper layout remains StyleSpec<FlexBoxSpec>.
  • No handwritten generated-style forwarding extension was added.
  • Clean generation reproduced all 25 committed artifacts byte-for-byte.

Migration example:

// Before
TextFieldStyler().container(
  FlexBoxStyler()
    .spacing(8)
    .crossAxisAlignment(CrossAxisAlignment.center),
);

// After
TextFieldStyler()
    .container(BoxStyler())
    .spacing(8)
    .crossAxisAlignment(CrossAxisAlignment.center);

Visual Evidence

Captured and visually verified locally; the PNGs are ignored and are not part of the package diff:

  • .context/screenshots/textarea-light.png — 368×796, SHA-256 fed0cd66eb4e9d01fe79dcf829c5997a26ed467055d3502b49ff222e94c97eec
  • .context/screenshots/textarea-dark.png — 368×796, SHA-256 e2edf10274afa5b07bb220de9d83bd15e4a2b34ca5c3f3d2d41b6078f65ddee0

The captures include focused empty, filled multiline, error, disabled, read-only, and custom-styled states. Hosted GitHub attachment upload remains pending because the connector and CLI do not expose the issue-attachment endpoint.

Validation

  • fvm flutter test packages/remix/test/components/textfield packages/remix/test/public_api_test.dart packages/remix/test/public_api_compatibility_test.dart — 170/170 passed on hosted naked_ui 1.0.0-beta.9, including multiline scrolling, selection-drag, and rapid double-tap pressed-state regressions.
  • fvm dart run melos run generate:check — 25 generated artifacts reproduced byte-for-byte.
  • fvm flutter analyze --fatal-infos — clean.
  • Fortal parity verified the hosted naked_ui 1.0.0-beta.9 resolution while retaining the compatible ^1.0.0-beta.8 package constraint.
  • GitHub test / Run tests for all packages on final head d1a06e269 — passed.
  • Formatting, conflict-marker scan, and git diff --check — clean.

naked_ui 1.0.0-beta.9 contains the pressed-state lifecycle fix from conceptadev/naked_ui#84, and this branch's lockfile now resolves that hosted release.

Related Issues


Draft gates

  • TextArea API/forwarding review complete.
  • Shared semantics/gesture review complete.
  • Mix code-generation and baseline render review complete.
  • Local light/dark screenshots captured, retained, and visually inspected.
  • Naked UI refactor(remix): generate Fortal wrappers #84 is merged and released in a compatible prerelease.
  • Remix lock resolves that release and all TextField/TextArea tests pass.
  • Full repository CI passes on the final dependency resolution.
  • Hosted screenshots are attached to this PR.
  • Manual VoiceOver/TalkBack, web accessibility-tree, keyboard/selection, RTL, narrow-width, and 200% text-scale checks are recorded.

Breaking Change

Does this PR require users of the package to manually update their code?

  • Yes, this is a breaking change.
  • No, this is not a breaking change.

Consumers styling TextField input-row layout through container(FlexBoxStyler(...)) must migrate container visuals to BoxStyler and use the generated top-level spacing and crossAxisAlignment methods.

Base automatically changed from chore/publish-foundation to main August 4, 2026 21:08
@leoafarias
leoafarias marked this pull request as ready for review August 5, 2026 19:09
leoafarias and others added 2 commits August 5, 2026 17:52
Move `_buildResolved` from `RemixTextField` onto `_RemixTextFieldBodyState`.
Its only caller was that state, and every argument it took — the style
controller, the effective focus node, and two press callbacks — was already
state-owned. The callbacks existed solely to bridge back to
`_updatePressSource`. No behavior change.

Record the two non-obvious invariants in the same file so a later
simplification pass does not undo them:

- The obsolete internal focus node must be disposed in a post-frame callback.
  This state's `didUpdateWidget` runs before `NakedTextField`'s, and Naked
  reads the outgoing node's `hasFocus` to decide whether to move focus onto
  the incoming one. A synchronous dispose detaches and unfocuses first, so
  focus would be dropped across a null -> external swap.
- The `joinedSemanticHint == effectiveSemanticErrorText` check is not dead
  code. `NakedTextField` concatenates `semanticHint` and `semanticErrorText`
  unconditionally without deduplicating, so an identical hint and error would
  be announced twice.

Document that `fortalTextFieldStyle()` must not be applied to `RemixTextArea`:
it pins the input container to a single-line height, so taller content scrolls
inside a one-line box with no layout error to signal it. There is no Fortal
TextArea preset because Radix Themes' TextArea is not in this package's mapped
parity surface, so its metrics have no reference data to derive from.
@tilucasoli
tilucasoli merged commit 183bc34 into main Aug 5, 2026
2 checks passed
@tilucasoli
tilucasoli deleted the feat/text-area branch August 5, 2026 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants