Skip to Content
Mix 2.0 is in development! You can access the Mix 1.0 docs here.
DocsTutorialsControlling Widget State

Controlling Widget State

In this tutorial, we’ll explore how to manage manually widget states in Mix, including hover, pressed, and focused states. Mix provides multiple approaches to handle widget states, from simple automatic state management to advanced manual control using WidgetStatesController.

Approach 1: Automatic State Management

The simplest way to handle widget states is using Mix’s automatic state management with the Box widget. Mix automatically tracks hover, press, and focus states for you:

class SimpleExample extends StatelessWidget { const SimpleExample({super.key}); BoxStyler get style => BoxStyler() .color(Colors.red) .size(100, 100) .onHovered(BoxStyler().color(Colors.blue)) .onPressed(BoxStyler().color(Colors.green)) .onFocused(BoxStyler().color(Colors.yellow)); @override Widget build(BuildContext context) { return style(); } }

In this example:

  • The base color is red
  • When hovered, the color changes to blue
  • When pressed, the color changes to green
  • When focused, the color changes to yellow

Approach 2: Manual State Control with WidgetStatesController

For more advanced scenarios where you need manual control over widget states, you can use WidgetStatesController with StyleBuilder:

class AdvancedExample extends StatefulWidget { const AdvancedExample({super.key}); @override State<AdvancedExample> createState() => _AdvancedExampleState(); } class _AdvancedExampleState extends State<AdvancedExample> { final controller = WidgetStatesController(); @override void dispose() { controller.dispose(); super.dispose(); } BoxStyler get style => BoxStyler() .color(Colors.red) .size(100, 100) .onHovered(BoxStyler().color(Colors.blue)) .onPressed(BoxStyler().color(Colors.green)) .onFocused(BoxStyler().color(Colors.yellow)); @override Widget build(BuildContext context) { return GestureDetector( onTapDown: (_) => controller.pressed = true, onTapUp: (_) => controller.pressed = false, onTapCancel: () => controller.pressed = false, child: StyleBuilder( style: style, controller: controller, builder: (context, spec) { return Box(styleSpec: StyleSpec(spec: spec)); }, ), ); } }

Creating the WidgetStatesController

First, create an instance of WidgetStatesController in your state class:

final controller = WidgetStatesController();

Remember to dispose of the controller when the widget is disposed to avoid memory leaks:

@override void dispose() { controller.dispose(); super.dispose(); }

Managing Widget States

The WidgetStatesController provides properties to control different widget states:

  • controller.pressed = true/false - Controls the pressed state
  • controller.hovered = true/false - Controls the hover state
  • controller.focused = true/false - Controls the focus state
  • controller.disabled = true/false - Controls the disabled state

Using StyleBuilder

StyleBuilder is the low-level building block for creating styled widgets with Mix. It resolves your style definitions and provides the resolved spec to the builder function:

StyleBuilder( style: style, controller: controller, builder: (context, spec) { return Box(styleSpec: StyleSpec(spec: spec)); }, )

The builder function receives:

  • context - The build context
  • spec - The resolved BoxSpec with all style properties applied based on the current widget states

Connecting Gesture Detection

Use Flutter’s GestureDetector to connect user interactions to the controller:

GestureDetector( onTapDown: (_) => controller.pressed = true, onTapUp: (_) => controller.pressed = false, onTapCancel: () => controller.pressed = false, child: StyleBuilder(...), )

When to Use Each Approach

Use automatic state management (Approach 1) when:

  • You need simple hover, press, and focus interactions
  • You want Mix to handle all the complexity for you
  • You’re building standard interactive widgets

Use manual state control (Approach 2) when:

  • You need custom gesture handling logic
  • You want to programmatically control widget states
  • You need to share state across multiple widgets
  • You’re building complex custom widgets with specific interaction patterns