From 7e0fdb3fd84f2331bf83c17a1160e9d9e2fff2ec Mon Sep 17 00:00:00 2001 From: suragch Date: Fri, 30 Apr 2021 18:44:45 +0800 Subject: [PATCH] added timeLabelType for showing remaining time --- CHANGELOG.md | 4 + example/lib/main.dart | 194 +++++++++++++++--------- example/pubspec.lock | 6 +- example/pubspec.yaml | 2 +- lib/audio_video_progress_bar.dart | 47 +++++- pubspec.yaml | 2 +- test/audio_video_progress_bar_test.dart | 2 + 7 files changed, 179 insertions(+), 78 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37510b3..c31ff28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [0.4.0] - April 30, 2021 + +* Added `timeLabelType` as a `TimeLabelType` enum with values of `totalTime` and `remainingTime`, which shows the time left as a negative number. + ## [0.3.2] - April 26, 2021 * Fixed bug with failure to update label color on theme change. diff --git a/example/lib/main.dart b/example/lib/main.dart index d7305ae..28ccb5f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -16,9 +16,7 @@ class MyApp extends StatelessWidget { builder: (context, value, child) { return MaterialApp( theme: ThemeData( - primarySwatch: value.color, - brightness: value.brightness - ), + primarySwatch: value.color, brightness: value.brightness), home: HomeWidget(), ); }); @@ -44,6 +42,8 @@ class _HomeWidgetState extends State { late AudioPlayer _player; final url = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3'; late Stream _durationState; + var _labelLocation = TimeLabelLocation.below; + var _labelType = TimeLabelType.totalTime; @override void initState() { @@ -82,78 +82,134 @@ class _HomeWidgetState extends State { padding: const EdgeInsets.all(20.0), child: Column( children: [ - Wrap(children: [ - OutlinedButton( - child: Text('Light'), - onPressed: () { - themeNotifier.value = ThemeVariation(Colors.blue, Brightness.light); - }, - ), - OutlinedButton( - child: Text('Dark'), - onPressed: () { - themeNotifier.value = ThemeVariation(Colors.blue, Brightness.dark); - }, - ), - ]), + _themeButtons(), + _labelLocationButtons(), + _labelTypeButtons(), Spacer(), - StreamBuilder( - stream: _durationState, - builder: (context, snapshot) { - final durationState = snapshot.data; - final progress = durationState?.progress ?? Duration.zero; - final buffered = durationState?.buffered ?? Duration.zero; - final total = durationState?.total ?? Duration.zero; - return ProgressBar( - progress: progress, - buffered: buffered, - total: total, - onSeek: (duration) { - _player.seek(duration); - }, - ); - }, - ), - StreamBuilder( - stream: _player.playerStateStream, - builder: (context, snapshot) { - final playerState = snapshot.data; - final processingState = playerState?.processingState; - final playing = playerState?.playing; - if (processingState == ProcessingState.loading || - processingState == ProcessingState.buffering) { - return Container( - margin: EdgeInsets.all(8.0), - width: 32.0, - height: 32.0, - child: CircularProgressIndicator(), - ); - } else if (playing != true) { - return IconButton( - icon: Icon(Icons.play_arrow), - iconSize: 32.0, - onPressed: _player.play, - ); - } else if (processingState != ProcessingState.completed) { - return IconButton( - icon: Icon(Icons.pause), - iconSize: 32.0, - onPressed: _player.pause, - ); - } else { - return IconButton( - icon: Icon(Icons.replay), - iconSize: 32.0, - onPressed: () => _player.seek(Duration.zero), - ); - } - }, - ), + _progressBar(), + _playButton(), ], ), ), ); } + + Wrap _themeButtons() { + return Wrap(children: [ + OutlinedButton( + child: Text('light'), + onPressed: () { + themeNotifier.value = ThemeVariation(Colors.blue, Brightness.light); + }, + ), + OutlinedButton( + child: Text('dark'), + onPressed: () { + themeNotifier.value = ThemeVariation(Colors.blue, Brightness.dark); + }, + ), + ]); + } + + Wrap _labelLocationButtons() { + return Wrap(children: [ + OutlinedButton( + child: Text('below'), + onPressed: () { + setState(() => _labelLocation = TimeLabelLocation.below); + }, + ), + OutlinedButton( + child: Text('sides'), + onPressed: () { + setState(() => _labelLocation = TimeLabelLocation.sides); + }, + ), + OutlinedButton( + child: Text('none'), + onPressed: () { + setState(() => _labelLocation = TimeLabelLocation.none); + }, + ), + ]); + } + + Wrap _labelTypeButtons() { + return Wrap(children: [ + OutlinedButton( + child: Text('total time'), + onPressed: () { + setState(() => _labelType = TimeLabelType.totalTime); + }, + ), + OutlinedButton( + child: Text('remaining time'), + onPressed: () { + setState(() => _labelType = TimeLabelType.remainingTime); + }, + ), + ]); + } + + StreamBuilder _progressBar() { + return StreamBuilder( + stream: _durationState, + builder: (context, snapshot) { + final durationState = snapshot.data; + final progress = durationState?.progress ?? Duration.zero; + final buffered = durationState?.buffered ?? Duration.zero; + final total = durationState?.total ?? Duration.zero; + return ProgressBar( + progress: progress, + buffered: buffered, + total: total, + onSeek: (duration) { + _player.seek(duration); + }, + timeLabelLocation: _labelLocation, + timeLabelType: _labelType, + ); + }, + ); + } + + StreamBuilder _playButton() { + return StreamBuilder( + stream: _player.playerStateStream, + builder: (context, snapshot) { + final playerState = snapshot.data; + final processingState = playerState?.processingState; + final playing = playerState?.playing; + if (processingState == ProcessingState.loading || + processingState == ProcessingState.buffering) { + return Container( + margin: EdgeInsets.all(8.0), + width: 32.0, + height: 32.0, + child: CircularProgressIndicator(), + ); + } else if (playing != true) { + return IconButton( + icon: Icon(Icons.play_arrow), + iconSize: 32.0, + onPressed: _player.play, + ); + } else if (processingState != ProcessingState.completed) { + return IconButton( + icon: Icon(Icons.pause), + iconSize: 32.0, + onPressed: _player.pause, + ); + } else { + return IconButton( + icon: Icon(Icons.replay), + iconSize: 32.0, + onPressed: () => _player.seek(Duration.zero), + ); + } + }, + ); + } } class DurationState { diff --git a/example/pubspec.lock b/example/pubspec.lock index 0fd2668..94fa1f3 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -21,7 +21,7 @@ packages: path: ".." relative: true source: path - version: "0.3.2" + version: "0.4.0" boolean_selector: dependency: transitive description: @@ -113,7 +113,7 @@ packages: name: just_audio url: "https://pub.dartlang.org" source: hosted - version: "0.7.0" + version: "0.7.4" just_audio_platform_interface: dependency: transitive description: @@ -127,7 +127,7 @@ packages: name: just_audio_web url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.3.1" matcher: dependency: transitive description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index eb01675..9df1b67 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: sdk: flutter audio_video_progress_bar: path: ../ - just_audio: ^0.7.0 + just_audio: ^0.7.4 rxdart: ^0.26.0 dev_dependencies: diff --git a/lib/audio_video_progress_bar.dart b/lib/audio_video_progress_bar.dart index da32fe7..19ceb55 100644 --- a/lib/audio_video_progress_bar.dart +++ b/lib/audio_video_progress_bar.dart @@ -13,6 +13,14 @@ enum TimeLabelLocation { none, } +/// The right time label can be shown as the [totalTime] or as the +/// [remainingTime]. If the choice is [remainingTime] then this will be shown +/// as a negative number. +enum TimeLabelType { + totalTime, + remainingTime, +} + /// A progress bar widget to show or set the location of the currently /// playing audio or video content. /// @@ -41,6 +49,7 @@ class ProgressBar extends LeafRenderObjectWidget { this.thumbGlowColor, this.thumbGlowRadius = 30.0, this.timeLabelLocation, + this.timeLabelType, this.timeLabelTextStyle, }) : super(key: key); @@ -113,6 +122,12 @@ class ProgressBar extends LeafRenderObjectWidget { /// put them on the sides or remove them altogether. final TimeLabelLocation? timeLabelLocation; + /// What to display for the time label on the right + /// + /// The right time label can show the total time or the remaining time as a + /// negative number. The default is [TimeLabelType.totalTime]. + final TimeLabelType? timeLabelType; + /// The [TextStyle] used by the time labels. /// /// By default it is [TextTheme.bodyText1]. @@ -138,6 +153,7 @@ class ProgressBar extends LeafRenderObjectWidget { thumbGlowColor ?? (thumbColor ?? primaryColor).withAlpha(80), thumbGlowRadius: thumbGlowRadius, timeLabelLocation: timeLabelLocation ?? TimeLabelLocation.below, + timeLabelType: timeLabelType ?? TimeLabelType.totalTime, timeLabelTextStyle: textStyle, ); } @@ -163,6 +179,7 @@ class ProgressBar extends LeafRenderObjectWidget { thumbGlowColor ?? (thumbColor ?? primaryColor).withAlpha(80) ..thumbGlowRadius = thumbGlowRadius ..timeLabelLocation = timeLabelLocation ?? TimeLabelLocation.below + ..timeLabelType = timeLabelType ?? TimeLabelType.totalTime ..timeLabelTextStyle = textStyle; } @@ -184,6 +201,7 @@ class ProgressBar extends LeafRenderObjectWidget { properties.add(DoubleProperty('thumbGlowRadius', thumbGlowRadius)); properties .add(StringProperty('timeLabelLocation', timeLabelLocation.toString())); + properties.add(StringProperty('timeLabelType', timeLabelType.toString())); properties .add(DiagnosticsProperty('timeLabelTextStyle', timeLabelTextStyle)); } @@ -204,6 +222,7 @@ class _RenderProgressBar extends RenderBox { required Color thumbGlowColor, double thumbGlowRadius = 30.0, required TimeLabelLocation timeLabelLocation, + required TimeLabelType timeLabelType, TextStyle? timeLabelTextStyle, }) : _progress = progress, _total = total, @@ -218,6 +237,7 @@ class _RenderProgressBar extends RenderBox { _thumbGlowColor = thumbGlowColor, _thumbGlowRadius = thumbGlowRadius, _timeLabelLocation = timeLabelLocation, + _timeLabelType = timeLabelType, _timeLabelTextStyle = timeLabelTextStyle { _drag = HorizontalDragGestureRecognizer() ..onStart = _onDragStart @@ -302,8 +322,15 @@ class _RenderProgressBar extends RenderBox { } TextPainter _rightTimeLabel() { - final text = _getTimeString(total); - return _layoutText(text); + switch (timeLabelType) { + case TimeLabelType.totalTime: + final text = _getTimeString(total); + return _layoutText(text); + case TimeLabelType.remainingTime: + final remaining = total - progress; + final text = '-${_getTimeString(remaining)}'; + return _layoutText(text); + } } TextPainter _layoutText(String text) { @@ -428,6 +455,18 @@ class _RenderProgressBar extends RenderBox { markNeedsLayout(); } + /// What to display for the time label on the right + /// + /// The right time label can show the total time or the remaining time as a + /// negative number. The default is [TimeLabelType.totalTime]. + TimeLabelType get timeLabelType => _timeLabelType; + TimeLabelType _timeLabelType; + set timeLabelType(TimeLabelType value) { + if (_timeLabelType == value) return; + _timeLabelType = value; + markNeedsLayout(); + } + /// The text style for the duration text labels. By default this style is /// taken from the theme's [textStyle.bodyText1]. TextStyle? get timeLabelTextStyle => _timeLabelTextStyle; @@ -545,7 +584,7 @@ class _RenderProgressBar extends RenderBox { final leftTimeLabel = _leftTimeLabel(); leftTimeLabel.paint(canvas, labelOffset); - // total time label + // total or remaining time label final rightTimeLabel = _rightTimeLabel(); final rightLabelDx = size.width - padding - rightTimeLabel.width; final rightLabelOffset = Offset(rightLabelDx, barHeight); @@ -573,7 +612,7 @@ class _RenderProgressBar extends RenderBox { final currentLabelOffset = Offset(0, verticalOffset); leftTimeLabel.paint(canvas, currentLabelOffset); - // total time label + // total or remaining time label final totalLabelDx = size.width - rightTimeLabel.width; final totalLabelOffset = Offset(totalLabelDx, verticalOffset); rightTimeLabel.paint(canvas, totalLabelOffset); diff --git a/pubspec.yaml b/pubspec.yaml index d0852a3..fb8bfde 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: audio_video_progress_bar description: A progress bar widget to show or change the position of an audio or video stream. -version: 0.3.2 +version: 0.4.0 homepage: https://github.com/suragch/audio_video_progress_bar environment: diff --git a/test/audio_video_progress_bar_test.dart b/test/audio_video_progress_bar_test.dart index 474908c..59cbdfd 100644 --- a/test/audio_video_progress_bar_test.dart +++ b/test/audio_video_progress_bar_test.dart @@ -34,6 +34,7 @@ void main() { thumbGlowColor: Color(0x00000000), thumbGlowRadius: 50.0, timeLabelLocation: TimeLabelLocation.sides, + timeLabelType: TimeLabelType.remainingTime, timeLabelTextStyle: const TextStyle(color: Color(0x00000000)), ), ); @@ -54,6 +55,7 @@ void main() { expect(progressBar.thumbGlowColor, Color(0x00000000)); expect(progressBar.thumbGlowRadius, 50.0); expect(progressBar.timeLabelLocation, TimeLabelLocation.sides); + expect(progressBar.timeLabelType, TimeLabelType.remainingTime); expect(progressBar.timeLabelTextStyle, const TextStyle(color: Color(0x00000000))); });