Skip to content

refactor: Use WidgetState instead of MixWidgetState #582

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

tilucasoli
Copy link
Collaborator

@tilucasoli tilucasoli commented Apr 25, 2025

Description

  • Remove MixWidgetState and use MixWidgetState instead

Changes

  • Create LongPressInheritedState that expose the longPress state instead of depend on MixWidgetState;
  • ReplaceMixWidgetState for WidgetState

Review Checklist

  • Testing: Have you tested your changes, including unit tests and integration tests for affected code?
  • Breaking Changes: Does this change introduce breaking changes affecting existing code or users?
  • Documentation Updates: Are all relevant documentation files (e.g. README, API docs) updated to reflect the changes in this PR?
  • Website Updates: Is the website containing the updates you make on documentation?

Summary by CodeRabbit

  • New Features

    • Introduced a new inherited widget to provide long-press state to widget descendants.
  • Refactor

    • Unified widget state handling by replacing a custom enum with a standard widget state type.
    • Removed the long-press state from the general widget state model and controller, now handled via the new inherited widget.
    • Made constructors of core utility and metadata classes constant to support compile-time instantiation.
  • Bug Fixes

    • Adjusted state variant logic and tests to accurately reflect long-press and other widget states.
  • Tests

    • Updated and simplified tests to align with the new state management approach and improved long-press detection.

Copy link

vercel bot commented Apr 25, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
mix-docs ⬜️ Ignored (Inspect) Visit Preview Apr 28, 2025 1:43pm

Copy link

coderabbitai bot commented Apr 25, 2025

Walkthrough

The changes refactor how the "long pressed" state is handled in the widget state management system. The longPressed state is removed from the core WidgetState and its controller, and is instead managed through a new LongPressInheritedState inherited widget. All references to the old MixWidgetState enum are replaced with the more general WidgetState type. Test files and helper utilities are updated to align with these changes, removing direct handling of the longPressed state and updating relevant assertions and widget setups. The .gitignore is also updated to ignore additional build directories. Additionally, some abstract classes received const constructors to enable compile-time constant instantiation.

Changes

File(s) Change Summary
packages/mix/example/.gitignore Added .build/ and .swiftpm/ to ignored directories.
.../gesture_mix_state.dart Introduced LongPressInheritedState for managing and propagating the long press state. Internal state _longPressed added, with updates via _setLongPressed. Widget tree wrapped with the new inherited widget.
.../mix_widget_state_builder.dart Removed longPressed from widget state model and builder. Generalized state handling from MixWidgetState to WidgetState. Updated method and field signatures accordingly.
.../widget_state_controller.dart Removed MixWidgetState enum and replaced with WidgetState extension. Removed longPressed state from controller. Updated all methods and fields to use WidgetState.
.../widget_state_variant.dart Refactored OnLongPressVariant to use LongPressInheritedState. Changed all internal state references from MixWidgetState to WidgetState. Updated constructors and field types.
.../testing_utils.dart Removed longPressed parameter and assignment from pumpWithPressable method.
.../gesture_mix_state_test.dart Updated tests to use WidgetState.pressed and LongPressInheritedState.of(context).longPressed instead of MixWidgetState.
.../interactive_mix_state_test.dart Replaced all MixWidgetState references with WidgetState in state assertions.
.../widget_state_variant_test.dart Changed long press state test to use LongPressInheritedState directly. Updated test description and removed redundant assertion. Imported gesture_mix_state.dart.
.../widget_state_controller_test.dart Removed all references to longPressed in controller and widget tests. Changed type usage from MixWidgetState to WidgetState. Updated test logic to exclude longPressed.
packages/mix/lib/src/core/element.dart Changed constructor of abstract class DtoUtility to const.
packages/mix/lib/src/core/utility.dart Changed constructor of abstract class SizingUtility to const.
packages/mix_generator/lib/src/core/metadata/base_metadata.dart Changed constructor of abstract class BaseMetadata to const.
packages/mix/lib/src/attributes/scalars/scalar_util.dart Changed constructor of FontSizeUtility to const.
packages/mix_generator/lib/src/core/metadata/property_metadata.dart Changed constructor of MixableTypeMetadata to const.
packages/mix_generator/lib/src/core/metadata/spec_metadata.dart Changed constructor of SpecMetadata to const.
packages/mix_generator/lib/src/core/metadata/tokens_metadata.dart Changed constructor of TokensMetadata to const.
packages/mix_generator/lib/src/core/metadata/utility_metadata.dart Changed constructor of UtilityMetadata to const.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant GestureWidget
  participant _GestureMixStateWidgetState
  participant LongPressInheritedState
  participant DescendantWidget

  User->>GestureWidget: Performs long press gesture
  GestureWidget->>_GestureMixStateWidgetState: onLongPressStart callback
  _GestureMixStateWidgetState->>_GestureMixStateWidgetState: _setLongPressed(true)
  _GestureMixStateWidgetState->>LongPressInheritedState: Rebuild with longPressed=true
  DescendantWidget->>LongPressInheritedState: Access longPressed via of(context)
  LongPressInheritedState-->>DescendantWidget: Returns current longPressed state
  User->>GestureWidget: Releases long press
  GestureWidget->>_GestureMixStateWidgetState: onLongPressEnd callback
  _GestureMixStateWidgetState->>_GestureMixStateWidgetState: _setLongPressed(false)
  _GestureMixStateWidgetState->>LongPressInheritedState: Rebuild with longPressed=false
