Skip to content

State Management & Interactive Styles

View raw .md
On this page

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#

Preview core-concepts/state_management_overview The live demo is not available for this example. state_management_overview.dart View source

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:

dart
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.

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., onTap is 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.

dart
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.

dart
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.

dart
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.

dart
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:

dart
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.

esc

Search index did not load.

No match for "".

Try a class like rounded-lg or a widget like WButton.