Thumb initial position is always zero
Closed this issue · 11 comments
By default, the progress bar thumb is on position zero despite the fact that the progress value is set to different value. I tried to resolve this issue and I think this is because the double _thumbValue = 0.0
. It should be given a value in constructor. I tried to fix this and came with this solution:
audio_video_progress_bar.zip
P. S. I also implemented an isDraggable named parameter, in order to restrict the possibility to move the thumb if needed.
Could you give a code example that shows the error? (preferably as an in-answer code block or a link to a GitHub repo rather and a zip file) I'm not sure I really understand.
The isDraggable option sounds like a different issue. Could you open another issue for that one and explain your use case?
I think the bug that he is trying to show is the one on this video.
video_bug.mp4
To replicate it you just need to add the seekbar inside a TweenAnimationBuilder
, the code to replicated is below:
class AnimatedVisibility extends StatelessWidget {
final Duration duration;
final Widget child;
final bool visible;
const AnimatedVisibility({
Key? key,
required this.duration,
required this.child,
required this.visible,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return TweenAnimationBuilder<double>(
tween: Tween<double>(
begin: visible ? 0.0 : 1.0,
end: visible ? 1.0 : 0.0,
),
child: child,
duration: duration,
builder: (context, value, child) {
return Visibility(
visible: value != 0,
child: Opacity(
opacity: value,
child: child,
),
);
},
);
}
}
Align(
alignment: Alignment.bottomCenter,
child: AnimatedVisibility(
visible: _isSeekbarVisible,
duration: VideoPlayer.hideControlsAnimation,
child: Seekbar(
position: widget.videoPlayerController.progress,
onChanged: (progress) {
widget.videoPlayerController.setProgress(progress);
},
onChanging: _showSeekbarForAMoment,
),
),
)
class Seekbar extends StatefulWidget {
final Stream<DurationState> position;
final Color color;
final Function(Duration position) onChanged;
final VoidCallback? onChanging;
const Seekbar({
Key? key,
required this.position,
this.color = Colors.red,
required this.onChanged,
this.onChanging,
}) : super(key: key);
@override
_SeekbarState createState() => _SeekbarState();
}
class _SeekbarState extends State<Seekbar> {
double? changedPosition;
@override
Widget build(BuildContext context) {
return StreamBuilder<DurationState>(
stream: widget.position,
initialData: DurationState(
progress: Duration.zero,
total: Duration.zero,
buffered: Duration.zero,
),
builder: (context, snapshot) {
var videoProgress = snapshot.data ??
DurationState(
progress: Duration.zero,
buffered: Duration.zero,
total: Duration.zero,
);
return Padding(
padding: EdgeInsets.only(left: 12.0, right: 16.0, bottom: 16.0, top: 16),
child: ProgressBar(
progress: videoProgress.progress,
buffered: videoProgress.buffered,
total: videoProgress.total,
barHeight: 2.0,
progressBarColor: AppColors.orange_1,
baseBarColor: AppColors.whiteFF.withOpacity(0.4),
bufferedBarColor: AppColors.whiteFF,
thumbRadius: 6.0,
thumbColor: AppColors.orange_1,
thumbGlowRadius: 12.0,
thumbGlowColor: AppColors.orange_1.withOpacity(0.4),
timeLabelLocation: TimeLabelLocation.none,
onSeek: (duration) {
widget.onChanging?.call();
widget.onChanged(duration);
},
),
);
},
);
}
So the effect that is trying to be achieved is fade in and fade out for the progress bar, correct? I'm able to achieve that effect with AnimatedOpacity
like so:
StreamBuilder<DurationState> _progressBar() {
return StreamBuilder<DurationState>(
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 AnimatedOpacity(
opacity: _visibility,
duration: const Duration(milliseconds: 300),
child: ProgressBar(
progress: progress,
buffered: buffered,
total: total,
onSeek: (duration) {
_player.seek(duration);
},
timeLabelLocation: _labelLocation,
timeLabelType: _labelType,
timeLabelTextStyle: _labelStyle,
),
);
},
);
}
This is from a modified version of the example project. Here is what it looks like:
I don't get the thumb jumping to zero.
Theoretically the thumb is just a ratio of the progress to the total time. It should only be zero if the progress or the total time is zero. That might indicate an error with these values.
I have the same issue using BLoC
and ProgressBar
widget.
return BlocBuilder<ClipPlaybackBloc, ClipPlaybackState>(
builder: (context, state) {
return ProgressBar(
thumbRadius: 10,
timeLabelTextStyle: TextStyle(color: Colors.white),
timeLabelLocation: TimeLabelLocation.none,
baseBarColor: Colors.white.withOpacity(0.24),
bufferedBarColor: Colors.white.withOpacity(0.24),
thumbColor: Colors.white,
progressBarColor: Theme.of(context).accentColor,
progress: state.newDuration,
buffered: Duration(milliseconds: 2000),
total: Duration(milliseconds: 10000),
onSeek: (newDuration) {
_handleTimeChange(context, newDuration);
});
},
);
Everything works fine until I decide to hide/show ProgressBar widget. Please see attached gif:
If you add print(state.newDuration)
before the return
statement, what value does it show when you make the ProgressBar visible again?
I added logger for _handleTimeChange
method and output is as expected.
I/flutter ( 4708): 15:57:32.154 (+0:06:22.702952): Clip Playback Player Progress Bar: User selected new duration: 0:00:01.745000
I/flutter ( 4708): 15:57:33.818 (+0:06:24.367607): Clip Playback Player Progress Bar: User selected new duration: 0:00:07.686000
edit:
this is what I got before return
statement:
I/flutter ( 4708): 16:03:48.946 (+0:12:39.495752): Clip Playback Player Progress Bar: User selected new duration: 0:00:03.073000
I/flutter ( 4708): 16:03:50.130 (+0:12:40.679133): Clip Playback Player Progress Bar: User selected new duration: 0:00:08.285000
I managed to workaround this ussue by using following method instead of Visibility widget
return AnimatedOpacity(opacity: state.isFullScreen ? 0.0 : 1.0, duration: Duration(milliseconds: 500), child: child);
Several people have mentioned the problem when using the Visibility
widget. I'll need to look more into why that is. I'm glad AnimatedOpacity
works for you, though.
I suppose this is because thumb widget doesn't depend on progress value. You can remember state for progress
but widget can not recover thumb
position after it's being rebuilt.
@enseitankad0 Thanks for your comment. That put me on the right track to solving the problem.
I looked more into the Visibility
widget. I learned that by default it removes its child from the widget tree. This has the effect of losing any state that the child may have. For ProgressBar
the _thumbValue
was internal state. The problems above could have been solved if people had set the maintainState
parameter of Visibility
to true
. The AnimatedOpacity
widget, on the other hand, does not remove the widget from the tree, so the state is never lost, which is why that option also worked.
That explanation aside, I updated ProgressBar
so that _thumbValue
is initialized from the values of progress
and total
rather than a hard-coded 0.0
when the widget is first created. This removes the need to save state when hiding the widget, so Visibility
works now without needing to set maintainState
.
I've published the change as version 0.6.2
.
If anyone can confirm that it works I'll close this issue.
I'll close this issue for now but I can reopen it if there are any more problems related to it.