一个简单易用的而且可高度自定义的播放器。
闲话不多说我们直接上“干货”。
Q:播放器就播放器,为什么要强调上高度自由呢? A:之所以强调高度自由是因为播放器上面的能见的元素你都可以去更改,同时提供很多的回调可以进行自定义处理。主要还是体现在了自定义这个方面。(你可以拿它去仿 blibli ,腾讯播放器等等)
Q:高度自由体现在哪些功能或者配置上面?
A:最能体现自由的是播放器的自定义拓展元素功能,但是往往很多集成的开发者都忽略了这一点,因为这个确实很方便,但是很多播放器都没能做到这一点;其次,利用自定义顶部控制拦可以添加很多 actions
操作,通过自定义拓展元素这个功能来辅助实现;最后,自定义底部控制拦可以添加或减少控制元素,更改进度条的样式等等。
Q:播放器目前具备哪些能力? A:目前还在完善当中,但是功能基本都已经完善了,剩下的就是根据开发者的反馈去优化和完善了,下面是我罗列的功能列表:
通过上图我们可以看一下播放器都有哪些属性呢?
属性 | 类型 | 描述 |
---|---|---|
dataSource | String | 视频URL或媒体文件的路径 |
playOptions | VideoPlayOptions | 视频播放自定义配置,包含是否自动播放,是否循环播放等(详情见下方的Useage) |
videoStyle | VideoStyle | 视频播放器自定义样式,自定义顶部控制拦样式、自定义底部控制拦样式、自定义Loading样式等(详情见下方的Useage) |
children | List\<Widget> | 自定义拓展的元素,需要使用 Widget Align (字幕、弹幕、广告、封面等其他自定义元素) |
回调方法 | 类型 | 描述 |
---|---|---|
oninit | VideoCallback\<VideoPlayerController> | 初始化完成回调 |
onplay | VideoCallback\<VideoPlayerValue> | 视频开始播放的回调 |
onpause | VideoCallback\<VideoPlayerValue> | 视频暂停播放回调 |
ontimeupdate | VideoCallback\<VideoPlayerValue> | 视频播放进度回调(通过返回的value进行字幕匹配) |
onend | VideoCallback\<VideoPlayerValue> | 视频播放结束回调 |
onvolume | VideoCallback\<double> | 播放声音大小变化回调 |
onbrightness | VideoCallback\<double> | 屏幕亮度变化回调 |
onpop | VideoCallback\<VideoPlayerValue> | 顶部控制栏返回按钮点击回调 |
onnetwork | VideoCallback\<String> | 网络变化回调 |
onfullscreen | VideoCallback\<bool> | 视频是否全屏回调 |
onprogressdrag | VideoProgressDragHandle | 进度条被拖拽的回调 |
属性 | 类型 | 描述 |
---|---|---|
startPosition | Duration | 开始播放节点,例如:Duration(seconds: 0)) |
loop | bool | 是否循环播放 |
seekSeconds | num | 设置视频快进/快退单位秒数,默认为15s |
progressGestureUnit | num | 设置(横向)手势调节视频进度的秒数单位,默认为1s |
volumeGestureUnit | num | 设置(右侧垂直)手势调节视频音量的单位,必须为0~1之间(不能小于0,不能大于1),默认为0.01 |
brightnessGestureUnit | num | 设置(左侧垂直)手势调节视频亮度的单位,必须为0~1之间(不能小于0,不能大于1),默认为0.01 |
autoplay | bool | 是否自动播放 |
aspectRatio | num | 视频播放比例,例如:16/9 或者 4/3 |
allowScrubbing | bool | 是否允许进度条拖拽 |
属性 | 类型 | 描述 |
---|---|---|
playIcon | Widget | 视频暂停播放时中央显示的图标,showPlayIcon为false 时,该属性设置无效。 |
replayIcon | Widget | 视频结束时中央显示的重播图标,showReplayIcon为false 时,该属性设置无效。 |
showPlayIcon | bool | 暂停时是否显示播放按钮 |
showReplayIcon | bool | 视频播放结束时是否显示重播按钮 |
videoTopBarStyle | VideoTopBarStyle | 视频顶部自定义样式(详情见下方的Useage) |
videoControlBarStyle | VideoControlBarStyle | 控制栏自定义样式(详情见下方的Useage) |
videoSubtitlesStyle | VideoSubtitles | 字幕自定义样式(详情见下方的Useage) |
videoLoadingStyle | VideoLoadingStyle | 视频缓冲或者初始化Loading的自定义样式(详情见下方的Useage) |
属性 | 类型 | 描述 |
---|---|---|
show | bool | 是否显示控制栏 |
barBackgroundColor | Color | 控制栏背景颜色,默认为Color.fromRGBO(0, 0, 0, 0.5) |
height | double | 自定义控制栏高度 |
padding | EdgeInsets | 自定义边距 |
popIcon | Widget | 自定义返回按钮 |
contents | List\<Widget> | 拓展控制栏中部元素(宽度自适应: Row 中的 Expanded ) |
actions | List\<Widget> | 拓展控制栏右侧控制元素 |
customBar | Widget | 重写控制栏(如果设置了customBar , 除show 属性意外上方属性均不生效),仅支持Align 和Positioned |
属性 | 类型 | 描述 |
---|---|---|
height | double | 控制栏高度,默认为30 |
padding | EdgeInsets | 控制栏内边距,默认为EdgeInsets.symmetric(vertical: 8, horizontal: 10) |
progressStyle | VideoProgressStyle | 自定义控制拦进度条样式 |
barBackgroundColor | Color | 控制栏背景颜色,默认为Color.fromRGBO(0, 0, 0, 0.5) |
timePadding | EdgeInsets | 视频时间的内边距,默认为EdgeInsets.symmetric(horizontal: 5) |
timeFontSize | double | 视频时间的字体大小,默认为8 |
timeFontColor | Color | 视频时间的颜色,默认为Color.fromRGBO(255, 255, 255, 1) |
playIcon | Widget | 控制栏播放图标(下图3 详细说明) |
pauseIcon | Widget | 控制栏暂停图标(下图3 详细说明) |
rewindIcon | Widget | 控制栏快退图标(下图3 详细说明) |
forwardIcon | Widget | 控制栏快进图标(下图3 详细说明) |
fullscreenIcon | Widget | 控制栏全屏图标(下图3 详细说明) |
fullscreenExitIcon | Widget | 控制栏取消全屏图标(下图3 详细说明) |
itemList | List\<String> | 控制栏自定义功能(下图4 详细说明),默认为["rewind", "play", "forward", "position-time", "progress", "duration-time", "fullscreen"]。如果我们需要调整控制栏显示的顺序,仅需要调整 list 中字符串的顺序,如果需要删减,直接从 list 中移除改字符串即可,例如移除快进和快退,则讲 list 设置为 ["play", "progress","position-time", "progress", "duration-time", "fullscreen"] 即可。后面会陆续开放自定义元素,也就是你把你的元素加入到 list 中。 |
属性 | 类型 | 描述 |
---|---|---|
padding | EdgeInsets | 进度条边距(itemList 中包含progress 有效) |
height | double | 进度条高度(itemList 中包含progress 有效) |
dragHeight | double | 进度条拖拽按钮高度(itemList 中包含progress 有效) |
progressRadius | double | 进度条圆角(itemList 中包含progress 有效) |
playedColor | Color | 已播放的进度条颜色(见下图) |
bufferedColor | Color | 已缓冲的进度条颜色(见下图) |
backgroundColor | Color | 进度条背景颜色(见下图) |
dragBarColor | Color | 进度条拖拽按钮演示(itemList 中包含progress 有效) |
属性 | 类型 | 描述 |
---|---|---|
rewind | String | 快退功能,对应VideoControlBarStyle 的rewindIcon 图标 |
play | String | 播放/暂停功能,对应VideoControlBarStyle 的playIcon pauseIcon 图标 |
forward | String | 快进功能,对应VideoControlBarStyle 的forwardIcon 图标 |
progress | String | 线条形进度条(与‘basic-progress’二选一),由VideoControlBarStyle 的progressStyle 控制样式 |
basic-progress | String | 矩形进度条(与‘progress’二选一),由VideoControlBarStyle 的progressStyle 控制样式 |
time | String | 时间格式:当前时间/视频总时长(与position-time 和duration-time 二选一),由VideoControlBarStyle 的timePadding timeFontSize timeFontColor 控制样式 |
position-time | String | 当前播放时间,样式控制与time 相同 |
duration-time | String | 视频总时长,样式控制与time 相同 |
fullscreen | String | 全屏/小屏功能,对应VideoControlBarStyle 的 fullscreenIcon fullscreenExitIcon 图标 |
添加依赖,打开根目录的pubspec.yaml
文件,在dependencies:
下面添加以下代码
awsome_video_player: #latest
安装依赖(如果已经自动安装请忽略)
cd 项目目录
flutter packages get
在页面中引入库
import 'package:awsome_video_player/awsome_video_player.dart';
这是一个完整的DEMO
main.dart
import 'package:flutter/material.dart';
import 'package:awsome_video_player/awsome_video_player.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String mainSubtitles = "主字幕";
String subSubtitles = "辅字幕";
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Awsome video player'),
),
body: Center(
child: AwsomeVideoPlayer(
"https://www.runoob.com/try/demo_source/movie.mp4",
/// 视频播放配置
playOptions: VideoPlayOptions(
seekSeconds: 30,
aspectRatio: 16 / 9,
loop: true,
autoplay: true,
allowScrubbing: true,
startPosition: Duration(seconds: 0)),
/// 自定义视频样式
videoStyle: VideoStyle(
/// 自定义视频暂停时视频中部的播放按钮
playIcon: Icon(
Icons.play_circle_outline,
size: 100,
color: Colors.white,
),
/// 暂停时是否显示视频中部播放按钮
showPlayIcon: true,
/// 自定义底部控制栏
videoControlBarStyle: VideoControlBarStyle(
/// 自定义颜色
//barBackgroundColor: Colors.blue,
/// 自定义进度条样式
progressStyle: VideoProgressStyle(
// padding: EdgeInsets.all(0),
playedColor: Colors.red,
bufferedColor: Colors.yellow,
backgroundColor: Colors.green,
dragBarColor: Colors.white,//进度条为`progress`时有效,如果时`basic-progress`则无效
height: 4,
progressRadius: 2,//进度条为`progress`时有效,如果时`basic-progress`则无效
dragHeight: 5//进度条为`progress`时有效,如果时`basic-progress`则无效
),
/// 更改进度栏的播放按钮
playIcon: Icon(
Icons.play_arrow,
color: Colors.white,
size: 16
),
/// 更改进度栏的暂停按钮
pauseIcon: Icon(
Icons.pause,
color: Colors.red,
size: 16,
),
/// 更改进度栏的快退按钮
rewindIcon: Icon(
Icons.replay_30,
size: 16,
color: Colors.white,
),
/// 更改进度栏的快进按钮
forwardIcon: Icon(
Icons.forward_30,
size: 16,
color: Colors.white,
),
/// 更改进度栏的全屏按钮
fullscreenIcon: Icon(
Icons.fullscreen,
size: 16,
color: Colors.white,
),
/// 更改进度栏的退出全屏按钮
fullscreenExitIcon: Icon(
Icons.fullscreen_exit,
size: 16,
color: Colors.red,
),
/// 决定控制栏的元素以及排序,示例见上方图3
itemList: [
"rewind",
"play",
"forward",
"position-time", //当前播放时间
"progress",//线形进度条
//"basic-progress",//矩形进度条
"duration-time", //视频总时长
// "time", //格式:当前时间/视频总时长
"fullscreen"
],
),
/// 自定义字幕
videoSubtitlesStyle: VideoSubtitles(
mianTitle: Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: EdgeInsets.fromLTRB(10, 0, 10, 30),
child: Text(
mainSubtitles,
maxLines: 2,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white, fontSize: 14)),
),
),
subTitle: Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: EdgeInsets.all(10),
child: Text(
subSubtitles,
maxLines: 2,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white, fontSize: 14)),
),
),
),
),
/// 自定义拓展元素
children: [
/// 这个将会覆盖的视频内容,因为这个层级是最高级,因此手势会失效(慎用)
/// 这个可以用来做视频广告
// Positioned(
// top: 0,
// left: 0,
// bottom: 0,
// right: 0,
// child: Text("data", style: TextStyle(color: Colors.white),),
// ),
],
/// 视频初始化完成回调
oninit: (controller) {
print("video oninit");
},
/// 视频播放回调
onplay: (value) {
print("video played");
},
/// 视频暂停回调
onpause: (value) {
print("video paused");
},
/// 视频播放结束回调
onended: (value) {
print("video ended");
},
/// 视频播放进度回调
/// 可以用来匹配字幕
ontimeupdate: (value) {
print("timeupdate $value");
var position = value.position.inMilliseconds / 1000;
//根据 position 来判断当前显示的字幕
},
/// 声音变化回调
onvolume: (value) {
print("onvolume $value");
},
/// 亮度变化回调
onbrightness: (value) {
print("onbrightness $value");
},
/// 网络变化回调
onnetwork: (value) {
print("onbrightness $value");
},
/// 顶部控制栏点击返回按钮
onpop: (value) {
print("返回上一页");
},
),
),
),
);
}
}
首先我来看一下控制栏的图标自定义(见上图1):
DEMO: main.dart
import 'package:flutter/material.dart';
import 'package:awsome_video_player/awsome_video_player.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Awsome video player'),
),
body: Center(
child: AwsomeVideoPlayer(
"https://www.runoob.com/try/demo_source/movie.mp4",
/// 视频播放配置
playOptions: VideoPlayOptions(
seekSeconds: 30,
aspectRatio: 16 / 9,
loop: true,
autoplay: true,
allowScrubbing: true,
startPosition: Duration(seconds: 0)),
/// 自定义视频样式 - 请注意我要划重点了
videoStyle: VideoStyle(
/// 自定义底部控制栏 - 这是重点了
videoControlBarStyle: VideoControlBarStyle(
/// 更改进度栏的播放按钮
playIcon: Icon(
Icons.play_arrow,
color: Colors.white,
size: 16
),
/// 更改进度栏的暂停按钮
pauseIcon: Icon(
Icons.pause,
color: Colors.red,
size: 16,
),
/// 更改进度栏的快退按钮
rewindIcon: Icon(
Icons.replay_30,
size: 16,
color: Colors.white,
),
/// 更改进度栏的快进按钮
forwardIcon: Icon(
Icons.forward_30,
size: 16,
color: Colors.white,
),
/// 更改进度栏的全屏按钮
fullscreenIcon: Icon(
Icons.fullscreen,
size: 16,
color: Colors.white,
),
/// 更改进度栏的退出全屏按钮
fullscreenExitIcon: Icon(
Icons.fullscreen_exit,
size: 16,
color: Colors.red,
),
),
),
),
),
),
);
}
}
我们可以根据videoStyle
中videoControlBarStyle
来自定义控制栏的样式:调整顺序(见上方图3)只需要调整 itemList
中字符串的顺序即可;控制显示的元素,将不需要暂时的元素从 itemList
中删除即可。
DEMO: main.dart
import 'package:flutter/material.dart';
import 'package:awsome_video_player/awsome_video_player.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Awsome video player'),
),
body: Center(
child: AwsomeVideoPlayer(
"https://www.runoob.com/try/demo_source/movie.mp4",
/// 视频播放配置
playOptions: VideoPlayOptions(
seekSeconds: 30,
aspectRatio: 16 / 9,
loop: true,
autoplay: true,
allowScrubbing: true,
startPosition: Duration(seconds: 0)),
/// 自定义视频样式 - 请注意我要划重点了
videoStyle: VideoStyle(
/// 自定义底部控制栏 - 这是重点了
videoControlBarStyle: VideoControlBarStyle(
/// 决定控制栏的元素以及排序,示例见上方图3
itemList: [
"progress",// 这里将进度条前置了,因此有了图3的效果
"rewind",//如果需要删除快退按钮,将其注释或删除即可
"play",
"forward",
"time",
"fullscreen"
],
),
),
),
),
),
);
}
}
同样我们还是通过videoStyle
中videoControlBarStyle
来自定义进度条的颜色(见上方图3)。
DEMO: main.dart
import 'package:flutter/material.dart';
import 'package:awsome_video_player/awsome_video_player.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Awsome video player'),
),
body: Center(
child: AwsomeVideoPlayer(
"https://www.runoob.com/try/demo_source/movie.mp4",
/// 视频播放配置
playOptions: VideoPlayOptions(
seekSeconds: 30,
aspectRatio: 16 / 9,
loop: true,
autoplay: true,
allowScrubbing: true,
startPosition: Duration(seconds: 0)),
/// 自定义视频样式
videoStyle: VideoStyle(
/// 自定义底部控制栏
videoControlBarStyle: VideoControlBarStyle(
/// 自定义颜色
barBackgroundColor: Colors.blue,//控制栏的背景颜色
/// 自定义进度条样式
progressStyle: VideoProgressStyle(
// padding: EdgeInsets.all(0),
playedColor: Colors.red,
bufferedColor: Colors.yellow,
backgroundColor: Colors.green,
dragBarColor: Colors.white,//进度条为`progress`时有效,如果时`basic-progress`则无效
height: 4,
progressRadius: 2,//进度条为`progress`时有效,如果时`basic-progress`则无效
dragHeight: 5//进度条为`progress`时有效,如果时`basic-progress`则无效
),
),
),
),
),
),
);
}
}
通过videoStyle
中videoTopBarStyle
来自定义顶部控制栏。
DEMO
DEMO: main.dart
import 'package:flutter/material.dart';
import 'package:awsome_video_player/awsome_video_player.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String videoUrl = "https://www.runoob.com/try/demo_source/movie.mp4";
bool showAdvertCover = false;//是否显示广告
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Awsome video player'),
),
body: Center(
child: videoUrl != ""
? AwsomeVideoPlayer(
videoUrl,
/// 视频播放配置
playOptions: VideoPlayOptions(
seekSeconds: 30,
aspectRatio: 16 / 9,
loop: true,
autoplay: true,
allowScrubbing: true,
startPosition: Duration(seconds: 0)),
/// 自定义视频样式
videoStyle: VideoStyle(
/// 自定义顶部控制栏
videoTopBarStyle: VideoTopBarStyle(
show: true, //是否显示
height: 30,
padding:
EdgeInsets.symmetric(vertical: 8, horizontal: 10),
barBackgroundColor: Color.fromRGBO(0, 0, 0, 0.5),
popIcon: Icon(
Icons.arrow_back,
size: 16,
color: Colors.white,
),
contents: [
Center(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 10),
child: Text(
'123',
style: TextStyle(color: Colors.white),
),
),
)
], //自定义顶部控制栏中间显示区域
actions: [
GestureDetector(
onTap: () {
///1. 可配合自定义拓展元素使用,例如广告
setState(() {
showAdvertCover = true;
});
///
},
child: Icon(
Icons.more_horiz,
size: 16,
color: Colors.white,
),
)
], //自定义顶部控制栏右侧显示区域
/// 设置cusotmBar之后,以上属性均无效(除了`show`之外)
// customBar: Text("123123132")
),
),
/// 顶部控制栏点击返回按钮
onpop: (value) {
print("返回上一页");
},
)
: AspectRatio(
aspectRatio: 16 / 9,
child: Center(
child: CircularProgressIndicator(strokeWidth: 2.0),
),
),
),
),
);
}
}
视频如果需要横竖屏不能使用safeArea
顶部控制拦或者底部控制拦文字或者图标被裁剪,调整字体或图标大小,也可以通过调整控制拦高度
视频的 dataSoure
不能为空,为空时使用加载视图,否则播放器会报错
import 'package:flutter/material.dart';
import 'package:awsome_video_player/awsome_video_player.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String videoUrl = "https://www.runoob.com/try/demo_source/movie.mp4";
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Awsome video player'),
),
body: Center(
/// 一般videoUrl是从服务端返回,在没有返回来之前,
/// 我们可以使用加载视图来占位
child: videoUrl != "" ? AwsomeVideoPlayer(
videoUrl,
/// 视频播放配置
playOptions: VideoPlayOptions(
seekSeconds: 30,
aspectRatio: 16 / 9,
loop: true,
autoplay: true,
allowScrubbing: true,
startPosition: Duration(seconds: 0)),
) : AspectRatio(
aspectRatio: 16 / 9,
child: Center(
child: CircularProgressIndicator(strokeWidth: 2.0),
),
),
),
),
);
}
}
AwsomeVideoPlayer
下面的children
仅支持Align
和Positioned
,children的层级会高于下面,这个功能会持续更新,后面会陆续出一些针对自定义拓展的高阶文档。
从 Android9.0 (API 28)
起,应用默认禁止http请求,需要在AndroidManifest.xml
中配置以下内容
<application
...
android:usesCleartextTraffic="true"
...
>
开发过程中遇到问题,请通过以下方式联系我,我会第一时间回复你:
Copyright © 2020, Mark Chen. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.