Loading

Suggested labels

remix, mix_lint, examples, repo

Poem

In the garden of code, the long press grew,
Now wrapped in state, inherited and new.
No more enum clutter, just context in sight,
Widgets can listen, their futures bright.
Tests and controllers, all realigned,
A hop, a skip—clean state, well-defined!
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a513dc0 and bc0a47e.

📒 Files selected for processing (5)
  • packages/mix/lib/src/attributes/scalars/scalar_util.dart (1 hunks)
  • packages/mix_generator/lib/src/core/metadata/property_metadata.dart (1 hunks)
  • packages/mix_generator/lib/src/core/metadata/spec_metadata.dart (1 hunks)
  • packages/mix_generator/lib/src/core/metadata/tokens_metadata.dart (1 hunks)
  • packages/mix_generator/lib/src/core/metadata/utility_metadata.dart (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • packages/mix_generator/lib/src/core/metadata/property_metadata.dart
  • packages/mix_generator/lib/src/core/metadata/tokens_metadata.dart
  • packages/mix/lib/src/attributes/scalars/scalar_util.dart
  • packages/mix_generator/lib/src/core/metadata/utility_metadata.dart
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Test
  • GitHub Check: Test Min SDK
  • GitHub Check: production
🔇 Additional comments (1)
packages/mix_generator/lib/src/core/metadata/spec_metadata.dart (1)

17-17: Adding const constructor improves performance and enables compile-time optimization.

Updating the constructor to be constant allows for compile-time constant instantiation of SpecMetadata objects. This is a good practice for immutable classes, as it enables optimizations like instance canonicalization and can improve overall performance.

This change is consistent with similar updates applied to other metadata classes throughout the codebase (BaseMetadata, MixableTypeMetadata, TokensMetadata, UtilityMetadata) as mentioned in the summary.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added the mix label Apr 25, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/mix/lib/src/core/widget_state/internal/mix_widget_state_builder.dart (3)

49-55: Consider exposing a maybeOf helper for null-safe look-ups

of(context) returns null when the model is not in the tree, but callers have to remember to perform the null-check every time. A small maybeOf (or tryOf) wrapper that simply delegates to inheritFrom without the non-nullable cast would make call-sites cleaner and keep the public API symmetrical with Flutter’s own Theme.of / Theme.maybeOf pattern.


56-71: Replace the long switch with a map to improve maintainability

As new WidgetState values are introduced, this switch can easily fall out of sync and silently return false for unhandled states (as the default branch does today). Building a map once and indexing into it keeps the code DRY and makes missing keys immediately obvious.

-    return switch (state) {
-      WidgetState.disabled => model.disabled,
-      WidgetState.hovered => model.hovered,
-      WidgetState.focused => model.focused,
-      WidgetState.pressed => model.pressed,
-      WidgetState.dragged => model.dragged,
-      WidgetState.selected => model.selected,
-      WidgetState.error => model.error,
-      _ => false,
-    };
+    const stateSelectors = <WidgetState, bool Function(MixWidgetStateModel)>{
+      WidgetState.disabled: (m) => m.disabled,
+      WidgetState.hovered:  (m) => m.hovered,
+      WidgetState.focused:  (m) => m.focused,
+      WidgetState.pressed:  (m) => m.pressed,
+      WidgetState.dragged:  (m) => m.dragged,
+      WidgetState.selected: (m) => m.selected,
+      WidgetState.error:    (m) => m.error,
+    };
+
+    final selector = stateSelectors[state];
+    return selector != null ? selector(model) : false;

96-111: Readability: parenthesise OR-chains or extract to helper

The current boolean expression relies on && having higher precedence than ||. While correct, it is easy to mis-read. Two small tweaks can avoid cognitive overhead:

  1. Add explicit parentheses around each <diff && dependencies.contains> pair, or
  2. Extract the comparison into a private helper returning bool hasChanged(WidgetState) and loop over dependencies.

Either way future maintainers will thank you. 🙂

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c126f9 and 706d34a.

📒 Files selected for processing (10)
  • packages/mix/example/.gitignore (1 hunks)
  • packages/mix/lib/src/core/widget_state/internal/gesture_mix_state.dart (3 hunks)
  • packages/mix/lib/src/core/widget_state/internal/mix_widget_state_builder.dart (3 hunks)
  • packages/mix/lib/src/core/widget_state/widget_state_controller.dart (4 hunks)
  • packages/mix/lib/src/variants/widget_state_variant.dart (3 hunks)
  • packages/mix/test/helpers/testing_utils.dart (0 hunks)
  • packages/mix/test/src/core/mix_state/internal/gesture_mix_state_test.dart (6 hunks)
  • packages/mix/test/src/core/mix_state/internal/interactive_mix_state_test.dart (5 hunks)
  • packages/mix/test/src/variants/widget_state_variant_test.dart (2 hunks)
  • packages/mix/test/src/widgets/widget_state/widget_state_controller_test.dart (7 hunks)
💤 Files with no reviewable changes (1)
  • packages/mix/test/helpers/testing_utils.dart
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Test Min SDK
  • GitHub Check: Test
🔇 Additional comments (30)
packages/mix/example/.gitignore (1)

8-8: Appropriate addition to .gitignore.

Adding .build/ and .swiftpm/ directories to the ignore list ensures Swift Package Manager build artifacts aren't accidentally committed, maintaining a clean repository.

Also applies to: 12-12

packages/mix/test/src/variants/widget_state_variant_test.dart (2)

5-5: Import added for LongPressInheritedState.

Correctly added the import for the new gesture_mix_state.dart to support the refactored long press state implementation.


37-43: Refactored long press state test to use LongPressInheritedState.

The test has been appropriately updated to use the new LongPressInheritedState widget directly rather than relying on the helper method with a longPressed parameter. This aligns with the PR's objective of removing MixWidgetState and managing long press state through a dedicated inherited widget.

packages/mix/test/src/core/mix_state/internal/interactive_mix_state_test.dart (1)

20-20: Updated widget state references.

All references to MixWidgetState.disabled, MixWidgetState.focused, and MixWidgetState.hovered have been consistently replaced with WidgetState.disabled, WidgetState.focused, and WidgetState.hovered. This aligns with the PR objective to use WidgetState instead of MixWidgetState.

Also applies to: 30-30, 43-44, 53-53, 67-67, 75-75

packages/mix/test/src/core/mix_state/internal/gesture_mix_state_test.dart (2)

59-60: Updated pressed state references.

All references to MixWidgetState.pressed have been consistently replaced with WidgetState.pressed, maintaining the same functionality while aligning with the PR objective.

Also applies to: 100-103, 109-112, 118-122, 136-136


81-84: Refactored long press state checking.

The test has been appropriately updated to check the long press state using LongPressInheritedState.of(context).longPressed instead of MixWidgetState.hasStateOf(context, MixWidgetState.longPressed). This aligns with the new approach of managing long press state through a dedicated inherited widget.

packages/mix/test/src/widgets/widget_state/widget_state_controller_test.dart (7)

80-83: LGTM! Clean enum type replacement.

The code correctly updates the batch operation to use WidgetState enum instead of the old MixWidgetState. This is consistent with the PR objective of removing MixWidgetState.


103-105: LGTM! Enum type consistently updated.

The batch operations parameter updates maintain the same functionality while using the new enum type.


167-168: LGTM! Updated widget state testing.

The state verification is correctly updated to use WidgetState.disabled instead of the previous enum type.


174-175: LGTM! Consistent enum usage maintained.

The negative test case is also correctly updated to use WidgetState.hovered enum value.


235-239: LGTM! Dependency notification checks updated.

Both the positive and negative dependency notification tests are correctly updated to use WidgetState enum values.


444-444: LGTM! Method signature updated correctly.

The _widgetStateOf method now accepts WidgetState instead of MixWidgetState, consistent with the overall refactoring.


459-468: LGTM! Widget state builder invocations updated.

All test widget state builders have been updated to use the new WidgetState enum values.

packages/mix/lib/src/core/widget_state/internal/gesture_mix_state.dart (5)

90-90: LGTM! Added local state for long press tracking.

This new field correctly implements the PR objective to move long press state management away from MixWidgetStateController into a local variable.


98-102: LGTM! Well-encapsulated state update method.

This helper method ensures the long press state is consistently updated through setState, triggering proper widget rebuilds.


127-127: LGTM! Consistent local state updates.

All gesture callbacks have been updated to use the new _setLongPressed method instead of modifying the controller directly, ensuring proper state management and widget rebuilds.

Also applies to: 132-132, 137-137, 142-142, 147-147


198-218: LGTM! Appropriate use of InheritedWidget pattern.

The build method now correctly wraps the gesture detector with the new LongPressInheritedState widget, making the long press state available to descendants through the widget tree rather than through the controller.


222-246: LGTM! Well-implemented InheritedWidget.

The LongPressInheritedState implementation follows best practices:

  • Provides both maybeOf and of static methods for accessing the state
  • Proper assertion message when not found in context
  • Correct implementation of updateShouldNotify to only trigger rebuilds when the state changes

This is a clean way to expose the long press state to descendant widgets.

packages/mix/lib/src/variants/widget_state_variant.dart (5)

5-5: LGTM! Import added for new dependency.

The import for the gesture_mix_state.dart file is necessary to access the new LongPressInheritedState class.


29-29: LGTM! Updated enum type in base class.

The _ToggleMixStateVariant class now correctly uses WidgetState instead of MixWidgetState.


59-60: LGTM! Updated enum reference in hover variant.

The OnHoverVariant class now correctly references WidgetState.hovered.


65-66: LGTM! Consistent enum updates in all variant classes.

All variant class constructors have been updated to use WidgetState enum values instead of MixWidgetState.

Also applies to: 83-84, 88-89, 93-94, 98-99, 103-104


69-79: LGTM! Well-refactored long press variant.

The OnLongPressVariant class has been completely refactored to:

  1. Extend ContextVariant directly instead of _ToggleMixStateVariant
  2. Access the long press state through LongPressInheritedState.of(context).longPressed

This implementation properly aligns with the new architecture where long press state is managed by an inherited widget rather than the controller.

packages/mix/lib/src/core/widget_state/widget_state_controller.dart (6)

5-8: LGTM! Clean extension implementation.

Creating an extension on WidgetState is an elegant way to maintain the static helper methods while eliminating the separate enum. This preserves the familiar API interface while simplifying the underlying implementation.


24-24: LGTM! Updated internal state storage type.

The controller's internal state set has been updated to use WidgetState instead of MixWidgetState.


27-67: LGTM! Consistent getter and setter updates.

All the getters and setters for individual widget states have been updated to use WidgetState enum values. Note that the longPressed property has been removed as expected, since it's now managed separately.


73-79: LGTM! Updated method signature.

The update method's parameter type has been changed from MixWidgetState to WidgetState.


86-101: LGTM! Updated batch method signature.

The batch method now accepts a list of tuples with WidgetState instead of MixWidgetState.


104-104: LGTM! Updated has method signature.

The has method now accepts WidgetState instead of MixWidgetState.

packages/mix/lib/src/core/widget_state/internal/mix_widget_state_builder.dart (1)

36-47: InheritedModel generic update looks correct – ✅

Switching the generic from MixWidgetState to WidgetState is exactly what enables dependants to subscribe to the new, unified enum. No functional or type–safety issues spotted here.

@@ -87,13 +87,20 @@ class _GestureMixStateWidgetState extends State<GestureMixStateWidget> {
int _pressCount = 0;
Timer? _timer;
late final MixWidgetStateController _controller;
bool _longPressed = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is done just for backwards compatibility? We also want to deprecate this, so can we also make sure the utilities that use long press have this marked as deprecated?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is solely for backward compatibility. I will include the deprecated annotation for them.

@@ -85,5 +85,5 @@ final class BoolUtility<T extends Attribute> extends ScalarUtility<T, bool> {
/// This class extends [DoubleUtility] and serves as a base for more specific sizing utilities.
abstract base class SizingUtility<T extends Attribute>
extends ScalarUtility<T, double> {
SizingUtility(super.builder);
const SizingUtility(super.builder);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the const being added now? Is this the Dart fix? Because removing this later would be a breaking change, just FYI. But I think we should keep it; it is not a mistake.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants