From 5aad89288203eb995e3d694b01a5ddbfe011f519 Mon Sep 17 00:00:00 2001 From: zarzet <42882290+zarzet@users.noreply.github.com> Date: Sat, 26 Sep 2026 04:10:00 +0700 Subject: [PATCH] fix(lyrics): pulse instrumental dots in unison --- lib/widgets/lyric_gap_indicator.dart | 10 +++------- test/now_playing_lyrics_transition_test.dart | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/widgets/lyric_gap_indicator.dart b/lib/widgets/lyric_gap_indicator.dart index 12ad2d8a..8af8d055 100644 --- a/lib/widgets/lyric_gap_indicator.dart +++ b/lib/widgets/lyric_gap_indicator.dart @@ -71,13 +71,9 @@ class _LyricGapIndicatorState extends ConsumerState AnimatedBuilder( animation: _pulse, builder: (context, child) { - // Stagger the breath without changing the row's layout or - // letting the pulse advance the playback-driven fill. - final phase = ((_pulse.value - index * 0.12) / 0.76).clamp( - 0.0, - 1.0, - ); - final breath = math.sin(math.pi * phase); + // All three dots breathe together; only their fill advances + // independently with the playback position. + final breath = math.sin(math.pi * _pulse.value); return Transform.scale( scale: reduceMotion || widget.position != null ? 1 diff --git a/test/now_playing_lyrics_transition_test.dart b/test/now_playing_lyrics_transition_test.dart index 511af6b5..527301c2 100644 --- a/test/now_playing_lyrics_transition_test.dart +++ b/test/now_playing_lyrics_transition_test.dart @@ -1274,8 +1274,17 @@ void main() { } final firstDot = find.byKey(const ValueKey('lyric-gap-dot-0')); - double dotWidth() => - (tester.getBottomRight(firstDot) - tester.getTopLeft(firstDot)).dx; + double dotWidth([int index = 0]) { + final dot = find.byKey(ValueKey('lyric-gap-dot-$index')); + return (tester.getBottomRight(dot) - tester.getTopLeft(dot)).dx; + } + + void expectSynchronizedDots() { + for (var index = 1; index < 3; index++) { + expect(dotWidth(index), closeTo(dotWidth(), 0.001)); + } + } + final restingWidth = dotWidth(); final restingCenter = tester.getCenter(firstDot); final rowSize = tester.getSize(find.byType(LyricGapIndicator)); @@ -1284,11 +1293,14 @@ void main() { final filling = dotAlphas(); final growingWidth = dotWidth(); expect(growingWidth, greaterThan(restingWidth)); + expectSynchronizedDots(); await tester.pump(const Duration(milliseconds: 350)); final peakWidth = dotWidth(); expect(peakWidth, greaterThan(growingWidth)); + expectSynchronizedDots(); await tester.pump(const Duration(milliseconds: 750)); expect(dotWidth(), lessThan(peakWidth)); + expectSynchronizedDots(); expect(tester.getCenter(firstDot), restingCenter); expect(tester.getSize(find.byType(LyricGapIndicator)), rowSize); expect(dotAlphas(), filling);