-
Notifications
You must be signed in to change notification settings - Fork 0
Widget States
layrz_ui re-exports Flutter's interactive state management types from package:flutter/widgets.dart. These are deliberately not wrapped or reimplemented — they are exported verbatim because they are design-system-agnostic and suitable for use in any widget system.
Interactive components (buttons, inputs, chips, and other controls) resolve visual properties (color, opacity, size) based on their current interactive state (hover, press, focus, disabled, etc.). Flutter provides two abstractions for this:
-
WidgetState— an enum representing interactive conditions -
WidgetStateProperty<T>— an interface for values that vary based on widget state
layrz_ui exports these types so widgets can use them consistently.
| Type | Purpose |
|---|---|
WidgetState |
Enum of interactive states (hovered, pressed, focused, disabled, etc.) |
WidgetStateProperty<T> |
Interface for state-aware property values |
WidgetStateMapper<T> |
Function type for mapping states to values |
WidgetStatePropertyAll<T> |
Concrete property — single value for all states |
WidgetStatesController |
Mutable ValueNotifier tracking active states |
WidgetStatesConstraint |
Constraint limiting which states can be active simultaneously |
WidgetStateMap<T> |
Concrete property — map of states to values |
WidgetPropertyResolver<T> |
Function type for state-to-value resolution |
WidgetStateColor |
Convenience property for Color values |
WidgetStateTextStyle |
Convenience property for TextStyle values |
WidgetStateBorderSide |
Convenience property for BorderSide values |
WidgetStateMouseCursor |
Convenience property for MouseCursor values |
WidgetStateOutlinedBorder |
Convenience property for OutlinedBorder values |
Import them directly from layrz_ui:
import 'package:layrz_ui/layrz_ui.dart';
// Available as:
WidgetState.pressed
WidgetStateProperty<Color>
WidgetStateColorAn enum representing the possible interactive states a widget can be in:
| State | Meaning |
|---|---|
hovered |
User is hovering over the widget (desktop platforms) |
focused |
Widget has keyboard focus |
pressed |
User is actively pressing the widget |
dragged |
Widget is being dragged (for draggable widgets) |
error |
Widget is in an error state |
disabled |
Widget is disabled and does not respond to input |
selected |
Widget is selected (for selectable widgets like checkboxes) |
WidgetStateProperty<T> is a functional interface that resolves a value of type T based on a set of active WidgetStates:
abstract class WidgetStateProperty<T> {
T resolve(Set<WidgetState> states);
}A single value for all states:
WidgetStatePropertyAll<Color>(Colors.blue)
// Always returns Colors.blue, regardless of stateA map of states to values:
WidgetStateMap<Color>({
WidgetState.hovered: Colors.lightBlue,
WidgetState.pressed: Colors.darkBlue,
WidgetState.disabled: Colors.grey,
})
// Returns different color based on which states are activeConvenience implementations for common types:
WidgetStateColor({
WidgetState.hovered: Colors.lightBlue,
WidgetState.pressed: Colors.darkBlue,
WidgetState.disabled: Colors.grey,
})A ValueNotifier that tracks the set of active states and notifies listeners when they change:
final controller = WidgetStatesController();
// Activate states
controller.update(WidgetState.hovered, true);
controller.update(WidgetState.pressed, true);
// Check active states
if (controller.value.contains(WidgetState.pressed)) {
// Handle pressed state
}
// Listen for changes
controller.addListener(() {
print('States changed: ${controller.value}');
});A simplified example of how a custom button might use WidgetStateProperty:
class CustomButton extends StatefulWidget {
const CustomButton({
required this.onTap,
required this.label,
this.backgroundColor = const WidgetStatePropertyAll(Colors.blue),
super.key,
});
final VoidCallback onTap;
final String label;
final WidgetStateProperty<Color> backgroundColor;
@override
State<CustomButton> createState() => _CustomButtonState();
}
class _CustomButtonState extends State<CustomButton> {
final _statesController = WidgetStatesController();
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (_) => _statesController.update(WidgetState.hovered, true),
onExit: (_) => _statesController.update(WidgetState.hovered, false),
child: GestureDetector(
onTapDown: (_) => _statesController.update(WidgetState.pressed, true),
onTapUp: (_) => _statesController.update(WidgetState.pressed, false),
onTapCancel: () => _statesController.update(WidgetState.pressed, false),
onTap: widget.onTap,
child: ValueListenableBuilder<Set<WidgetState>>(
valueListenable: _statesController,
builder: (context, states, _) {
final backgroundColor = widget.backgroundColor.resolve(states);
return Container(
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Text(widget.label),
);
},
),
),
);
}
@override
void dispose() {
_statesController.dispose();
super.dispose();
}
}Usage:
CustomButton(
onTap: () => print('Tapped'),
label: 'Click me',
backgroundColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.pressed)) {
return Colors.darkBlue;
}
if (states.contains(WidgetState.hovered)) {
return Colors.lightBlue;
}
return Colors.blue;
}),
)layrz_ui deliberately re-exports these types rather than wrapping them because:
- Design-system agnostic — These types are not specific to any design system; they belong in Flutter core
- No value added by wrapping — Any wrapper would be a thin pass-through; wrapping adds complexity without benefit
- Adoption — Components can adopt the standard Flutter patterns without learning layrz_ui-specific abstractions
See decision D13 for full details.
For complete API reference and more examples, refer to the Flutter documentation:
Last updated: 2026-08-13
Related pages: Getting-Started, LayrzApp
Made with ❤️ by Golden M, Inc.