Fix animation transition lifecycle and target correctness
- Dominant language
- Dart
- Stars
- 800
- Forks
- 49
- Avg merge
- 3h 39m
- Merged PRs (30d)
- 22
Description
## Summary
Repair curve, spring, phase, and keyframe driver transitions so interrupted
animations hold the visible value, removing a configuration reaches the new
target, phase delays use the correct transition index, and completion
listeners fire exactly once. Treat this as one lifecycle-contract change —
fixing only one symptom risks leaving another old/new driver combination
inconsistent.
## Why this matters
Several behaviors can render something other than the requested target:
1. `CurveAnimationDriver` builds its delay with `ConstantTween(_initialSpec)`.
When an in-progress transition is interrupted, the next delay snaps back to
the driver's first target instead of holding the currently visible
interpolated spec.
2. `StyleAnimationBuilder` replaces a driver with `config ?? oldConfig` on type
change. That fallback is valid for implicit curve/spring drivers (they
implement `didUpdateSpec`) but phase/keyframe drivers do not retarget there.
3. Removing a looping phase/keyframe config can therefore create a new driver
from the old config and keep the old sequence running instead of displaying
the new unanimated target.
4. Phase tween construction checks `configs[currentIndex].delay` but weights the
inserted delay with `configs[nextIndex].delay`, so placement and duration
disagree.
5. `_setUpAnimation` adds anonymous phase status listeners on every update and
never removes them, so completion callbacks accumulate.
6. `PhaseAnimationConfig.onEnd` exists but the driver reads the last curve
config's callback instead.
## Source anchors
- `packages/mix/lib/src/animation/style_animation_builder.dart`
- `packages/mix/lib/src/animation/style_animation_driver.dart`
- `packages/mix/lib/src/animation/animation_config.dart`
## Required behavior
- Interrupted implicit animations begin from the currently displayed
`StyleSpec`; a configured delay holds that value for its full duration with no
backward jump.
- Reuse an outgoing config for a null target only when the driver supports
retargeting (curve, spring). Removing phase/keyframe config must stop/dispose
the old driver and expose the supplied target on the same update.
- The full old→new driver matrix (null, curve, spring, phase, keyframe) must
dispose old listeners/tickers and reach the correct target.
- Each phase transition uses one consistent destination index for its delay,
duration, curve, and callback.
- Choose one phase completion contract and fire it exactly once; do not retain
two competing `onEnd` paths.
- Fail early with actionable errors for empty phase lists, unequal style/
curve-config counts, negative durations/delays, looping timelines with no
positive duration, and duplicate keyframe track IDs.
## Acceptance criteria
- [ ] Interrupted delayed transitions hold the visible value without snapping.
- [ ] Removing phase/keyframe animation reaches the new target immediately and stops old tickers.
- [ ] Curve/spring outgoing transitions retain their animate-out behavior.
- [ ] Phase delay placement matches configured durations and delays exactly.
- [ ] Completion and trigger listeners never accumulate across config updates.
- [ ] Invalid phase/keyframe configurations fail before controller execution.
- [ ] The full driver-transition matrix is represented by tests.
- [ ] New regression tests fail on `main` and pass with the fix.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with packages/mix/lib/src/animation/style_animation_builder.dart, style_animation_driver.dart, and animation_config.dart, tracing setup and replacement across the null, curve, spring, phase, and keyframe cases. Add regression coverage for interrupted transitions, removal, delays, listeners, invalid configurations, and the full driver matrix. Done means the acceptance criteria hold, old tickers and listeners are disposed, and each completion callback fires once.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart, flutter
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100