Skip to content

Commit

Permalink
fix: Added missing widget state utilities (#411)
Browse files Browse the repository at this point in the history
  • Loading branch information
leoafarias authored Jul 30, 2024
1 parent a1aa8e8 commit de48c85
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 3 deletions.
3 changes: 1 addition & 2 deletions packages/mix/lib/src/attributes/spacing/spacing_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ final class SpacingDirectionalUtility<T extends Attribute>
}

@immutable
final class SpacingSideUtility<T extends Attribute>
extends MixUtility<T, double> {
class SpacingSideUtility<T extends Attribute> extends MixUtility<T, double> {
const SpacingSideUtility(super.builder);

T call(double value) => builder(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,68 @@ enum MixWidgetState {
static const hasStateOf = MixWidgetStateModel.hasStateOf;
}

/// A controller that manages the state of a widget.
///
/// [MixWidgetStateController] tracks various states of a widget, such as
/// [disabled], [hovered], [focused], [pressed], [dragged], [selected], and
/// [longPressed]. These states are stored in a [Set] called [value].
///
/// The controller extends [ChangeNotifier], allowing listeners to be notified
/// when the state of the widget changes.
class MixWidgetStateController extends ChangeNotifier {
/// The current set of states for the widget.
///
/// This is annotated with `@visibleForTesting` to indicate that it is
/// accessible for testing purposes.
@visibleForTesting
Set<MixWidgetState> value = {};

/// Whether the widget is currently in the disabled state.
bool get disabled => value.contains(MixWidgetState.disabled);

/// Whether the widget is currently being hovered over.
bool get hovered => value.contains(MixWidgetState.hovered);

/// Whether the widget currently has focus.
bool get focused => value.contains(MixWidgetState.focused);

/// Whether the widget is currently being pressed.
bool get pressed => value.contains(MixWidgetState.pressed);

/// Whether the widget is currently being dragged.
bool get dragged => value.contains(MixWidgetState.dragged);

/// Whether the widget is currently in the selected state.
bool get selected => value.contains(MixWidgetState.selected);

/// Whether the widget is currently being long-pressed.
bool get longPressed => value.contains(MixWidgetState.longPressed);

/// Sets whether the widget is in the disabled state.
set disabled(bool value) => update(MixWidgetState.disabled, value);

/// Sets whether the widget is being hovered over.
set hovered(bool value) => update(MixWidgetState.hovered, value);

/// Sets whether the widget has focus.
set focused(bool value) => update(MixWidgetState.focused, value);

/// Sets whether the widget is being pressed.
set pressed(bool value) => update(MixWidgetState.pressed, value);

/// Sets whether the widget is being dragged.
set dragged(bool value) => update(MixWidgetState.dragged, value);

/// Sets whether the widget is in the selected state.
set selected(bool value) => update(MixWidgetState.selected, value);

/// Sets whether the widget is being long-pressed.
set longPressed(bool value) => update(MixWidgetState.longPressed, value);

/// Updates the state of the widget for a given [key].
///
/// If [add] is true, the [key] state is added to [value]. If false, it is
/// removed. Listeners are notified if the state has changed.
// ignore: prefer-named-boolean-parameters
void update(MixWidgetState key, bool add) {
final valueHasChanged = add ? value.add(key) : value.remove(key);
Expand All @@ -45,6 +86,11 @@ class MixWidgetStateController extends ChangeNotifier {
}
}

/// Batch updates the state of the widget with multiple state changes.
///
/// [updates] is a list of tuples, where each tuple contains a state [key]
/// and a boolean [add] indicating whether to add or remove the state.
/// Listeners are notified if any state has changed.
void batch(List<(MixWidgetState, bool)> updates) {
var valueHasChanged = false;
for (final update in updates) {
Expand All @@ -62,5 +108,6 @@ class MixWidgetStateController extends ChangeNotifier {
}
}

/// Checks if the widget is currently in the given state [key].
bool has(MixWidgetState key) => value.contains(key);
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ class OnContextVariantUtility {
late final enabled = const OnNotVariant(OnDisabledVariant());
late final disabled = const OnDisabledVariant();
late final longPress = const OnLongPressVariant();
late final selected = const OnSelectedVariant();
late final unselected = const OnNotVariant(OnSelectedVariant());
late final dragged = const OnDraggedVariant();

/// Creates an [OnNotVariant] with the specified [variant].
///
Expand Down
11 changes: 10 additions & 1 deletion packages/mix/lib/src/widgets/pressable_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class Pressable extends StatefulWidget {
this.semanticButtonLabel,
this.onKeyEvent,
this.unpressDelay = kDefaultAnimationDuration,
this.controller,
this.actions,
});

Expand Down Expand Up @@ -143,6 +144,8 @@ class Pressable extends StatefulWidget {
/// Actions to be bound to the widget
final Map<Type, Action<Intent>>? actions;

final MixWidgetStateController? controller;

/// The duration to wait after the press is released before the state of pressed is removed
final Duration unpressDelay;

Expand All @@ -152,7 +155,13 @@ class Pressable extends StatefulWidget {

@visibleForTesting
class PressableWidgetState extends State<Pressable> {
final _controller = MixWidgetStateController();
late final MixWidgetStateController _controller;

@override
void initState() {
super.initState();
_controller = widget.controller ?? MixWidgetStateController();
}

@override
void dispose() {
Expand Down

0 comments on commit de48c85

Please sign in to comment.