- Summarize the problem
I need help finding a Flutter construct which will allow me to repeatedly check the return value of an API function call for getting the current time of a playing video (position). My goal is to update the Slider() to the position of the current video. I am using plugin flutter_vlc_player for playing back videos.
The most important part of the code below is the videoPlayerController.getPosition() function call. I need a way to repeatedly call this function and get the latest value. This is what I am struggling with.
The SeekBar class instantiated is the Slider() I am updating.
I think I am close to a solution as StreamBuilder is meant to update based on events. Also if I perform hot refresh of app after playing a video, the Slider updates once.
What I am seeing is the stream function is called twice but returns null each time because the video isn't playing yet. I need the stream function to be called while the video is playing.
I/flutter (29465): snapshot: null
I/flutter (29465): snapshot: null
One last thing: videoPlayerController.getPosition() is a Future.
- Describe what you've tried:
I tried using StreamBuilder() and FutureBuilder() but I got the same results. The current position is only fetched twice when I need it to be continuously fetched during video playback. I checked the Flutter documentation on StreamBuilder but their example only shows when there is one item to be grabbed and not multiple. I need to rebuild the Slider() widget based on the value returned from function repeatedly.
- Show some code:
VlcPlayerController videoPlayerController = VlcPlayerController.network(
'rtsp://ip_addr:8554/test',
hwAcc: HwAcc.FULL,
autoPlay: true,
options: VlcPlayerOptions(
video: VlcVideoOptions(),
rtp: VlcRtpOptions(['--rtsp-tcp'],),
extras: ['--h264-fps=30']
),
);
await Navigator.push(context,
MaterialPageRoute(builder: (context) =>
Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.blueAccent,
title: const Text("Playback")),
body: Center(
child: Column(
children: [
VlcPlayer(
controller: videoPlayerController,
aspectRatio: 16/9,
placeholder: const Center(child: CircularProgressIndicator()),
),
StatefulBuilder(builder: (context, setState) {
return Row(
children: [
TextButton(
child: Icon(isPlaying ? Icons.play_arrow : Icons.pause),
style: ButtonStyle(backgroundColor: MaterialStateProperty.all<Color>(Colors.blueAccent),
foregroundColor: MaterialStateProperty.all<Color>(Colors.white)),
onPressed: () {
setState(() {
if(videoPlayerController.value.isPlaying)
{
isPlaying = true;
videoPlayerController.pause();
}
else {
isPlaying = false;
videoPlayerController.play();
}
});
}
),
Text("${videoPlayerController.value.position.inMinutes}:${videoPlayerController.value.position.inSeconds}",
style: const TextStyle(color: Colors.white)),
],
);
}),
StreamBuilder<Duration>(
stream: Stream.fromFuture(videoPlayerController.getPosition()),
builder: (BuildContext context, AsyncSnapshot <Duration> snapshot) {
Duration position = snapshot.data ?? const Duration();
print('snapshot: ${snapshot.data?.inSeconds.toDouble()}');
return Column(
children: [
SeekBar(
duration: const Duration(seconds: 5),
position: position,
onChangeEnd: (newPosition)
{
videoPlayerController.seekTo(newPosition);
},
)
],
);
}
),
]
)
),
)
)
);
Thank you for reading and help. I am still learning Flutter/Dart so any references to helpful classes will be great.
CodePudding user response:
Is there a reason you want to use StreamBuilder in this case? You can use a StatefulWidget and add a listener to the controller. Then update the position inside that listener.
Use this to add listener:
videoPlayerController.addListener(updateSeeker);
Also make sure to remove the listener in dispose method:
videoPlayerController.removeListener(updateSeeker);
Here is the updateSeeker method:
Future<void> updateSeeker() async {
final newPosition = await videoPlayerController.getPosition();
setState(() {
position = newPosition;
});
}
Here is an example of a widget that plays the video and shows its position in a Text widget:
class VideoPlayer extends StatefulWidget {
const VideoPlayer({Key? key}) : super(key: key);
@override
_VideoPlayerState createState() => _VideoPlayerState();
}
class _VideoPlayerState extends State<VideoPlayer> {
final videoPlayerController = VlcPlayerController.network(url);
var position = Duration.zero;
@override
void initState() {
super.initState();
videoPlayerController.addListener(updateSeeker);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
VlcPlayer(
aspectRatio: 16 / 9,
controller: videoPlayerController,
),
Text(position.toString()),
],
),
);
}
Future<void> updateSeeker() async {
final newPosition = await videoPlayerController.getPosition();
setState(() {
position = newPosition;
});
}
@override
void dispose() {
videoPlayerController.removeListener(updateSeeker);
super.dispose();
}
}
