只要逻辑行得通,代码实现只是迟早的问题。产品经理想要视频轮播的功能,经过分析轮播的逻辑是很简单,只要当一个视频播放完成之后自动播放下一个视频,然后周而复始。前后也花了好几天的时间去尝试,皇天不负有心人,终于有所突破,在此记录一下,给需要的读者参考。
其中需要解决的问题如下:
1、如何获取播放完成的事件
2、在播放完成的事件触发之后如何更改视频源
代码中做注释会解答上面的1、2问题,其实在做的过程中还会遇到其它的棘手问题,尤其是状态改变之后会触发多次build,目前是通过标记位的方式来解决的,现在只是实现了两个视频轮播。
具体代码如下
import 'dart:async';
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
///视频加载组件
class VideoLoader extends StatefulWidget {
@override
_VideoLoader createState() {
return _VideoLoader();
}
}
class _VideoLoader extends State<VideoLoader> {
late VideoPlayerController _controller;
//视频源列表,读者在使用代码时需要将视频源换成你自己的视频源
List<String> _listVideoUrls = [
"http://192.168.30.34/ims/resource/17d87da3-4021-48b8-9c68-0d53d0c349df.mp4",
"http://192.168.30.34/ims/resource/17d87da3-4021-48b8-9c68.mp4"
];
//定义两个标记位用来保证setstate()中的函数只执行一次.
List<bool> flags = [false, false];
@override
void initState() {
super.initState();
playVideo1();
}
//播放视频源1
void playVideo1() async {
_controller = VideoPlayerController.network(_listVideoUrls.elementAt(0));
print('视频1加载完成');
_controller.addListener(() async {
Duration? res = await _controller.position;
setState(() {
if (res! >= _controller.value.duration) {
if (!flags.elementAt(0)) {
flags[0] = true;
print('视频1播放完成');
//移除当前_controller的监听事件
_controller.removeListener(() {});
_controller.dispose();
if (flags[1] == true) {
flags[1] = false;
print('初始化flags[1] 为false');
}
playVideo2();
}
}
});
});
_controller.setLooping(false);
_controller.initialize().then((_) => setState(() {}));
_controller.play();
}
void playVideo2() async {
_controller = VideoPlayerController.network(_listVideoUrls.elementAt(1));
print('视频2资源设置完成');
_controller.addListener(() async {
Duration? res = await _controller.position;
setState(() {
if (res! >= _controller.value.duration) {
// print('_controller.value.duration:${_controller.value.duration}');
if (!flags.elementAt(1)) {
flags[1] = true;
_controller.removeListener(() {});
_controller.dispose();
print('视频2播放完成');
if (flags[0] == true) {
flags[0] = false;
print('初始化flags[0] 为false');
}
playVideo1();
}
}
});
});
_controller.setLooping(false);
_controller.initialize().then((_) => setState(() {}));
_controller.play();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
print('build---------------');
return Scaffold(
// appBar: CupertinoNavigationBar(
// backgroundColor: Colors.white,
// middle: const Text('视频播放'),
// ),
body: Center(
child: Container(
// padding: const EdgeInsets.all(10.0),
alignment: Alignment.center,
child: GestureDetector(
onTap: () {},
child: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
VideoPlayer(_controller),
//播放倍速
_ControlsOverlay(controller: _controller),
//播放进度条
VideoProgressIndicator(_controller, allowScrubbing: true),
],
),
),
)),
);
}
}
class _ControlsOverlay extends StatelessWidget {
const _ControlsOverlay({Key? key, required this.controller})
: super(key: key);
static const _examplePlaybackRates = [
0.25,
0.5,
1.0,
1.5,
2.0,
3.0,
5.0,
10.0,
];
final VideoPlayerController controller;
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
AnimatedSwitcher(
duration: Duration(milliseconds: 50),
reverseDuration: Duration(milliseconds: 200),
child: controller.value.isPlaying
? SizedBox.shrink()
: Container(
color: Colors.black26,
child: Center(
child: Icon(
Icons.play_arrow,
color: Colors.white,
size: 100.0,
),
),
),
),
GestureDetector(
onTap: () {
controller.value.isPlaying ? controller.pause() : controller.play();
},
),
Align(
alignment: Alignment.topRight,
child: PopupMenuButton<double>(
initialValue: controller.value.playbackSpeed,
tooltip: 'Playback speed',
onSelected: (speed) {
controller.setPlaybackSpeed(speed);
},
itemBuilder: (context) {
return [
for (final speed in _examplePlaybackRates)
PopupMenuItem(
value: speed,
child: Text('${speed}x'),
)
];
},
child: Padding(
padding: const EdgeInsets.symmetric(
// Using less vertical padding as the text is also longer
// horizontally, so it feels like it would need more spacing
// horizontally (matching the aspect ratio of the video).
vertical: 12,
horizontal: 16,
),
child: Text('${controller.value.playbackSpeed}x'),
),
),
),
],
);
}
}
注意,如果项目中没有添加video_player包的话需要先在pubspec.yaml中添加。
添加方式
在项目中执行 flutter pub add video_player
添加完成之后 执行 flutter pub get命令。
添加好的pubspec.yaml结构如下:
依赖好之后就可以在项目中使用VideoPlayer相关资源了。
祝君成功,如有疑问可联系笔者交流!