fix(mix): discover widget-state dependencies through nested variants - #1006
Draft
leoafarias wants to merge 3 commits into
Draft
fix(mix): discover widget-state dependencies through nested variants#1006leoafarias wants to merge 3 commits into
leoafarias wants to merge 3 commits into
Conversation
Style.widgetStates only scanned top-level WidgetStateVariants, so a style whose only widget-state variants were nested under another variant (for example onDark or onBreakpoint) or wrapped in NotVariant reported no dependencies. StyleBuilder then attached no MixInteractionDetector and those variants never activated. Collect dependencies recursively with identity-based cycle protection, and introduce ContextVariant.widgetStateDependencies so any context variant can declare the widget states it needs tracked. NotVariant delegates to its inner variant. Refs #967 (Style.widgetStates is one of its anchors; this addresses the under-tracking side, not the over-subscription concerns tracked there).
Discovering widget-state dependencies through nested variants made StyleBuilder mount MixInteractionDetector for states it cannot produce. The detector only derives hovered/pressed from pointer input; disabled, focused and the rest must come from an external controller or an ancestor scope, both of which already bypass it. Mounting it anyway had two visible costs: its opaque Listener swallowed pointer events aimed at widgets beneath it, and the state scope it opened was reused by descendants, so a nested box hovered on its ancestor's bounds instead of its own. Also: - declare the producible set on MixInteractionDetector so the fact lives with the code that implements it - drop the dart:collection import; Set.identity() is in dart:core - record why the visited set exists: cycles, and onBuilder storing the receiver as its placeholder, which is O(2^n) without dedup - document widgetStateDependencies as the override point for custom context variants, and its static-discovery limits - cover nested pressed, onEnabled discovery, hover exit, and both edges of the detector-mounting boundary
…te-discovery-v1 # Conflicts: # packages/mix/CHANGELOG.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a silent activation bug in widget-state tracking, extracted from the Tailwind parity work (where
dark:hover:combinations exposed it) but fully general.The bug
Style.widgetStatesonly scanned top-levelWidgetStateVariants.StyleBuilderuses that set to decide whether to attach aMixInteractionDetector, so a style whose only widget-state variants were:onDark(BoxStyler().onHovered(...)),onBreakpoint(...), or the reverse nesting, oronNot(ContextVariant.widgetState(.hovered), ...)reported no state dependencies, got no interaction detector, and those variants never activated. No error, no warning — the hover/press/focus style simply did nothing.
The fix
ContextVariant.widgetStateDependencies(defaults to empty) so any context variant can declare the widget states it needs tracked:WidgetStateVariantreports its state,NotVariantdelegates to its inner variant. This also gives future variants (e.g. a focus-visible variant, coming in the stacked follow-up PR) a first-class way to participate in tracking.Issue-tracker context
No existing issue reports this. #967 lists
Style.widgetStatesamong its anchors but describes the opposite family of defects (over-subscription and stale inherited styles); this PR addresses the under-tracking side and leaves #967's concerns untouched.Test plan
packages/mixsuite on this branch standalone: 2,839 tests pass,dart analyzeclean.Stacked follow-up: #1005's replacement feature PR (Pressable input/semantics rework + focus-visible) is based on this branch.