import 'package:auto_size_text/auto_size_text.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_xlider/flutter_xlider.dart'; import 'package:ocarina/api/audio/audioplayer_service.dart'; import 'package:ocarina/api/subsonic/song.dart'; import 'package:ocarina/main.dart'; import 'package:ocarina/util/util.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:shimmer/shimmer.dart'; import 'package:text_scroll/text_scroll.dart'; /// The player control widget /// /// Showcases the playing song's details and features playback controls class Player extends StatefulWidget { /// The player control widget /// /// Showcases the playing song's details and features playback controls const Player({super.key}); @override State createState() => PlayerState(); } /// State of [Player] class PlayerState extends State { void update() { logger.d(AudioPlayerService().song?.coverArtUrl); setState(() {}); } var _showFullControls = false; final _sheetController = DraggableScrollableController(); @override Widget build(BuildContext context) { return NotificationListener( onNotification: (n) { if (n.extent > 0.3) { _showFullControls = true; } else { _showFullControls = false; } setState(() {}); return false; }, child: DraggableScrollableSheet( controller: _sheetController, initialChildSize: 0.1, snap: true, snapSizes: const [0.1, 1], minChildSize: 0.1, builder: (c, s) => SingleChildScrollView( controller: s, child: Column( children: [ ClipRRect( borderRadius: BorderRadius.circular(8), child: GestureDetector( onTap: () { _sheetController.animateTo( (_sheetController.size == 1) ? 0.1 : 1, duration: const Duration(milliseconds: 300), curve: Curves.easeIn, ); }, child: Container( color: Theme.of(context).colorScheme.primaryContainer, height: 10.h, width: 100.w, child: AnimatedOpacity( opacity: _showFullControls ? 0 : 1, duration: const Duration(milliseconds: 300), child: Padding( padding: const EdgeInsets.all(16), child: ValueListenableBuilder( valueListenable: songNotifier, builder: (c, t, w) { return Row( children: [ SizedBox( height: 10.h, width: 10.h, child: Padding( padding: const EdgeInsets.all(16), child: ClipRRect( child: (t == null) ? ColoredBox( color: Theme.of(context) .colorScheme .primaryContainer, child: Center( child: Icon( Icons.music_note, color: Theme.of(context) .colorScheme .onPrimaryContainer, ), ), ) : CachedNetworkImage( cacheKey: t.coverArtId, imageUrl: t.coverArtUrl, placeholder: (c, d) => Shimmer.fromColors( baseColor: Colors.grey.shade300, highlightColor: Colors.grey.shade100, child: Container( color: Colors.grey, ), ), errorWidget: (c, _, __) { logger ..e(_) ..e(__); return ColoredBox( color: Theme.of(context) .colorScheme .primaryContainer, child: Center( child: Icon( Icons.music_note, color: Theme.of(context) .colorScheme .onPrimaryContainer, ), ), ); }, ), ), ), ), const SizedBox( width: 5, ), Expanded( child: AutoSizeText( AudioPlayerService().song == null ? "Nothing" : AudioPlayerService().song!.title, style: TextStyle( color: Theme.of(context) .colorScheme .onPrimaryContainer, fontSize: 14, decoration: TextDecoration.none, ), overflowReplacement: TextScroll( AudioPlayerService().song == null ? "Nothing" : AudioPlayerService().song!.title, ), ), ), SizedBox( width: 35.w, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( onPressed: () { AudioPlayerService().previous(); }, icon: const Icon(Icons.skip_previous), ), IconButton( onPressed: () async { if (AudioPlayerService().song == null) { return; } if (AudioPlayerService().isPlaying) { await AudioPlayerService().pause(); } else { AudioPlayerService().resume(); } setState(() {}); }, icon: AnimatedCrossFade( firstChild: const Icon(Icons.play_arrow), secondChild: const Icon(Icons.pause), crossFadeState: (AudioPlayerService().isPlaying) ? CrossFadeState.showSecond : CrossFadeState.showFirst, duration: const Duration(milliseconds: 300), ), ), IconButton( onPressed: () { AudioPlayerService().next(); }, icon: const Icon(Icons.skip_next), ), ], ), ), ], ); }, ), ), ), ), ), ), Container( // Full player controls here height: 90.h, color: Theme.of(context).colorScheme.primaryContainer, width: 100.w, child: Center( child: Column( children: [ const SizedBox( height: 30, ), SizedBox( width: 80.w, height: 80.w, child: ClipRRect( borderRadius: BorderRadius.circular(16), child: (AudioPlayerService().song == null) ? ColoredBox( color: Theme.of(context) .colorScheme .secondaryContainer, child: Center( child: Icon( Icons.music_note, color: Theme.of(context) .colorScheme .onSecondaryContainer, ), ), ) : (CachedNetworkImage( cacheKey: AudioPlayerService().song!.coverArtId, imageUrl: AudioPlayerService().song!.coverArtUrl, placeholder: (c, d) => Shimmer.fromColors( baseColor: Colors.grey.shade300, highlightColor: Colors.grey.shade100, child: Container( color: Colors.grey, ), ), errorWidget: (c, _, __) { logger ..e(_) ..e(__); return ColoredBox( color: Theme.of(context) .colorScheme .secondaryContainer, child: Center( child: Icon( Icons.music_note, color: Theme.of(context) .colorScheme .onSecondaryContainer, ), ), ); }, )), ), ), const SizedBox( height: 10, ), SizedBox( width: 70.w, height: 40, child: AutoSizeText( AudioPlayerService().song?.title ?? "Nothing", textAlign: TextAlign.center, style: TextStyle( fontWeight: FontWeight.bold, fontSize: 20.sp, ), overflowReplacement: TextScroll( AudioPlayerService().song?.title ?? "Nothing", textAlign: TextAlign.center, style: TextStyle( fontWeight: FontWeight.bold, fontSize: 20.sp, ), ), ), ), SizedBox( width: 70.w, height: 30, child: AutoSizeText( AudioPlayerService().song?.artistName ?? "Nobody", style: TextStyle(fontSize: 16.sp), textAlign: TextAlign.center, overflowReplacement: TextScroll( AudioPlayerService().song?.artistName ?? "Nobody", style: TextStyle( fontSize: 16.sp, ), ), ), ), SizedBox( width: 60.w, height: 40, child: Overlay( initialEntries: [ OverlayEntry( builder: (c) => ValueListenableBuilder>( valueListenable: progressNotifier, builder: (c, v, _) { return FlutterSlider( values: [v[0].toDouble()], max: v[1].toDouble(), min: 0, onDragCompleted: (handlerIndex, lowerValue, upperValue) { if (AudioPlayerService().song == null) { return; } logger.d(lowerValue); AudioPlayerService() .seek(lowerValue as double); }, ); }, ), ), ], ), ), const SizedBox( height: 10, ), SizedBox( width: 60.w, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( onPressed: () { AudioPlayerService().previous(); }, icon: const Icon( Icons.skip_previous, size: 36, ), ), IconButton( onPressed: () async { if (AudioPlayerService().song == null) { return; } if (AudioPlayerService().isPlaying) { await AudioPlayerService().pause(); } else { AudioPlayerService().resume(); } setState(() {}); }, icon: AnimatedCrossFade( firstChild: const Icon( Icons.play_arrow, size: 36, // TODO: adapt to display size ), secondChild: const Icon(Icons.pause, size: 36), crossFadeState: (AudioPlayerService().isPlaying) ? CrossFadeState.showSecond : CrossFadeState.showFirst, duration: const Duration(milliseconds: 300), ), ), IconButton( onPressed: () { AudioPlayerService().next(); }, icon: const Icon( Icons.skip_next, size: 36, ), ), ], ), ), ], ), ), ), ], ), ), ), ); } }