@@ -125,7 +125,7 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
125125 _horizontalDragOffset += details.delta.dx;
126126 _updatePreviewSong ();
127127 });
128- if (_swipeProgress >= 1.0 && ! _hasTriggeredHaptic) {
128+ if (_previewSong != null && _swipeProgress >= 1.0 && ! _hasTriggeredHaptic) {
129129 _hasTriggeredHaptic = true ;
130130 HapticFeedback .lightImpact ();
131131 }
@@ -138,16 +138,22 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
138138 final velocity = details.primaryVelocity ?? 0 ;
139139 final provider = context.read <PlayerProvider >();
140140
141- final shouldSkipNext = (_horizontalDragOffset < - _swipeThreshold ||
141+ final shouldSkipNext =
142+ (_horizontalDragOffset < - _swipeThreshold ||
142143 velocity < - _swipeVelocityThreshold) &&
143144 provider.hasNext;
144- final shouldSkipPrevious = (_horizontalDragOffset > _swipeThreshold ||
145+ final shouldSkipPrevious =
146+ (_horizontalDragOffset > _swipeThreshold ||
145147 velocity > _swipeVelocityThreshold) &&
146148 provider.hasPrevious;
147149
148150 if (shouldSkipNext || shouldSkipPrevious) {
151+ final targetIndex = shouldSkipNext
152+ ? provider.currentIndex + 1
153+ : provider.currentIndex - 1 ;
149154 _animateSwipeCompletion (
150155 goNext: shouldSkipNext,
156+ targetIndex: targetIndex,
151157 provider: provider,
152158 flingVelocity: velocity.abs (),
153159 );
@@ -158,6 +164,7 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
158164
159165 void _animateSwipeCompletion ({
160166 required bool goNext,
167+ required int targetIndex,
161168 required PlayerProvider provider,
162169 required double flingVelocity,
163170 }) {
@@ -174,13 +181,9 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
174181
175182 _swipeAnimationController.duration = Duration (milliseconds: durationMs);
176183 _swipeAnimationController.reset ();
177- final animation = Tween <double >(
178- begin: startOffset,
179- end: endOffset,
180- ).animate (CurvedAnimation (
181- parent: _swipeAnimationController,
182- curve: Curves .easeOut,
183- ));
184+ final animation = Tween <double >(begin: startOffset, end: endOffset).animate (
185+ CurvedAnimation (parent: _swipeAnimationController, curve: Curves .easeOut),
186+ );
184187
185188 void listener () {
186189 if (! mounted) return ;
@@ -199,11 +202,7 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
199202 _previewSong = null ;
200203 _isSwipeAnimating = false ;
201204 });
202- if (goNext) {
203- provider.skipNext ();
204- } else {
205- provider.skipPrevious ();
206- }
205+ provider.skipToIndex (targetIndex);
207206 });
208207 }
209208
@@ -217,13 +216,12 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
217216
218217 _swipeAnimationController.duration = Duration (milliseconds: durationMs);
219218 _swipeAnimationController.reset ();
220- final animation = Tween <double >(
221- begin: startOffset,
222- end: 0.0 ,
223- ).animate (CurvedAnimation (
224- parent: _swipeAnimationController,
225- curve: Curves .easeOutQuad,
226- ));
219+ final animation = Tween <double >(begin: startOffset, end: 0.0 ).animate (
220+ CurvedAnimation (
221+ parent: _swipeAnimationController,
222+ curve: Curves .easeOutQuad,
223+ ),
224+ );
227225
228226 void listener () {
229227 if (! mounted) return ;
@@ -304,11 +302,7 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
304302 duration: animDuration,
305303 curve: animCurve,
306304 transform: Matrix4 .identity ()
307- ..setTranslationRaw (
308- 0.0 ,
309- - _morphProgress * 10 ,
310- 0.0 ,
311- )
305+ ..setTranslationRaw (0.0 , - _morphProgress * 10 , 0.0 )
312306 ..scaleByDouble (
313307 1.0 + _morphProgress * 0.03 ,
314308 1.0 + _morphProgress * 0.03 ,
@@ -320,6 +314,7 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
320314 currentImageUrl: _cachedImageUrl ?? '' ,
321315 currentThumbnailUrl: _cachedThumbnailUrl,
322316 previewImageUrl: _getPreviewArtworkUrl (_previewSong),
317+ hasPreviewSong: _previewSong != null ,
323318 size: artworkSize,
324319 swipeProgress: _swipeProgress,
325320 horizontalDragOffset: _horizontalDragOffset,
@@ -607,9 +602,12 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
607602 _getPreviewArtworkUrl (
608603 _previewSong,
609604 ),
605+ hasPreviewSong:
606+ _previewSong != null ,
610607 size: artworkSize,
611608 swipeProgress: _swipeProgress,
612- horizontalDragOffset: _horizontalDragOffset,
609+ horizontalDragOffset:
610+ _horizontalDragOffset,
613611 ),
614612 ),
615613
@@ -680,7 +678,8 @@ class _NowPlayingScreenState extends State<NowPlayingScreen>
680678 }
681679
682680 Widget _buildRadioPlayer (BuildContext context, RadioStation station) {
683- final animDuration = (_isDragging || _isHorizontalDragging || _isSwipeAnimating)
681+ final animDuration =
682+ (_isDragging || _isHorizontalDragging || _isSwipeAnimating)
684683 ? Duration .zero
685684 : const Duration (milliseconds: 300 );
686685 final animCurve = Curves .easeOutCubic;
@@ -1371,6 +1370,7 @@ class _SwipeableAlbumArtwork extends StatelessWidget {
13711370 final String currentImageUrl;
13721371 final String ? currentThumbnailUrl;
13731372 final String ? previewImageUrl;
1373+ final bool hasPreviewSong;
13741374 final double size;
13751375 final double swipeProgress;
13761376 final double horizontalDragOffset;
@@ -1379,16 +1379,15 @@ class _SwipeableAlbumArtwork extends StatelessWidget {
13791379 required this .currentImageUrl,
13801380 this .currentThumbnailUrl,
13811381 this .previewImageUrl,
1382+ this .hasPreviewSong = false ,
13821383 required this .size,
13831384 required this .swipeProgress,
13841385 required this .horizontalDragOffset,
13851386 });
13861387
13871388 @override
13881389 Widget build (BuildContext context) {
1389- final hasPreview = previewImageUrl != null &&
1390- previewImageUrl! .isNotEmpty &&
1391- horizontalDragOffset != 0 ;
1390+ final hasPreview = hasPreviewSong && horizontalDragOffset != 0 ;
13921391
13931392 final isSwipingRight = horizontalDragOffset > 0 ;
13941393 final previewStart = isSwipingRight
@@ -1406,7 +1405,10 @@ class _SwipeableAlbumArtwork extends StatelessWidget {
14061405
14071406 // Fade based on distance from center
14081407 final totalDistance = size + _kCarouselGap;
1409- final progress = (horizontalDragOffset.abs () / totalDistance).clamp (0.0 , 1.0 );
1408+ final progress = (horizontalDragOffset.abs () / totalDistance).clamp (
1409+ 0.0 ,
1410+ 1.0 ,
1411+ );
14101412 final currentOpacity = (1.0 - progress * 0.5 ).clamp (0.5 , 1.0 );
14111413 final previewOpacity = (progress * 0.5 + 0.5 ).clamp (0.5 , 1.0 );
14121414
@@ -1437,6 +1439,8 @@ class _SwipeableAlbumArtwork extends StatelessWidget {
14371439 }
14381440
14391441 Widget _buildPreviewArtwork (BuildContext context) {
1442+ final hasArtwork = previewImageUrl != null && previewImageUrl! .isNotEmpty;
1443+
14401444 return Container (
14411445 width: size,
14421446 height: size,
@@ -1452,7 +1456,9 @@ class _SwipeableAlbumArtwork extends StatelessWidget {
14521456 ),
14531457 child: ClipRRect (
14541458 borderRadius: BorderRadius .circular (12 ),
1455- child: isLocalFilePath (previewImageUrl)
1459+ child: ! hasArtwork
1460+ ? _buildNoArtPlaceholder (context)
1461+ : isLocalFilePath (previewImageUrl)
14561462 ? Image .file (
14571463 File (previewImageUrl! ),
14581464 key: ValueKey (previewImageUrl),
@@ -1470,8 +1476,8 @@ class _SwipeableAlbumArtwork extends StatelessWidget {
14701476 useOldImageOnUrlChange: true ,
14711477 fadeInDuration: Duration .zero,
14721478 fadeOutDuration: Duration .zero,
1473- placeholder: (_, __ ) => _buildPlaceholder (),
1474- errorWidget: (_, __, ___ ) => _buildNoArtPlaceholder (context),
1479+ placeholder: (_, _ ) => _buildPlaceholder (),
1480+ errorWidget: (_, _, _ ) => _buildNoArtPlaceholder (context),
14751481 ),
14761482 ),
14771483 );
0 commit comments