diff options
author | Scott Murray <scott.murray@konsulko.com> | 2024-01-03 18:14:12 -0500 |
---|---|---|
committer | Scott Murray <scott.murray@konsulko.com> | 2024-01-03 18:23:55 -0500 |
commit | 2d395f4431ba4aa5055d02437463588f4d4c8127 (patch) | |
tree | 977be67fcfab0edac1e6e03f77af6e7be0dc8931 /lib/presentation/screens/media/media_player_controls.dart | |
parent | 4742fde5c48726357cc8db06d237e9db6c3df608 (diff) |
Initial mediaplayer implementation
Notable changes:
- Added dart_mpd package as a dependency.
- Added MPD client class and associated provider.
- Added MPD client configuration to the configuration file for
potential usecases where MPD may not be available locally.
- Added playlist, play state, art, etc. providers for use in the
mediaplayer front end UI.
- Reworked MediaPlayer classes to wire up MPD client backend.
- Removed volume slider from the bottom of the media pages to
make more room for playlist / preset tables, as only being
able to show 3 entries in each was not usable in practice.
- Reworked media player mocked up position indicator into an
actual slider control, and reworked the radio slider styling
to match for better UI consistency.
- Reworked media player and radio playlist / preset tables to
attempt to have them layout at the same location and add an
always visible scroll bar. The scroll bars currently have a
layout issue with respect to scroll track size that does not
seem to have an obvious fix. They are usable for now, and
further investigation will be done when time permits.
- Wired up play/pause button on the side volume control via a
new set of play state and controller providers.
Bug-AGL: SPEC-5028, SPEC-5029
Change-Id: I87efecc58b4e185443942eb32ff8148ebcd675c3
Signed-off-by: Scott Murray <scott.murray@konsulko.com>
Diffstat (limited to 'lib/presentation/screens/media/media_player_controls.dart')
-rw-r--r-- | lib/presentation/screens/media/media_player_controls.dart | 201 |
1 files changed, 136 insertions, 65 deletions
diff --git a/lib/presentation/screens/media/media_player_controls.dart b/lib/presentation/screens/media/media_player_controls.dart index 518b669..26cdfce 100644 --- a/lib/presentation/screens/media/media_player_controls.dart +++ b/lib/presentation/screens/media/media_player_controls.dart @@ -1,40 +1,49 @@ import 'package:flutter_ics_homescreen/core/utils/helpers.dart'; import 'package:flutter_ics_homescreen/export.dart'; -import 'package:flutter_ics_homescreen/presentation/screens/media/widgets/gradient_progress_indicator.dart'; +import 'package:flutter_ics_homescreen/presentation/screens/settings/settings_screens/audio_settings/widget/slider_widgets.dart'; -class MediaPlayerControls extends StatefulWidget { - const MediaPlayerControls( - {super.key, - required this.songName, - required this.songLengthStart, - required this.songLengthStop}); +// Time to string helper, returns HH:MM:SS or MM:SS as appropriate +String timeToString(Duration time) { + String result = ""; + if (time > const Duration(minutes: 59, seconds: 59)) { + result = time.toString().split('.').first.padLeft(8, "0"); + } else { + result = time.toString().substring(2, 7); + } + return result; +} - final String songName; - final String songLengthStart; - final String songLengthStop; +class MediaPlayerControls extends ConsumerStatefulWidget { + const MediaPlayerControls({super.key}); @override - State<MediaPlayerControls> createState() => _MediaPlayerControlsState(); + ConsumerState<MediaPlayerControls> createState() => + _MediaPlayerControlsState(); } -class _MediaPlayerControlsState extends State<MediaPlayerControls> { - late String songName; - late String songLengthStart; - late String songLengthStop; - final String albumName = "Gorillaz"; - - int songProgress = 20; - - @override - void initState() { - songName = widget.songName; - songLengthStart = widget.songLengthStart; - songLengthStop = widget.songLengthStop; - super.initState(); - } +class _MediaPlayerControlsState extends ConsumerState<MediaPlayerControls> { + //@override + //void initState() { + // super.initState(); + //} @override Widget build(BuildContext context) { + var currentSong = ref.watch( + mediaPlayerStateProvider.select((mediaplayer) => mediaplayer.song)); + var songPosition = ref.watch(mediaPlayerPositionProvider); + + String songName = ""; + String songDetail = ""; + String songPositionString = "00:00"; + String songLengthString = "00:00"; + if (currentSong != null) { + songName = currentSong.title; + songDetail = currentSong.artist; + songLengthString = timeToString(currentSong.duration); + } + songPositionString = timeToString(songPosition); + return Material( color: Colors.transparent, child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -46,42 +55,25 @@ class _MediaPlayerControlsState extends State<MediaPlayerControls> { shadows: [Helpers.dropShadowRegular], fontSize: 44), ), - MediaPlayerControlsubDetails( - albumName: albumName, + MediaPlayerControlsDetails( + songDetail: songDetail, ), Column(children: [ - GradientProgressIndicator( - percent: songProgress, - type: "media", - gradient: LinearGradient( - begin: Alignment.centerLeft, - end: Alignment.centerRight, - colors: [ - AGLDemoColors.jordyBlueColor, - AGLDemoColors.jordyBlueColor.withOpacity(0.8), - ]), - backgroundColor: AGLDemoColors.gradientBackgroundDarkColor, - ), - // const LinearProgressIndicator( - // backgroundColor: AGLDemoColors.gradientBackgroundDarkColor, - // color: Colors.white70, - // minHeight: 8, - // value: 0.7, - // ), + const MediaPlayerControlsSlider(), Padding( padding: const EdgeInsets.symmetric(vertical: 5), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - songLengthStart, + songPositionString, style: TextStyle( color: Colors.white, fontSize: 26, shadows: [Helpers.dropShadowRegular]), ), Text( - songLengthStop, + songLengthString, style: TextStyle( color: Colors.white, fontSize: 26, @@ -91,23 +83,23 @@ class _MediaPlayerControlsState extends State<MediaPlayerControls> { ), ), ]), - const MediaPlayerActions(), + const MediaPlayerControlsActions(), ]), ); } } -class MediaPlayerControlsubDetails extends StatefulWidget { - const MediaPlayerControlsubDetails({super.key, required this.albumName}); - final String albumName; +class MediaPlayerControlsDetails extends StatefulWidget { + const MediaPlayerControlsDetails({super.key, required this.songDetail}); + final String songDetail; @override - State<MediaPlayerControlsubDetails> createState() => - _MediaPlayerControlsubDetailsState(); + State<MediaPlayerControlsDetails> createState() => + _MediaPlayerControlsDetailsState(); } -class _MediaPlayerControlsubDetailsState - extends State<MediaPlayerControlsubDetails> { +class _MediaPlayerControlsDetailsState + extends State<MediaPlayerControlsDetails> { bool isShuffleEnabled = false; bool isRepeatEnabled = false; @override @@ -116,7 +108,7 @@ class _MediaPlayerControlsubDetailsState mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - widget.albumName, + widget.songDetail, style: TextStyle( color: Colors.white, fontWeight: FontWeight.w400, @@ -158,25 +150,98 @@ class _MediaPlayerControlsubDetailsState } } -class MediaPlayerActions extends StatefulWidget { - const MediaPlayerActions({super.key}); +class MediaPlayerControlsSlider extends ConsumerStatefulWidget { + const MediaPlayerControlsSlider({super.key}); + + @override + ConsumerState<MediaPlayerControlsSlider> createState() => + MediaPlayerControlsSliderState(); +} + +class MediaPlayerControlsSliderState + extends ConsumerState<MediaPlayerControlsSlider> { + //late Duration songPosition; + + //@override + //void initState() { + // songPosition = ref.read(mediaPlayerPositionProvider); + // super.initState(); + //} + + @override + Widget build(BuildContext context) { + var currentSong = ref.watch( + mediaPlayerStateProvider.select((mediaplayer) => mediaplayer.song)); + var songPosition = ref.watch(mediaPlayerPositionProvider); + + Duration songLength = Duration.zero; + if (currentSong != null) { + songLength = currentSong.duration; + } + + return Container( + height: 80, + child: SliderTheme( + data: SliderThemeData( + overlayShape: SliderComponentShape.noOverlay, + valueIndicatorShape: SliderComponentShape.noOverlay, + activeTickMarkColor: Colors.transparent, + inactiveTickMarkColor: Colors.transparent, + inactiveTrackColor: AGLDemoColors.periwinkleColor, + thumbShape: const PolygonSliderThumb(sliderValue: 3, thumbRadius: 23), + //trackHeight: 5, + ), + child: Slider( + max: songLength.inMilliseconds.toDouble(), + value: songPosition.inMilliseconds.toDouble(), + onChangeStart: (double value) { + // Disable timer so position will not change while control is + // being dragged. It will be re-enabled via the playback state + // update from MPD. + ref.read(mediaPlayerPositionProvider.notifier).pause(); + }, + onChanged: (double newValue) { + setState(() { + ref + .read(mediaPlayerPositionProvider.notifier) + .set(Duration(milliseconds: newValue.toInt())); + }); + }, + onChangeEnd: (double newValue) { + ref.read(mpdClientProvider).seek(newValue.toInt()); + }, + ), + ), + ); + } +} + +class MediaPlayerControlsActions extends ConsumerStatefulWidget { + const MediaPlayerControlsActions({super.key}); @override - State<MediaPlayerActions> createState() => _MediaPlayerActionsState(); + ConsumerState<MediaPlayerControlsActions> createState() => + _MediaPlayerControlsActionsState(); } -class _MediaPlayerActionsState extends State<MediaPlayerActions> { +class _MediaPlayerControlsActionsState + extends ConsumerState<MediaPlayerControlsActions> { bool isPressed = false; - bool isPlaying = true; @override Widget build(BuildContext context) { + bool isPlaying = ref.watch(mediaPlayerStateProvider + .select((mediaplayer) => mediaplayer.playState)) == + PlayState.playing; + return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ InkWell( customBorder: const CircleBorder(), - onTap: () {}, + onTap: () { + ref.read(mpdClientProvider).previous(); + }, child: Padding( padding: const EdgeInsets.all(8.0), child: SvgPicture.asset( @@ -191,7 +256,11 @@ class _MediaPlayerActionsState extends State<MediaPlayerActions> { customBorder: const CircleBorder(), onTap: () { setState(() { - isPlaying = !isPlaying; + if (isPlaying) { + ref.read(mpdClientProvider).pause(); + } else { + ref.read(mpdClientProvider).play(); + } }); }, onTapDown: (details) { @@ -221,7 +290,9 @@ class _MediaPlayerActionsState extends State<MediaPlayerActions> { ), InkWell( customBorder: const CircleBorder(), - onTap: () {}, + onTap: () { + ref.read(mpdClientProvider).next(); + }, child: Padding( padding: const EdgeInsets.all(8.0), child: SvgPicture.asset( |