VisorVisor
FlutterWidgets

VisorTextInput

Animated floating-label text input covering five validation states (default, focused, error, valid, disabled).

VisorTextInput is the Flutter form-field primitive. A floating label animates above the field on focus or when content is present; the surface moves through five validation states (default, focused, error, valid, disabled) using semantic tokens. VisorPasswordInput and VisorPhoneInput build on top of it.

Preview

When to Use

  • Single-line text entry in forms (name, email, address, search)
  • Any field that benefits from a floating label to save vertical space
  • Fields requiring inline validation feedback (error / valid states)

When Not to Use

  • Multi-line text entry (use a textarea / expandable variant)
  • Password fields with a visibility toggle (use VisorPasswordInput)
  • Selecting from a fixed list (use Select or Dropdown)

Installation

npx visor add text-input --target flutter

Or copy components/flutter/visor_text_input/visor_text_input.dart into your project.

Basic Usage

import 'package:ui/ui.dart';

VisorTextInput(
  labelText: 'Email',
  controller: _email,
  keyboardType: TextInputType.emailAddress,
  textInputAction: TextInputAction.next,
)

With Validation

VisorTextInput(
  labelText: 'Email',
  controller: _email,
  validator: (value) {
    if (value == null || value.isEmpty) return 'Required';
    if (!value.contains('@')) return 'Enter a valid email';
    return null;
  },
  autovalidateMode: AutovalidateMode.onUserInteraction,
)

With Prefix Icon

VisorTextInput(
  labelText: 'Search',
  prefixIcon: const Icon(Icons.search),
  onChanged: _runSearch,
)

Async Valid State

VisorTextInput(
  labelText: 'Username',
  controller: _username,
  isValid: _usernameAvailable,
  errorText: _usernameAvailable == false ? 'Already taken' : null,
)

API Reference

PropertyTypeDefaultDescription
labelTextStringrequiredFloating label text
controllerTextEditingController?nullOptional external text controller
focusNodeFocusNode?nullOptional external focus node
prefixIconWidget?nullLeading icon
suffixWidgetWidget?nullTrailing widget (rendered after the valid checkmark when both are present)
errorTextString?nullOverride the error message
onChangedValueChanged<String>?nullFires on every keystroke
onFieldSubmittedValueChanged<String>?nullFires on keyboard submit
validatorString? Function(String?)?nullSynchronous validator
keyboardTypeTextInputType?nullKeyboard type hint
textInputActionTextInputAction?nullKeyboard action button type
autofocusboolfalseRequest focus on first build
enabledbooltrueWhen false, reduces opacity and ignores input
autocorrectbooltrueEnable autocorrect
enableSuggestionsbooltrueEnable keyboard suggestions
textCapitalizationTextCapitalization.noneText capitalization mode
autovalidateModeAutovalidateMode?.onUserInteractionWhen the validator runs
obscureTextboolfalseWhen true, replaces typed characters with bullets
inputFormattersList<TextInputFormatter>?nullOptional input formatters
isValidbool?nullExplicit valid/invalid override (null = derive from validator)
semanticLabelString?nullOverride the announced label (defaults to labelText)

Accessibility

  • Semantics(label: …, enabled: …, textField: true) wraps the field.
  • Reduce-motion aware — when MediaQuery.disableAnimations is true the label-float animation duration collapses to zero.
  • Layout uses EdgeInsetsDirectional so paddings mirror in RTL.

Source

  • components/flutter/visor_text_input/visor_text_input.dart
  • Quality contract audit row: docs/flutter-widget-quality-contract.md (Rec8)