✨ Full screen player
This commit is contained in:
		@@ -59,6 +59,7 @@ class MyApp extends StatelessWidget {
 | 
				
			|||||||
  void _initializeProviders(BuildContext context) async {
 | 
					  void _initializeProviders(BuildContext context) async {
 | 
				
			||||||
    Get.lazyPut(() => SpotifyProvider());
 | 
					    Get.lazyPut(() => SpotifyProvider());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Get.put(AudioPlayerProvider());
 | 
				
			||||||
    Get.put(ActiveSourcedTrackProvider());
 | 
					    Get.put(ActiveSourcedTrackProvider());
 | 
				
			||||||
    Get.put(AudioPlayerStreamProvider());
 | 
					    Get.put(AudioPlayerStreamProvider());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -69,7 +70,6 @@ class MyApp extends StatelessWidget {
 | 
				
			|||||||
    Get.put(ScrobblerProvider());
 | 
					    Get.put(ScrobblerProvider());
 | 
				
			||||||
    Get.put(UserPreferencesProvider());
 | 
					    Get.put(UserPreferencesProvider());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Get.put(AudioPlayerProvider());
 | 
					 | 
				
			||||||
    Get.put(QueryingTrackInfoProvider());
 | 
					    Get.put(QueryingTrackInfoProvider());
 | 
				
			||||||
    Get.put(SourcedTrackProvider());
 | 
					    Get.put(SourcedTrackProvider());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -128,11 +128,11 @@ class AudioPlayerProvider extends GetxController {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Giving the initial track a boost so MediaKit won't skip
 | 
					    // Giving the initial track a boost so MediaKit won't skip
 | 
				
			||||||
    // because of timeout
 | 
					    // because of timeout
 | 
				
			||||||
    final intendedActiveTrack = medias.elementAt(initialIndex);
 | 
					    // final intendedActiveTrack = medias.elementAt(initialIndex);
 | 
				
			||||||
    if (intendedActiveTrack.track is! LocalTrack) {
 | 
					    // if (intendedActiveTrack.track is! LocalTrack) {
 | 
				
			||||||
      await Get.find<SourcedTrackProvider>()
 | 
					    //   await Get.find<SourcedTrackProvider>()
 | 
				
			||||||
          .fetch(RhythmMedia(intendedActiveTrack.track));
 | 
					    //       .fetch(RhythmMedia(intendedActiveTrack.track));
 | 
				
			||||||
    }
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (medias.isEmpty) return;
 | 
					    if (medias.isEmpty) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,12 +20,15 @@ class AudioPlayerStreamProvider extends GetxController {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  List<StreamSubscription>? _subscriptions;
 | 
					  List<StreamSubscription>? _subscriptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  AudioPlayerStreamProvider() {
 | 
				
			||||||
  void onInit() {
 | 
					 | 
				
			||||||
    super.onInit();
 | 
					 | 
				
			||||||
    AudioServices.create().then(
 | 
					    AudioServices.create().then(
 | 
				
			||||||
      (value) => notificationService = value,
 | 
					      (value) => notificationService = value,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void onInit() {
 | 
				
			||||||
 | 
					    super.onInit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _subscriptions = [
 | 
					    _subscriptions = [
 | 
				
			||||||
      subscribeToPlaylist(),
 | 
					      subscribeToPlaylist(),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,5 @@
 | 
				
			|||||||
import 'package:go_router/go_router.dart';
 | 
					import 'package:go_router/go_router.dart';
 | 
				
			||||||
import 'package:rhythm_box/screens/explore.dart';
 | 
					import 'package:rhythm_box/screens/explore.dart';
 | 
				
			||||||
import 'package:rhythm_box/screens/player/view.dart';
 | 
					 | 
				
			||||||
import 'package:rhythm_box/screens/playlist/view.dart';
 | 
					import 'package:rhythm_box/screens/playlist/view.dart';
 | 
				
			||||||
import 'package:rhythm_box/screens/settings.dart';
 | 
					import 'package:rhythm_box/screens/settings.dart';
 | 
				
			||||||
import 'package:rhythm_box/shells/nav_shell.dart';
 | 
					import 'package:rhythm_box/shells/nav_shell.dart';
 | 
				
			||||||
@@ -28,9 +27,4 @@ final router = GoRouter(routes: [
 | 
				
			|||||||
      ),
 | 
					      ),
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
  ),
 | 
					  ),
 | 
				
			||||||
  GoRoute(
 | 
					 | 
				
			||||||
    path: '/player',
 | 
					 | 
				
			||||||
    name: 'player',
 | 
					 | 
				
			||||||
    builder: (context, state) => const PlayerScreen(),
 | 
					 | 
				
			||||||
  ),
 | 
					 | 
				
			||||||
]);
 | 
					]);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,235 @@
 | 
				
			|||||||
 | 
					import 'dart:async';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:dismissible_page/dismissible_page.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:gap/gap.dart';
 | 
				
			||||||
 | 
					import 'package:get/get.dart';
 | 
				
			||||||
 | 
					import 'package:google_fonts/google_fonts.dart';
 | 
				
			||||||
 | 
					import 'package:rhythm_box/providers/audio_player.dart';
 | 
				
			||||||
 | 
					import 'package:rhythm_box/services/artist.dart';
 | 
				
			||||||
 | 
					import 'package:rhythm_box/services/audio_player/audio_player.dart';
 | 
				
			||||||
 | 
					import 'package:rhythm_box/widgets/auto_cache_image.dart';
 | 
				
			||||||
 | 
					import 'package:rhythm_box/services/audio_services/image.dart';
 | 
				
			||||||
 | 
					import 'package:rhythm_box/widgets/tracks/querying_track_info.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PlayerScreen extends StatefulWidget {
 | 
					class PlayerScreen extends StatefulWidget {
 | 
				
			||||||
  const PlayerScreen({super.key});
 | 
					  final Duration durationCurrent, durationTotal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const PlayerScreen({
 | 
				
			||||||
 | 
					    super.key,
 | 
				
			||||||
 | 
					    required this.durationCurrent,
 | 
				
			||||||
 | 
					    required this.durationTotal,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  State<PlayerScreen> createState() => _PlayerScreenState();
 | 
					  State<PlayerScreen> createState() => _PlayerScreenState();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _PlayerScreenState extends State<PlayerScreen> {
 | 
					class _PlayerScreenState extends State<PlayerScreen> {
 | 
				
			||||||
 | 
					  late final AudioPlayerProvider _playback = Get.find();
 | 
				
			||||||
 | 
					  late final QueryingTrackInfoProvider _query = Get.find();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String? get _albumArt =>
 | 
				
			||||||
 | 
					      (_playback.state.value.activeTrack?.album?.images).asUrlString(
 | 
				
			||||||
 | 
					        index:
 | 
				
			||||||
 | 
					            (_playback.state.value.activeTrack?.album?.images?.length ?? 1) - 1,
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool get _isPlaying => _playback.isPlaying.value;
 | 
				
			||||||
 | 
					  bool get _isFetchingActiveTrack => _query.isQueryingTrackInfo.value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Duration _durationCurrent = Duration.zero;
 | 
				
			||||||
 | 
					  Duration _durationTotal = Duration.zero;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void _updateDurationCurrent(Duration dur) {
 | 
				
			||||||
 | 
					    setState(() => _durationCurrent = dur);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void _updateDurationTotal(Duration dur) {
 | 
				
			||||||
 | 
					    setState(() => _durationTotal = dur);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  List<StreamSubscription>? _subscriptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<void> _togglePlayState() async {
 | 
				
			||||||
 | 
					    if (!audioPlayer.isPlaying) {
 | 
				
			||||||
 | 
					      await audioPlayer.resume();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      await audioPlayer.pause();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    setState(() {});
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String _formatDuration(Duration duration) {
 | 
				
			||||||
 | 
					    String negativeSign = duration.isNegative ? '-' : '';
 | 
				
			||||||
 | 
					    String twoDigits(int n) => n.toString().padLeft(2, '0');
 | 
				
			||||||
 | 
					    String twoDigitMinutes = twoDigits(duration.inMinutes.abs());
 | 
				
			||||||
 | 
					    String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60).abs());
 | 
				
			||||||
 | 
					    return '$negativeSign$twoDigitMinutes:$twoDigitSeconds';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  double? _draggingValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void initState() {
 | 
				
			||||||
 | 
					    super.initState();
 | 
				
			||||||
 | 
					    _durationCurrent = widget.durationCurrent;
 | 
				
			||||||
 | 
					    _durationTotal = widget.durationTotal;
 | 
				
			||||||
 | 
					    _subscriptions = [
 | 
				
			||||||
 | 
					      audioPlayer.durationStream.listen(_updateDurationTotal),
 | 
				
			||||||
 | 
					      audioPlayer.positionStream.listen(_updateDurationCurrent),
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void dispose() {
 | 
				
			||||||
 | 
					    if (_subscriptions != null) {
 | 
				
			||||||
 | 
					      for (final subscription in _subscriptions!) {
 | 
				
			||||||
 | 
					        subscription.cancel();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    super.dispose();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return const Placeholder();
 | 
					    final size = MediaQuery.of(context).size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return DismissiblePage(
 | 
				
			||||||
 | 
					      backgroundColor: Theme.of(context).colorScheme.surface,
 | 
				
			||||||
 | 
					      onDismissed: () {
 | 
				
			||||||
 | 
					        Navigator.of(context).pop();
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      direction: DismissiblePageDismissDirection.down,
 | 
				
			||||||
 | 
					      child: Material(
 | 
				
			||||||
 | 
					        color: Colors.transparent,
 | 
				
			||||||
 | 
					        child: SafeArea(
 | 
				
			||||||
 | 
					          child: Column(
 | 
				
			||||||
 | 
					            mainAxisAlignment: MainAxisAlignment.center,
 | 
				
			||||||
 | 
					            children: [
 | 
				
			||||||
 | 
					              Hero(
 | 
				
			||||||
 | 
					                tag: const Key('current-active-track-album-art'),
 | 
				
			||||||
 | 
					                child: ClipRRect(
 | 
				
			||||||
 | 
					                  borderRadius: const BorderRadius.all(Radius.circular(16)),
 | 
				
			||||||
 | 
					                  child: AspectRatio(
 | 
				
			||||||
 | 
					                    aspectRatio: 1,
 | 
				
			||||||
 | 
					                    child: _albumArt != null
 | 
				
			||||||
 | 
					                        ? AutoCacheImage(
 | 
				
			||||||
 | 
					                            _albumArt!,
 | 
				
			||||||
 | 
					                            width: size.width,
 | 
				
			||||||
 | 
					                            height: size.width,
 | 
				
			||||||
 | 
					                          )
 | 
				
			||||||
 | 
					                        : Container(
 | 
				
			||||||
 | 
					                            color: Theme.of(context)
 | 
				
			||||||
 | 
					                                .colorScheme
 | 
				
			||||||
 | 
					                                .surfaceContainerHigh,
 | 
				
			||||||
 | 
					                            width: 64,
 | 
				
			||||||
 | 
					                            height: 64,
 | 
				
			||||||
 | 
					                            child: const Center(child: Icon(Icons.image)),
 | 
				
			||||||
 | 
					                          ),
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
 | 
					                ).marginSymmetric(horizontal: 24),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              const Gap(24),
 | 
				
			||||||
 | 
					              Text(
 | 
				
			||||||
 | 
					                _playback.state.value.activeTrack?.name ?? 'Not playing',
 | 
				
			||||||
 | 
					                style: Theme.of(context).textTheme.titleLarge,
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              Text(
 | 
				
			||||||
 | 
					                _playback.state.value.activeTrack?.artists?.asString() ??
 | 
				
			||||||
 | 
					                    'No author',
 | 
				
			||||||
 | 
					                style: Theme.of(context).textTheme.bodyMedium,
 | 
				
			||||||
 | 
					                overflow: TextOverflow.ellipsis,
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              const Gap(24),
 | 
				
			||||||
 | 
					              Column(
 | 
				
			||||||
 | 
					                children: [
 | 
				
			||||||
 | 
					                  SliderTheme(
 | 
				
			||||||
 | 
					                    data: SliderThemeData(
 | 
				
			||||||
 | 
					                      trackHeight: 2,
 | 
				
			||||||
 | 
					                      trackShape: _PlayerProgressTrackShape(),
 | 
				
			||||||
 | 
					                      thumbShape: const RoundSliderThumbShape(
 | 
				
			||||||
 | 
					                        enabledThumbRadius: 8,
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
 | 
					                      overlayShape: SliderComponentShape.noOverlay,
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    child: Slider(
 | 
				
			||||||
 | 
					                      value: _draggingValue ??
 | 
				
			||||||
 | 
					                          _durationCurrent.inMilliseconds.toDouble(),
 | 
				
			||||||
 | 
					                      min: 0,
 | 
				
			||||||
 | 
					                      max: _durationTotal.inMilliseconds.toDouble(),
 | 
				
			||||||
 | 
					                      onChanged: (value) {
 | 
				
			||||||
 | 
					                        setState(() => _draggingValue = value);
 | 
				
			||||||
 | 
					                      },
 | 
				
			||||||
 | 
					                      onChangeEnd: (value) {
 | 
				
			||||||
 | 
					                        print('Seek to $value ms');
 | 
				
			||||||
 | 
					                        audioPlayer.seek(Duration(milliseconds: value.toInt()));
 | 
				
			||||||
 | 
					                      },
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
 | 
					                  Row(
 | 
				
			||||||
 | 
					                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
				
			||||||
 | 
					                    children: [
 | 
				
			||||||
 | 
					                      Text(
 | 
				
			||||||
 | 
					                        _formatDuration(_durationCurrent),
 | 
				
			||||||
 | 
					                        style: GoogleFonts.robotoMono(fontSize: 12),
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
 | 
					                      Text(
 | 
				
			||||||
 | 
					                        _formatDuration(_durationTotal),
 | 
				
			||||||
 | 
					                        style: GoogleFonts.robotoMono(fontSize: 12),
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                  ).paddingSymmetric(horizontal: 8, vertical: 4),
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					              ).paddingSymmetric(horizontal: 24),
 | 
				
			||||||
 | 
					              const Gap(24),
 | 
				
			||||||
 | 
					              Row(
 | 
				
			||||||
 | 
					                mainAxisAlignment: MainAxisAlignment.center,
 | 
				
			||||||
 | 
					                children: [
 | 
				
			||||||
 | 
					                  SizedBox(
 | 
				
			||||||
 | 
					                    width: 56,
 | 
				
			||||||
 | 
					                    height: 56,
 | 
				
			||||||
 | 
					                    child: IconButton.filled(
 | 
				
			||||||
 | 
					                      icon: _isFetchingActiveTrack
 | 
				
			||||||
 | 
					                          ? const SizedBox(
 | 
				
			||||||
 | 
					                              height: 20,
 | 
				
			||||||
 | 
					                              width: 20,
 | 
				
			||||||
 | 
					                              child: CircularProgressIndicator(
 | 
				
			||||||
 | 
					                                color: Colors.white,
 | 
				
			||||||
 | 
					                                strokeWidth: 2.5,
 | 
				
			||||||
 | 
					                              ),
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                          : Icon(
 | 
				
			||||||
 | 
					                              !_isPlaying ? Icons.play_arrow : Icons.pause,
 | 
				
			||||||
 | 
					                              size: 28,
 | 
				
			||||||
 | 
					                            ),
 | 
				
			||||||
 | 
					                      onPressed:
 | 
				
			||||||
 | 
					                          _isFetchingActiveTrack ? null : _togglePlayState,
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        ).marginAll(24),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _PlayerProgressTrackShape extends RoundedRectSliderTrackShape {
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Rect getPreferredRect({
 | 
				
			||||||
 | 
					    required RenderBox parentBox,
 | 
				
			||||||
 | 
					    Offset offset = Offset.zero,
 | 
				
			||||||
 | 
					    required SliderThemeData sliderTheme,
 | 
				
			||||||
 | 
					    bool isEnabled = false,
 | 
				
			||||||
 | 
					    bool isDiscrete = false,
 | 
				
			||||||
 | 
					  }) {
 | 
				
			||||||
 | 
					    final trackHeight = sliderTheme.trackHeight;
 | 
				
			||||||
 | 
					    final trackLeft = offset.dx;
 | 
				
			||||||
 | 
					    final trackTop = offset.dy + (parentBox.size.height - trackHeight!) / 2;
 | 
				
			||||||
 | 
					    final trackWidth = parentBox.size.width;
 | 
				
			||||||
 | 
					    return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,12 @@
 | 
				
			|||||||
import 'dart:async';
 | 
					import 'dart:async';
 | 
				
			||||||
import 'dart:math';
 | 
					import 'dart:math';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:dismissible_page/dismissible_page.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:gap/gap.dart';
 | 
					import 'package:gap/gap.dart';
 | 
				
			||||||
import 'package:get/get.dart';
 | 
					import 'package:get/get.dart';
 | 
				
			||||||
import 'package:go_router/go_router.dart';
 | 
					 | 
				
			||||||
import 'package:rhythm_box/providers/audio_player.dart';
 | 
					import 'package:rhythm_box/providers/audio_player.dart';
 | 
				
			||||||
 | 
					import 'package:rhythm_box/screens/player/view.dart';
 | 
				
			||||||
import 'package:rhythm_box/services/audio_player/audio_player.dart';
 | 
					import 'package:rhythm_box/services/audio_player/audio_player.dart';
 | 
				
			||||||
import 'package:rhythm_box/services/audio_services/image.dart';
 | 
					import 'package:rhythm_box/services/audio_services/image.dart';
 | 
				
			||||||
import 'package:rhythm_box/widgets/auto_cache_image.dart';
 | 
					import 'package:rhythm_box/widgets/auto_cache_image.dart';
 | 
				
			||||||
@@ -71,6 +72,12 @@ class _BottomPlayerState extends State<BottomPlayer>
 | 
				
			|||||||
    _subscriptions = [
 | 
					    _subscriptions = [
 | 
				
			||||||
      audioPlayer.durationStream.listen(_updateDurationTotal),
 | 
					      audioPlayer.durationStream.listen(_updateDurationTotal),
 | 
				
			||||||
      audioPlayer.positionStream.listen(_updateDurationCurrent),
 | 
					      audioPlayer.positionStream.listen(_updateDurationCurrent),
 | 
				
			||||||
 | 
					      _playback.state.listen((state) {
 | 
				
			||||||
 | 
					        if (state.playlist.medias.isNotEmpty && !_isLifted) {
 | 
				
			||||||
 | 
					          _animationController.animateTo(1);
 | 
				
			||||||
 | 
					          _isLifted = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }),
 | 
				
			||||||
      _playback.isPlaying.listen((value) {
 | 
					      _playback.isPlaying.listen((value) {
 | 
				
			||||||
        if (value && !_isLifted) {
 | 
					        if (value && !_isLifted) {
 | 
				
			||||||
          _animationController.animateTo(1);
 | 
					          _animationController.animateTo(1);
 | 
				
			||||||
@@ -104,6 +111,7 @@ class _BottomPlayerState extends State<BottomPlayer>
 | 
				
			|||||||
      axisAlignment: -1,
 | 
					      axisAlignment: -1,
 | 
				
			||||||
      child: Obx(
 | 
					      child: Obx(
 | 
				
			||||||
        () => GestureDetector(
 | 
					        () => GestureDetector(
 | 
				
			||||||
 | 
					          behavior: HitTestBehavior.translucent,
 | 
				
			||||||
          child: Column(
 | 
					          child: Column(
 | 
				
			||||||
            children: [
 | 
					            children: [
 | 
				
			||||||
              if (_durationCurrent != Duration.zero)
 | 
					              if (_durationCurrent != Duration.zero)
 | 
				
			||||||
@@ -122,10 +130,10 @@ class _BottomPlayerState extends State<BottomPlayer>
 | 
				
			|||||||
              Row(
 | 
					              Row(
 | 
				
			||||||
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
					                mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
				
			||||||
                children: [
 | 
					                children: [
 | 
				
			||||||
                  ClipRRect(
 | 
					                  Hero(
 | 
				
			||||||
                    borderRadius: const BorderRadius.all(Radius.circular(8)),
 | 
					                    tag: const Key('current-active-track-album-art'),
 | 
				
			||||||
                    child: Hero(
 | 
					                    child: ClipRRect(
 | 
				
			||||||
                      tag: const Key('current-active-track-album-art'),
 | 
					                      borderRadius: const BorderRadius.all(Radius.circular(8)),
 | 
				
			||||||
                      child: _albumArt != null
 | 
					                      child: _albumArt != null
 | 
				
			||||||
                          ? AutoCacheImage(_albumArt!, width: 64, height: 64)
 | 
					                          ? AutoCacheImage(_albumArt!, width: 64, height: 64)
 | 
				
			||||||
                          : Container(
 | 
					                          : Container(
 | 
				
			||||||
@@ -172,7 +180,10 @@ class _BottomPlayerState extends State<BottomPlayer>
 | 
				
			|||||||
            ],
 | 
					            ],
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
          onTap: () {
 | 
					          onTap: () {
 | 
				
			||||||
            GoRouter.of(context).pushNamed('player');
 | 
					            context.pushTransparentRoute(PlayerScreen(
 | 
				
			||||||
 | 
					              durationCurrent: _durationCurrent,
 | 
				
			||||||
 | 
					              durationTotal: _durationTotal,
 | 
				
			||||||
 | 
					            ));
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,6 +65,8 @@ class _PlaylistTrackListState extends State<PlaylistTrackList> {
 | 
				
			|||||||
            title: Text(item?.name ?? 'Loading...'),
 | 
					            title: Text(item?.name ?? 'Loading...'),
 | 
				
			||||||
            subtitle: Text(
 | 
					            subtitle: Text(
 | 
				
			||||||
              item?.artists?.asString() ?? 'Please stand by...',
 | 
					              item?.artists?.asString() ?? 'Please stand by...',
 | 
				
			||||||
 | 
					              maxLines: 1,
 | 
				
			||||||
 | 
					              overflow: TextOverflow.ellipsis,
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            onTap: () {
 | 
					            onTap: () {
 | 
				
			||||||
              if (item == null) return;
 | 
					              if (item == null) return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -326,6 +326,14 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.0.0"
 | 
					    version: "2.0.0"
 | 
				
			||||||
 | 
					  dismissible_page:
 | 
				
			||||||
 | 
					    dependency: "direct main"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: dismissible_page
 | 
				
			||||||
 | 
					      sha256: "5b2316f770fe83583f770df1f6505cb19102081c5971979806e77f2e507a9958"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "1.0.2"
 | 
				
			||||||
  drift:
 | 
					  drift:
 | 
				
			||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,6 +81,7 @@ dependencies:
 | 
				
			|||||||
    git:
 | 
					    git:
 | 
				
			||||||
      url: https://github.com/KRTirtho/scrobblenaut.git
 | 
					      url: https://github.com/KRTirtho/scrobblenaut.git
 | 
				
			||||||
      ref: dart-3-support
 | 
					      ref: dart-3-support
 | 
				
			||||||
 | 
					  dismissible_page: ^1.0.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dev_dependencies:
 | 
					dev_dependencies:
 | 
				
			||||||
  flutter_test:
 | 
					  flutter_test:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user