提交 c24d98cd authored 作者: jungleiOS's avatar jungleiOS

loading 组件背景设为黑色,挡住亮度组件初始化时闪现问题,退出播放器恢复亮度

上级 2abe3aa0
......@@ -10,19 +10,60 @@ import 'package:gz_video_player/video_style.dart';
import 'package:gz_video_player/video_subtitles.dart';
import 'package:gz_video_player/video_top_bar_style.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
Widget build(BuildContext context) {
return const MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('HomePage'),
),
body: Center(
child: GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) {
return const VideoPlayerPage();
}),
);
},
child: Container(
width: 120.0,
height: 30.0,
color: Colors.blue,
alignment: Alignment.center,
child: const Text('to video player'),
),
),
),
);
}
}
class VideoPlayerPage extends StatefulWidget {
const VideoPlayerPage({super.key});
@override
State<VideoPlayerPage> createState() => _VideoPlayerPageState();
}
class _MyAppState extends State<MyApp> {
class _VideoPlayerPageState extends State<VideoPlayerPage> {
// String videoUrl = "https://www.runoob.com/try/demo_source/movie.mp4";
// String videoUrl = "https://yun.zxziyuan-yun.com/20180221/4C6ivf8O/index.m3u8";
......@@ -52,7 +93,7 @@ class _MyAppState extends State<MyApp> {
dataList = [
VideoModel(
url:
'https://mpv.videocc.net/8f38fa3352/5/8f38fa3352cdb65e6773173ae8c767b5_1.mp4',
'https://mpv.videocc.net/8f38fa3352/5/8f38fa3352cdb65e6773173ae8c767b5_1.mp4',
title: '高正视频',
),
VideoModel(
......@@ -65,404 +106,402 @@ class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar:
!_isFullscreen ? AppBar(title: const Text('video player')) : null,
body: Column(children: <Widget>[
videoUrl != ""
? GZVideoPlayer(
dataSource: videoUrl,
sourceType: DataSourceType.network,
/// 视频播放配置
playOptions: VideoPlayOptions(
seekSeconds: 30,
//左侧垂直手势调节视频亮度的单位(0~1之间,不能小于0,不能大于1)
brightnessGestureUnit: 0.01,
//右侧垂直手势调节视频音量的单位(0~1之间,不能小于0,不能大于1)
volumeGestureUnit: 0.005,
//横行手势调节视频进度的单位毫秒数
progressGestureUnit: 2000,
aspectRatio: 16 / 9,
loop: false,
autoplay: true,
allowScrubbing: true,
startPosition: const Duration(seconds: 0),
),
/// 自定义视频样式
videoStyle: VideoStyle(
/// 自定义视频暂停时视频中部的播放按钮
playIcon: const Icon(
Icons.play_circle_outline,
size: 60,
color: Colors.white,
),
return Scaffold(
appBar: !_isFullscreen ? AppBar(title: const Text('video player')) : null,
body: Column(children: <Widget>[
videoUrl != ""
? GZVideoPlayer(
dataSource: videoUrl,
sourceType: DataSourceType.network,
/// 视频播放配置
playOptions: VideoPlayOptions(
seekSeconds: 30,
//左侧垂直手势调节视频亮度的单位(0~1之间,不能小于0,不能大于1)
brightnessGestureUnit: 0.01,
//右侧垂直手势调节视频音量的单位(0~1之间,不能小于0,不能大于1)
volumeGestureUnit: 0.005,
//横行手势调节视频进度的单位毫秒数
progressGestureUnit: 2000,
aspectRatio: 16 / 9,
loop: false,
autoplay: true,
allowScrubbing: true,
startPosition: const Duration(seconds: 0),
),
/// 暂停时是否显示视频中部播放按钮
showPlayIcon: true,
videoLoadingStyle: VideoLoadingStyle(
/// 重写部分(二选一)
// 重写Loading的widget
// customLoadingIcon: CircularProgressIndicator(strokeWidth: 2.0),
// 重写Loading 下方的Text widget
// customLoadingText: Text("加载中..."),
/// 设置部分(二选一)
// 设置Loading icon 下方的文字
loadingText: "Loading...",
// 设置loading icon 下方的文字颜色
loadingTextFontColor: Colors.white,
// 设置loading icon 下方的文字大小
loadingTextFontSize: 20,
),
/// 自定义视频样式
videoStyle: VideoStyle(
/// 自定义视频暂停时视频中部的播放按钮
playIcon: const Icon(
Icons.play_circle_outline,
size: 60,
color: Colors.white,
),
/// 自定义顶部控制栏
videoTopBarStyle: VideoTopBarStyle(
show: true,
//是否显示
height: 60,
padding: const EdgeInsets.symmetric(
vertical: 8, horizontal: 10),
barBackgroundColor: const Color.fromRGBO(0, 0, 0, 0.5),
popIcon: const Icon(
Icons.arrow_back,
size: 25,
color: Colors.white,
),
contents: [
Center(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 10),
child: const Text(
'123',
style:
TextStyle(color: Colors.white, fontSize: 14),
),
),
)
],
//自定义顶部控制栏中间显示区域
actions: [
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
///1. 可配合自定义拓展元素使用,例如广告
setState(() {
showAdvertCover = true;
});
///
},
child: const Icon(
Icons.more_horiz,
/// 暂停时是否显示视频中部播放按钮
showPlayIcon: true,
videoLoadingStyle: VideoLoadingStyle(
/// 重写部分(二选一)
// 重写Loading的widget
// customLoadingIcon: CircularProgressIndicator(strokeWidth: 2.0),
// 重写Loading 下方的Text widget
// customLoadingText: Text("加载中..."),
/// 设置部分(二选一)
// 设置Loading icon 下方的文字
loadingText: "Loading...",
// 设置loading icon 下方的文字颜色
loadingTextFontColor: Colors.white,
// 设置loading icon 下方的文字大小
loadingTextFontSize: 20,
),
/// 自定义顶部控制栏
videoTopBarStyle: VideoTopBarStyle(
show: true,
//是否显示
height: 60,
padding:
const EdgeInsets.symmetric(vertical: 8, horizontal: 10),
barBackgroundColor: const Color.fromRGBO(0, 0, 0, 0.5),
popIcon: const Icon(
Icons.arrow_back,
size: 25,
color: Colors.white,
),
)
], //自定义顶部控制栏右侧显示区域
/// 设置cusotmBar之后,以上属性均无效(除了`show`之外)
// customBar: Positioned(
// top: 0,
// left: 0,
// right: 0,
// child: Container(
// width: double.infinity,
// height: 50,
// color: Colors.yellow,
// child: Text("12312312"),
// ),
// ),
// customBar: Align(
// alignment: Alignment.topLeft,
// child: Container(
// width: double.infinity,
// height: 30,
// color: Colors.yellow,
// child: GestureDetector(
// onTap: () {
// debugPrint("yes");
// },
// child: Text("123123132")
// )
// ),
// ),
),
contents: [
Center(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 10),
child: const Text(
'123',
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
)
],
//自定义顶部控制栏中间显示区域
actions: [
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
///1. 可配合自定义拓展元素使用,例如广告
setState(() {
showAdvertCover = true;
});
///
},
child: const Icon(
Icons.more_horiz,
size: 25,
color: Colors.white,
),
)
], //自定义顶部控制栏右侧显示区域
/// 设置cusotmBar之后,以上属性均无效(除了`show`之外)
// customBar: Positioned(
// top: 0,
// left: 0,
// right: 0,
// child: Container(
// width: double.infinity,
// height: 50,
// color: Colors.yellow,
// child: Text("12312312"),
// ),
// ),
// customBar: Align(
// alignment: Alignment.topLeft,
// child: Container(
// width: double.infinity,
// height: 30,
// color: Colors.yellow,
// child: GestureDetector(
// onTap: () {
// debugPrint("yes");
// },
// child: Text("123123132")
// )
// ),
// ),
),
/// 自定义底部控制栏
videoControlBarStyle: VideoControlBarStyle(
/// 自定义颜色
// barBackgroundColor: Colors.blue,
/// 自定义时间TextStyle
timeTextStyle: const TextStyle(fontSize: 14.0, color: Colors.white),
///添加边距
padding: const EdgeInsets.symmetric(
vertical: 8, horizontal: 10),
///设置控制拦的高度,默认为30,如果图标设置过大但是高度不够就会出现图标被裁剪的现象
height: 60,
progressStyle: VideoProgressStyle(
playedColor: Colors.blue,
),
/// 自定义底部控制栏
videoControlBarStyle: VideoControlBarStyle(
/// 自定义颜色
// barBackgroundColor: Colors.blue,
/// 自定义时间TextStyle
timeTextStyle:
const TextStyle(fontSize: 14.0, color: Colors.white),
///添加边距
padding:
const EdgeInsets.symmetric(vertical: 8, horizontal: 10),
///设置控制拦的高度,默认为30,如果图标设置过大但是高度不够就会出现图标被裁剪的现象
height: 60,
progressStyle: VideoProgressStyle(
playedColor: Colors.blue,
),
/// 自定义进度条样式
// progressStyle: VideoProgressStyle(
// // padding: EdgeInsets.all(0),
// padding: EdgeInsets.symmetric(
// vertical: 0,
// horizontal: 10), //vertical不能设置太大,不然被把进度条压缩肉眼无法识别
// 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: const Icon(Icons.play_arrow,
color: Colors.white, size: 25),
/// 更改进度栏的暂停按钮
pauseIcon: const Icon(
Icons.pause,
color: Colors.white,
size: 25,
),
/// 自定义进度条样式
// progressStyle: VideoProgressStyle(
// // padding: EdgeInsets.all(0),
// padding: EdgeInsets.symmetric(
// vertical: 0,
// horizontal: 10), //vertical不能设置太大,不然被把进度条压缩肉眼无法识别
// 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: const Icon(Icons.play_arrow,
color: Colors.white, size: 25),
/// 更改进度栏的暂停按钮
pauseIcon: const Icon(
Icons.pause,
color: Colors.white,
size: 25,
),
/// 更改进度栏的快退按钮
rewindIcon: const Icon(
Icons.replay_30,
size: 25,
color: Colors.white,
),
/// 更改进度栏的快退按钮
rewindIcon: const Icon(
Icons.replay_30,
size: 25,
color: Colors.white,
),
/// 更改进度栏的快进按钮
forwardIcon: const Icon(
Icons.forward_30,
size: 25,
color: Colors.white,
),
/// 更改进度栏的快进按钮
forwardIcon: const Icon(
Icons.forward_30,
size: 25,
color: Colors.white,
),
/// 更改进度栏的全屏按钮
fullscreenIcon: const Icon(
Icons.fullscreen,
size: 25,
color: Colors.white,
),
/// 更改进度栏的全屏按钮
fullscreenIcon: const Icon(
Icons.fullscreen,
size: 25,
color: Colors.white,
),
/// 更改进度栏的退出全屏按钮
fullscreenExitIcon: const Icon(
Icons.fullscreen_exit,
size: 25,
color: Colors.white,
),
/// 更改进度栏的退出全屏按钮
fullscreenExitIcon: const Icon(
Icons.fullscreen_exit,
size: 25,
color: Colors.white,
),
/// 决定控制栏的元素以及排序,示例见上方图3
itemList: [
"rewind",
"play",
"forward",
"position-time", //当前播放时间
"progress", //线条形进度条(与‘basic-progress’二选一)
// "basic-progress",//矩形进度条(与‘progress’二选一)
"duration-time", //视频总时长
// "time",//格式:当前时间/视频总时长
'speed',
"fullscreen"
],
),
/// 决定控制栏的元素以及排序,示例见上方图3
itemList: [
"rewind",
"play",
"forward",
"position-time", //当前播放时间
"progress", //线条形进度条(与‘basic-progress’二选一)
// "basic-progress",//矩形进度条(与‘progress’二选一)
"duration-time", //视频总时长
// "time",//格式:当前时间/视频总时长
'speed',
"fullscreen"
],
),
/// 自定义字幕
videoSubtitlesStyle: VideoSubtitles(
mainTitle: Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 30),
child: Text(mainSubtitles,
maxLines: 2,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white, fontSize: 14)),
/// 自定义字幕
videoSubtitlesStyle: VideoSubtitles(
mainTitle: Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 30),
child: Text(mainSubtitles,
maxLines: 2,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white, fontSize: 14)),
),
),
subTitle: Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.all(10),
child: Text(subSubtitles,
maxLines: 2,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white, fontSize: 14)),
),
),
),
),
subTitle: Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.all(10),
child: Text(subSubtitles,
maxLines: 2,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white, fontSize: 14)),
),
/// 自定义拓展元素
children: [
/// DEMO1 自定义视频播放状态Toast
/// 待完善
/// DEMO2 这个将会覆盖的视频内容,因为这个层级是最高级,因此手势会失效(慎用)
/// 这个可以用来做视频广告
showAdvertCover
? Align(
alignment: Alignment.center,
child: Container(
width: 200,
height: 100,
color: Colors.blue[500],
child: Stack(
fit: StackFit.expand,
children: <Widget>[
//关闭广告
Align(
alignment: Alignment.topRight,
child: GestureDetector(
onTap: () {
setState(() {
showAdvertCover = false;
});
},
child: const Icon(
Icons.cancel,
size: 16,
color: Colors.white,
),
),
),
Container(
alignment: Alignment.center,
child: const Text(
"一个广告封面",
style: TextStyle(color: Colors.white),
),
)
],
),
),
)
: const Align(),
Builder(builder: (BuildContext context) {
state = GZVideoPlayer.of(context);
return const SizedBox();
})
],
/// 视频暂停回调
onPause: (value) {
debugPrint("video paused");
setState(() {
isPlaying = false;
});
},
/// 视频播放回调
onPlay: (value) {
debugPrint("video played");
setState(() {
isPlaying = true;
});
},
/// 视频播放结束回调
onEnded: (value) {
debugPrint("video ended");
},
/// 视频播放进度回调
/// 可以用来匹配字幕
onTimeUpdate: (value) {
// debugPrint("timeupdate ${value.position.inSeconds}");
// var position = value.position.inMilliseconds / 1000;
//根据 position 来判断当前显示的字幕
},
onProgressDrag: (position, duration) {
debugPrint("进度条拖拽的时间节点: $position");
debugPrint("进度条总时长: $duration");
},
onVolume: (value) {
// debugPrint("onvolume $value");
},
onBrightness: (value) {
debugPrint("onbrightness $value");
},
onFullscreen: (fullscreen) {
debugPrint("is fullscreen $fullscreen");
setState(() {
_isFullscreen = fullscreen;
});
},
onSpeedChange: (VideoSpeedItem item) {
debugPrint('current speed ${item.title}');
},
/// 顶部控制栏点击返回按钮
onPop: (value) {
debugPrint("返回上一页");
},
)
: const AspectRatio(
aspectRatio: 16 / 9,
child: Center(
child: CircularProgressIndicator(strokeWidth: 2.0),
),
),
),
/// 自定义拓展元素
children: [
/// DEMO1 自定义视频播放状态Toast
/// 待完善
/// DEMO2 这个将会覆盖的视频内容,因为这个层级是最高级,因此手势会失效(慎用)
/// 这个可以用来做视频广告
showAdvertCover
? Align(
alignment: Alignment.center,
child: Container(
width: 200,
height: 100,
color: Colors.blue[500],
child: Stack(
fit: StackFit.expand,
children: <Widget>[
//关闭广告
Align(
alignment: Alignment.topRight,
child: GestureDetector(
//update data source
!_isFullscreen
? Expanded(
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: VideoListWidget(
dataList: dataList,
onTapItem: (int index) {
VideoModel model = dataList[index];
setState(() {
videoUrl = model.url;
});
},
),
),
SliverToBoxAdapter(
child: Center(
child: CustomBtn(
title: '暂停/播放',
onTap: () {
setState(() {
showAdvertCover = false;
});
state?.togglePlay();
},
child: const Icon(
Icons.cancel,
size: 16,
color: Colors.white,
),
),
),
Container(
alignment: Alignment.center,
child: const Text(
"一个广告封面",
style: TextStyle(color: Colors.white),
),
)
],
),
),
)
: const Align(),
Builder(builder: (BuildContext context) {
state = GZVideoPlayer.of(context);
return const SizedBox();
})
],
/// 视频暂停回调
onPause: (value) {
debugPrint("video paused");
setState(() {
isPlaying = false;
});
},
/// 视频播放回调
onPlay: (value) {
debugPrint("video played");
setState(() {
isPlaying = true;
});
},
/// 视频播放结束回调
onEnded: (value) {
debugPrint("video ended");
},
/// 视频播放进度回调
/// 可以用来匹配字幕
onTimeUpdate: (value) {
// debugPrint("timeupdate ${value.position.inSeconds}");
// var position = value.position.inMilliseconds / 1000;
//根据 position 来判断当前显示的字幕
},
onProgressDrag: (position, duration) {
debugPrint("进度条拖拽的时间节点: $position");
debugPrint("进度条总时长: $duration");
},
onVolume: (value) {
// debugPrint("onvolume $value");
},
onBrightness: (value) {
debugPrint("onbrightness $value");
},
onFullscreen: (fullscreen) {
debugPrint("is fullscreen $fullscreen");
setState(() {
_isFullscreen = fullscreen;
});
},
onSpeedChange: (VideoSpeedItem item) {
debugPrint('current speed ${item.title}');
},
/// 顶部控制栏点击返回按钮
onPop: (value) {
debugPrint("返回上一页");
},
)
: const AspectRatio(
aspectRatio: 16 / 9,
child: Center(
child: CircularProgressIndicator(strokeWidth: 2.0),
),
),
//update data source
!_isFullscreen
? Expanded(
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: VideoListWidget(
dataList: dataList,
onTapItem: (int index) {
VideoModel model = dataList[index];
setState(() {
videoUrl = model.url;
});
},
),
),
SliverToBoxAdapter(
child: Center(
child: CustomBtn(
title: '暂停/播放',
onTap: () {
state?.togglePlay();
},
),
),
),
SliverToBoxAdapter(
child: Center(
child: CustomBtn(
title: '播放到指定位置',
onTap: () {
state?.seekTo(const Duration(seconds: 10));
},
SliverToBoxAdapter(
child: Center(
child: CustomBtn(
title: '播放到指定位置',
onTap: () {
state?.seekTo(const Duration(seconds: 10));
},
),
),
),
),
],
),
],
),
)
: Container(),
]),
),
)
: Container(),
]),
);
}
}
......
......@@ -121,7 +121,8 @@ class GZVideoPlayerState extends State<GZVideoPlayer>
bool _initialized = false;
/// 屏幕亮度
final ValueNotifier<double> _brightness = ValueNotifier(0.5);
final ValueNotifier<double> _brightness = ValueNotifier(0.0);
late double _originBrightness;
/// 视频音量
final ValueNotifier<double> _volume = ValueNotifier(0.5);
......@@ -221,14 +222,18 @@ class GZVideoPlayerState extends State<GZVideoPlayer>
/// 常亮
Wakelock.toggle(enable: true);
/// 初始化亮度为系统亮度
ScreenBrightness().current.then((brightness) {
_brightness.value = brightness;
});
initBrightness();
/// 倍数按钮标题
_speedTitle = widget.videoStyle.videoSpeedButtonStyle.title;
_initPlayer();
}
void initBrightness() async {
double brightness = await ScreenBrightness().current;
_brightness.value = brightness;
_originBrightness = brightness;
}
@override
void didUpdateWidget(GZVideoPlayer oldWidget) {
if (oldWidget.dataSource != widget.dataSource) {
......@@ -251,6 +256,7 @@ class GZVideoPlayerState extends State<GZVideoPlayer>
_subscription.cancel();
_brightness.dispose();
_volume.dispose();
ScreenBrightness().setScreenBrightness(_originBrightness);
super.dispose();
}
......
......@@ -8,27 +8,30 @@ class VideoLoadingView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.center,
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
loadingStyle.customLoadingIcon,
loadingStyle.customLoadingText != null
? loadingStyle.customLoadingText!
: Container(
margin: const EdgeInsets.only(top: 10),
child: Text(
loadingStyle.loadingText,
style: TextStyle(
color: loadingStyle.loadingTextFontColor,
fontSize: loadingStyle.loadingTextFontSize,
return DecoratedBox(
decoration: const BoxDecoration(color: Colors.black),
child: Align(
alignment: Alignment.center,
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
loadingStyle.customLoadingIcon,
loadingStyle.customLoadingText != null
? loadingStyle.customLoadingText!
: Container(
margin: const EdgeInsets.only(top: 10),
child: Text(
loadingStyle.loadingText,
style: TextStyle(
color: loadingStyle.loadingTextFontColor,
fontSize: loadingStyle.loadingTextFontSize,
),
),
),
)
],
)
],
),
),
),
);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论