From e43e44cbb9c31d983a3a222d7fd9ac3995999e3c Mon Sep 17 00:00:00 2001 From: alvaro Date: Sun, 7 Nov 2021 22:40:10 -0400 Subject: [PATCH] Added callback to notify when user changes player position. Added property to define whether or not to show progress bar. Added style properties for player position and duration label. Treated fails when playing an audio with no duration or with no possibility of getting its current position. Visual improvements in player. --- tau_sound/lib/public/ui/tau_player_ui.dart | 224 +++++++++++++-------- 1 file changed, 136 insertions(+), 88 deletions(-) diff --git a/tau_sound/lib/public/ui/tau_player_ui.dart b/tau_sound/lib/public/ui/tau_player_ui.dart index b2179ed..55d7a9c 100644 --- a/tau_sound/lib/public/ui/tau_player_ui.dart +++ b/tau_sound/lib/public/ui/tau_player_ui.dart @@ -63,6 +63,18 @@ class TauPlayerUI extends StatefulWidget { /// should do when user press the play/pause button. final Future Function(TauPlayer tauPlayer) onTap; + /// A callback from TauPlayerUI that notifies when user clicks in the Slider to change actual audio position + final Function(double position)? onPositionChanged; + + /// A Flag to set if player should show progressbar or not, default is TRUE + final bool? showProgressBar; + + /// Style for player position label + final TextStyle? playerPositionTextStyle; + + /// Style for player duration label + final TextStyle? playerDurationTextStyle; + /// /// /// @@ -70,6 +82,10 @@ class TauPlayerUI extends StatefulWidget { required this.player, required this.onTap, this.onSpeedChanged, + this.onPositionChanged, + this.showProgressBar = true, + this.playerDurationTextStyle, + this.playerPositionTextStyle, this.iconSize = 45, this.playerRefreshDuration = const Duration(milliseconds: 500), this.playPauseColor, @@ -124,6 +140,7 @@ class _TauPlayerUIState extends State _animationController!.reverse(); break; case PlayerState.isPlaying: + default: _animationController!.forward(); break; } @@ -145,109 +162,140 @@ class _TauPlayerUIState extends State @override Widget build(BuildContext context) { return StreamBuilder( - stream: widget.player.onProgress, - builder: (context, snapshot) { - if (snapshot.data != null) { - _audioDuration ??= snapshot.data!.duration; - } - return Row( - mainAxisSize: MainAxisSize.max, - children: [ - Material( - color: Colors.transparent, - shape: CircleBorder(), - child: InkWell( - onTap: () async { - await widget.onTap(widget.player); - }, - child: AnimatedIcon( - icon: AnimatedIcons.play_pause, - progress: _animationController!, - size: widget.iconSize ?? 45, - color: - widget.playPauseColor ?? Theme.of(context).primaryColor, - ), + stream: widget.player.onProgress, + builder: (context, snapshot) { + if (snapshot.data != null) { + _audioDuration = snapshot.data!.duration; + _actualPlayerPosition = snapshot.data!.position; + } + + return Row( + mainAxisSize: MainAxisSize.max, + children: [ + Material( + color: Colors.transparent, + shape: CircleBorder(), + child: InkWell( + onTap: () async { + await widget.onTap(widget.player); + }, + child: AnimatedIcon( + icon: AnimatedIcons.play_pause, + progress: _animationController!, + size: widget.iconSize ?? 45, + color: + widget.playPauseColor ?? Theme.of(context).primaryColor, ), ), + ), + if (widget.showProgressBar! && + (snapshot.data != null && + !snapshot.data!.duration.inMilliseconds.isNegative)) Expanded( child: Theme( data: ThemeData( sliderTheme: widget.sliderThemeData ?? SliderThemeData( - trackHeight: 18, - thumbShape: RoundSliderThumbShape( - enabledThumbRadius: 0, - disabledThumbRadius: 0, - elevation: 25, - ), - ), - ), - child: Slider( - value: snapshot.data == null - ? 0.0 - : snapshot.data!.position.inMilliseconds.toDouble(), - max: _audioDuration == null - ? 0 - : _audioDuration!.inMilliseconds.toDouble(), - onChanged: (double value) { - widget.player.seekTo( - Duration( - milliseconds: value.toInt(), + trackHeight: 5, + thumbColor: Theme.of(context).primaryColor, + activeTrackColor: + Theme.of(context).toggleableActiveColor, + inactiveTrackColor: + Theme.of(context).unselectedWidgetColor, ), - ); - }, - onChangeEnd: (value) { - setState(() {}); - }, ), + child: _audioDuration != null && + _audioDuration!.inMilliseconds > 0 + ? Slider.adaptive( + value: _actualPlayerPosition == null + ? 0.0 + : _actualPlayerPosition!.inMilliseconds + .toDouble(), + max: _audioDuration == null + ? 0 + : _audioDuration!.inMilliseconds.toDouble(), + onChanged: _onChanged, + onChangeStart: _onChanged, + onChangeEnd: _onChanged, + ) + : SizedBox(), ), ), - if (widget.speeds != null && widget.speeds!.isNotEmpty) - AnimatedContainer( - duration: Duration(milliseconds: 300), - width: widget.alwaysShowPlayerSpeed - ? 35 - : widget.player.isPlaying - ? 35 - : 0, - padding: EdgeInsets.zero, - margin: EdgeInsets.zero, - curve: Curves.easeIn, - child: ElevatedButton( - onPressed: () { - _speedIndex++; - if (_speedIndex == widget.speeds!.length) { - _speedIndex = 0; - } - _actualSpeed = widget.speeds![_speedIndex]; - widget.player.setSpeed(_actualSpeed!); - if (widget.onSpeedChanged != null) { - widget.onSpeedChanged!(_actualSpeed!); - } - setState(() {}); - }, - style: ElevatedButton.styleFrom( - shape: CircleBorder(), - padding: EdgeInsets.zero, - ), - child: FittedBox( - child: Text( - '${_actualSpeed}x', - ), + if (widget.speeds != null && widget.speeds!.isNotEmpty) + AnimatedContainer( + duration: Duration(milliseconds: 300), + width: widget.alwaysShowPlayerSpeed + ? 35 + : widget.player.isPlaying + ? 35 + : 0, + padding: EdgeInsets.zero, + margin: EdgeInsets.zero, + curve: Curves.easeIn, + child: ElevatedButton( + onPressed: () { + _speedIndex++; + if (_speedIndex == widget.speeds!.length) { + _speedIndex = 0; + } + _actualSpeed = widget.speeds![_speedIndex]; + widget.player.setSpeed(_actualSpeed!); + if (widget.onSpeedChanged != null) { + widget.onSpeedChanged!(_actualSpeed!); + } + setState(() {}); + }, + style: ElevatedButton.styleFrom( + shape: CircleBorder(), + padding: EdgeInsets.zero, + ), + child: FittedBox( + child: Text( + '${_actualSpeed}x', ), ), ), - SizedBox( - width: 10, ), - if (_audioDuration != null) - Text( - '${_convertDurationToTime(snapshot.data?.position)}/${_convertDurationToTime(_audioDuration)}') - else - Text(_convertDurationToTime(_actualPlayerPosition)) - ], - ); - }); + SizedBox( + width: 10, + ), + if (_audioDuration != null && _audioDuration!.inMilliseconds > 0) + Column( + children: [ + Text( + '${_convertDurationToTime(snapshot.data?.position)}', + style: widget.playerPositionTextStyle, + ), + Text( + '${_convertDurationToTime(_audioDuration)}', + style: widget.playerDurationTextStyle, + ) + ], + ) + else if (snapshot.data != null && + snapshot.data!.position.inSeconds > 0) + Text( + _convertDurationToTime(snapshot.data?.position), + style: widget.playerPositionTextStyle, + ) + ], + ); + }, + ); + } + + void _onChanged(double position) { + setState(() { + _actualPlayerPosition = Duration( + milliseconds: position.toInt(), + ); + widget.player.seekTo( + _actualPlayerPosition!, + ); + }); + if (widget.onPositionChanged != null) { + widget.onPositionChanged!(position); + } } ///