State Management & Interactive Styles
- Introduction
- How It Works
- Built-in State Prefixes
- Quick Reference
- Interactive Widgets
- Custom States
- State Propagation
- Related Documentation
Introduction
Interactive styles allow you to define how your UI reacts to user input without managing complex state variables. Instead of nesting multiple MouseRegion, GestureDetector, and Focus widgets, Wind provides a declarative way to handle states directly in your className string.
By using prefixes like hover:, focus:, and active:, you can build responsive, tactile interfaces that feel native to both web and mobile platforms.
How It Works
Wind's parsing engine identifies state prefixes in your class string and stores them in a separate style map. When the widget's internal state changes (e.g., a user hovers over it), the widget rebuilds and applies the corresponding styles.
Consider this basic button example:
WButton(
onTap: () => print('Action triggered'),
className: 'bg-blue-500 hover:bg-blue-600 active:bg-blue-700 p-4 rounded-lg duration-200',
child: WText('Save Changes', className: 'text-white'),
)
In this case, the background color transitions smoothly between shades based on the interaction state.
[!NOTE] For state prefixes to work, the widget must be wrapped in an interactive component like
WAnchor,WButton, orWInput. A standardWDivis passive and will not trigger interaction states on its own.
Built-in State Prefixes
Wind supports several interactive states out of the box. These are automatically managed when using interactive widgets:
- hover: Triggered when a pointer enters the widget bounds.
- focus: Triggered when the widget receives keyboard or input focus.
- active: Triggered while the widget is being pressed or tapped.
- disabled: Triggered when the widget's interactivity is disabled (e.g.,
onTapis null).
Quick Reference
| Prefix | Trigger | Typical Use Case |
|---|---|---|
hover: |
Pointer entry | Changing background or elevation on desktop. |
focus: |
Keyboard focus | Showing a ring or border on input fields. |
active: |
Pointer press | Scaling down or darkening a button on tap. |
disabled: |
Interactivity off | Reducing opacity or greying out actions. |
Interactive Widgets
Wind provides specific widgets designed to manage and propagate these states.
WAnchor: The Core Wrapper
WAnchor is the low-level engine behind Wind's interactivity. It provides no visual styling itself; its sole purpose is to detect gestures and share that state with its child tree. This is useful for making entire cards or complex layouts interactive.
WAnchor(
onTap: () {},
child: WDiv(
className: 'bg-white hover:bg-slate-50 border p-6 rounded-xl duration-300',
children: [
WText('Interactive Card', className: 'group-hover:text-blue-600'),
],
),
)
WButton: The High-Level Action
While WAnchor is for generic wrappers, WButton is optimized for actions. It includes built-in support for loading states, padding defaults, and button-specific accessibility.
WButton(
onTap: _submit,
className: 'bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50 text-white px-8 py-3 rounded',
child: Text('Submit'),
)
WInput: Focus States
WInput manages its own focus state. It is common to use focus: prefixes here to highlight the active field or show a focus ring.
WInput(
placeholder: 'Search...',
className: 'border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 p-3 rounded-lg',
)
Custom States
Sometimes you need to style a widget based on a state that isn't built-in, such as whether an item is "selected" or "loading". You can pass custom strings to the states parameter.
WDiv(
className: 'border-2 border-gray-200 selected:border-blue-500 bg-white selected:bg-blue-50 p-4',
states: isSelected ? {'selected'} : {},
child: WText('Selection Item'),
)
If the states set contains "selected", any class prefixed with selected: will be applied.
State Propagation
Interactivity in Wind is shared through WindContext. When an ancestor widget like WAnchor changes state, it propagates that state to all children in its subtree.
Let's look at a nested example:
WAnchor(
onTap: () {},
child: WDiv(
className: 'p-4 bg-white hover:bg-gray-50',
children: [
WText('Title', className: 'font-bold hover:text-blue-600'),
WText('Description', className: 'text-gray-500'),
],
),
)
Even if the mouse is only hovering over the padding of the WDiv, the WText widget will receive the "hover" state and update its color accordingly.
Related Documentation
- Responsive Design - Combining states with breakpoints.
- Dark Mode - Styling for night owls.
- WAnchor Widget - Technical reference for the anchor engine.