WInput
A utility-first form input that combines React-style controlled state management with Tailwind-like styling. It renders Material-free (EditableText core inside a Container) and works under any theming ancestor: Material, Cupertino, a custom app, or a bare WidgetsApp. No Material ancestor required (fixes #102).
- Basic Usage
- Constructor
- Props
- Layout Modes
- Event Handling
- State Variants
- Accessibility
- Styling Examples
- All Supported Classes
- Customizing Theme
- Related Documentation
WInput(
value: _email,
onChanged: (value) => setState(() => _email = value),
type: InputType.email,
placeholder: 'Enter your email',
className: 'p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500',
placeholderClassName: 'text-gray-400 italic',
)
Basic Usage
Pass value and onChanged for controlled (React-style) binding. Omit both to let the field manage its own state internally. WInput works under any ancestor widget tree. No MaterialApp required.
WInput(
placeholder: 'Type something...',
className: 'w-full p-2 border rounded',
onChanged: (val) => print(val),
)
Constructor
const WInput({
Key? key,
String? value,
ValueChanged? onChanged,
InputType type = InputType.text,
String? className,
String? placeholderClassName,
String? placeholder,
bool enabled = true,
bool readOnly = false,
bool autofocus = false,
TextInputAction? textInputAction,
ValueChanged? onSubmitted,
VoidCallback? onEditingComplete,
VoidCallback? onTap,
TapRegionCallback? onTapOutside,
int? maxLines,
int minLines = 1,
FocusNode? focusNode,
TextEditingController? controller,
Set? states,
List? inputFormatters,
TextCapitalization textCapitalization = TextCapitalization.none,
bool autocorrect = true,
bool enableSuggestions = true,
Widget? prefix,
Widget? suffix,
String? semanticLabel,
})
Props
| Prop | Type | Default | Description |
|---|---|---|---|
className |
String? |
null |
Wind utility classes for the input field |
placeholderClassName |
String? |
null |
Utility classes for styling the hint text |
value |
String? |
null |
The controlled value of the input |
onChanged |
ValueChanged |
null |
Callback when text changes |
type |
InputType |
InputType.text |
Input keyboard and behavior (text, password, email, number, multiline). number restricts input to a signed decimal on every platform (web included); pass inputFormatters to override. |
placeholder |
String? |
null |
Hint text shown when input is empty |
enabled |
bool |
true |
Whether the input is interactive |
readOnly |
bool |
false |
Whether the input is read-only |
autofocus |
bool |
false |
Autofocus on mount |
textInputAction |
TextInputAction? |
null |
Keyboard action (e.g., .done, .next) |
onSubmitted |
ValueChanged |
null |
Callback when action button is pressed |
onTapOutside |
TapRegionCallback? |
null |
Callback when tapping outside (useful for blur) |
maxLines |
int? |
null |
Max lines for multiline input |
minLines |
int |
1 |
Min lines for multiline input |
prefix |
Widget? |
null |
Widget displayed before text |
suffix |
Widget? |
null |
Widget displayed after text |
controller |
TextEditingController? |
null |
Optional external controller |
states |
Set |
null |
Custom states for dynamic styling |
onEditingComplete |
VoidCallback? |
null |
Callback fired when the user finishes editing (loses focus, presses done, etc.) |
onTap |
VoidCallback? |
null |
Callback fired when the field is tapped |
focusNode |
FocusNode? |
null |
External focus controller for programmatic focus management |
inputFormatters |
List |
null |
Formatters applied as the user types (digit-only, masks, etc.) |
textCapitalization |
TextCapitalization |
TextCapitalization.none |
Auto-capitalize behavior (none / sentences / words / characters) |
autocorrect |
bool |
true |
Enable OS autocorrect suggestions |
enableSuggestions |
bool |
true |
Enable OS suggestion bar (Android) |
semanticLabel |
String? |
null |
Accessible name for the single textbox semantics node. Falls back to placeholder when null. Used by screen readers and getByLabel-style locators. |
Layout Modes
Multiline Input
For text areas, set the type to InputType.multiline. You can control the height using minLines or standard sizing classes.
WInput(
type: InputType.multiline,
minLines: 3,
maxLines: 5,
className: 'w-full p-4 border rounded-xl bg-gray-50',
placeholder: 'Enter your message...',
)
Event Handling
WInput provides standard callbacks for user interaction. Use onSubmitted for handling "Enter" or keyboard action buttons.
WInput(
className: 'border p-2',
textInputAction: TextInputAction.search,
onSubmitted: (value) {
_performSearch(value);
},
onTapOutside: (_) => FocusScope.of(context).unfocus(),
)
State Variants
Wind automatically manages focus:, disabled: (when enabled: false), and readonly: (when readOnly: true) states. You can also trigger custom states like error: by passing them to the states property.
WInput(
states: _hasError ? {'error'} : {},
className: 'border border-gray-300 focus:border-blue-500 error:border-red-500',
)
[!NOTE]
focus:styles (likering-2) are applied automatically when the field gains focus.
Accessibility
WInput emits exactly one typeable textbox semantics node. Its accessible name is semanticLabel when provided, falling back to placeholder. This single-node shape ensures label-by-text locators (e.g., Playwright getByLabel, dusk label lookup) resolve cleanly with no duplicate accessible names.
InputType.password fields report isObscured: true in the semantics tree so screen readers announce the field as a password input.
When the field is enabled: false, it is non-editable and reports isReadOnly in the semantics tree.
[!NOTE]
WInputsupports native text selection (drag-select, double-tap word, long-press) with Cupertino-style selection handles on all platforms. This keepsWInputcupertino-only (nopackage:flutter/material.dartimport) and consistent with the rest of Wind's own look. Interactive selection requires anOverlayancestor (MaterialApp/CupertinoApp/WidgetsAppall provide one). Under a bare root with noOverlay(unusual in practice), typing and focus still work, but all interactive selection (drag-select, double-tap word, long-press, the drag handles, and the toolbar) is suppressed rather than throwing.
[!NOTE] Web: Flutter renders to a canvas (CanvasKit or Skwasm) and does not use a native browser
. Mouse drag-select, double-click word, and keyboard copy (Ctrl+C / Cmd+C) all work through Flutter's own selection layer. Right-click opens the browser's native context menu by default (not WInput's selection toolbar). Apps that want WInput's selection toolbar on right-click can callBrowserContextMenu.disableContextMenu()once at startup; this is an app-level opt-in and not WInput behavior. No magnifier is shown on web.
[!WARNING] Passing both
valueandcontrollerat the same time throws anAssertionErrorin debug mode. Use eithervalue+onChanged(controlled) orcontroller(external), not both.
Styling Examples
Search Input with Prefix
You can use the prefix prop to add icons while maintaining the utility-first styling of the input.
WInput(
prefix: Icon(Icons.search, color: Colors.gray),
className: 'pl-10 p-2 bg-gray-100 rounded-full border-transparent focus:bg-white focus:border-blue-500',
placeholder: 'Search...',
)
Password Field
Use InputType.password to obscure text. You can use the suffix prop to add a visibility toggle.
WInput(
type: _obscure ? InputType.password : InputType.text,
suffix: IconButton(
icon: Icon(_obscure ? Icons.visibility : Icons.visibility_off),
onPressed: () => setState(() => _obscure = !_obscure),
),
className: 'p-3 border rounded',
)
All Supported Classes
| Category | Classes |
|---|---|
| Layout | w-{size}, h-{size}, flex-1, w-full |
| Spacing | p-{n}, px-{n}, py-{n}, m-{n} |
| Typography | text-{size}, font-{weight}, italic, uppercase |
| Colors | bg-{color}, text-{color} |
| Borders | border, border-{n}, rounded-{size}, border-{color} |
| Effects | shadow-{size}, opacity-{n}, ring-{n}, ring-offset-{n} |
Customizing Theme
You can override the default input appearance through WindThemeData. This is useful for setting global border colors or padding defaults.
WindThemeData(
colors: {
'input-border': Colors.blueGrey,
},
// WInput respects global spacing units for p-x classes
baseSpacingUnit: 4.0,
)
Related Documentation
- WFormInput - Form-integrated input with labels and validation.
- WButton - Interactive button component.
- WText - Typography component.