FijkPlayer 第三方的一个视频播放器,这是一个大佬基于比利比利播放器封装的,有常用的API 可自定义样式
pub传送门
默认的样式 展示:

自定义的样式 展示:


**使用:**
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | fijkplayer: ^0.8.4 /// 声明一个FijkPlayer final FijkPlayer player = FijkPlayer(); @override void initState() { /// 指定视频地址 player.setDataSource("http://video.kekedj.com/20190215/mp4/20190527/TWICE%20-%20BDZ%20(Korean%20Ver.)%20(Stage%20Mix)%EF%BC%88%E6%97%A5%E6%9C%AC%E8%AA%9E%E5%AD%97%E5%B9%95%EF%BC%89.mp4", autoPlay: true); super.initState(); } @override void dispose() { super.dispose(); player.release(); } /// 使用FijkView body: SafeArea(child: Center( child: FijkView( color: Colors.black, player: player, panelBuilder: (FijkPlayer player, FijkData data, BuildContext context, Size viewSize, Rect texturePos) { /// 使用自定义的布局 return CustomFijkPanel( player: player, buildContext: context, viewSize: viewSize, texturePos: texturePos, ); }, ), ),), |
自定义的底部
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | class CustomFijkWidgetBottom extends StatefulWidget { final FijkPlayer player; final BuildContext buildContext; final Size viewSize; final Rect texturePos; const CustomFijkPanel({ @required this.player, this.buildContext, this.viewSize, this.texturePos, }); @override _CustomFijkWidgetBottomState createState() => _CustomFijkWidgetBottomState(); } class _CustomFijkWidgetBottomState extends State<CustomFijkWidgetBottom > { FijkPlayer get player => widget.player; /// 播放状态 bool _playing = false; /// 是否显示状态栏+菜单栏 bool isPlayShowCont = true; /// 总时长 String duration = "00:00:00"; /// 已播放时长 String durrentPos = "00:00:00"; /// 进度条总长度 double maxDurations = 0.0; /// 流监听器 StreamSubscription _currentPosSubs; /// 定时器 Timer _timer; /// 进度条当前进度 double sliderValue = 0.0; @override void initState() { /// 提前加载 /// 进行监听 widget.player.addListener(_playerValueChanged); /// 接收流 _currentPosSubs = widget.player.onCurrentPosUpdate.listen((v) { setState(() { /// 实时获取当前播放进度(进度条) this.sliderValue = v.inMilliseconds.toDouble(); /// 实时获取当前播放进度(数字展示) durrentPos = v.toString().substring(0,v.toString().indexOf(".")); }); }); /// 初始化 super.initState(); } /// 监听器 void _playerValueChanged() { FijkValue value = player.value; /// 获取进度条总时长 maxDurations = value.duration.inMilliseconds.toDouble(); /// 获取展示的时长 duration = value.duration.toString().substring(0,value.duration.toString().indexOf(".")); /// 播放状态 bool playing = (value.state == FijkState.started); if (playing != _playing) setState(() =>_playing = playing); } @override Widget build(BuildContext context) { Rect rect = Rect.fromLTRB( max(0.0, widget.texturePos.left), max(0.0, widget.texturePos.top), min(widget.viewSize.width, widget.texturePos.right), min(widget.viewSize.height, widget.texturePos.bottom), ); return Positioned.fromRect( rect: rect, child: GestureDetector( onTap: (){ setState(() { /// 显示 、隐藏 进度条+标题栏 isPlayShowCont = !isPlayShowCont; /// 如果显示了 , 3秒后 隐藏进度条+标题栏 if(isPlayShowCont) _timer = Timer(Duration(seconds: 3),()=>isPlayShowCont = false); }); }, child:Container( color: Color.fromRGBO(0, 0, 0, 0.0), alignment: Alignment.bottomLeft, child:Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[ /// 标题栏 !isPlayShowCont ? SizedBox() :Container( color: Color.fromRGBO(0, 0, 0, 0.65), height: 35, child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[ IconButton(icon: Icon(Icons.chevron_left,color: Colors.white,), onPressed: (){ Navigator.pop(context); }), ], ), ), /// 控制条 !isPlayShowCont ? SizedBox() : Container( color: Color.fromRGBO(0, 0, 0, 0.65), height: 50, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[ IconButton( icon: Icon( _playing ? Icons.pause : Icons.play_arrow, color: Colors.white, ), onPressed: () => _playing ? widget.player.pause() : widget.player.start(), ), /// 进度条 使用Slider滑动组件实现 Expanded(child: SliderTheme( data: SliderTheme.of(context).copyWith( //已拖动的颜色 activeTrackColor: Colors.greenAccent, //未拖动的颜色 inactiveTrackColor: Colors.green, //提示进度的气泡的背景色 valueIndicatorColor: Colors.green, //提示进度的气泡文本的颜色 valueIndicatorTextStyle: TextStyle( color:Colors.white, ), //滑块中心的颜色 thumbColor: Colors.green, //滑块边缘的颜色 overlayColor: Colors.white, //对进度线分割后,断续线中间间隔的颜色 inactiveTickMarkColor: Colors.white, ), child: Slider( value: this.sliderValue, label: '${int.parse((this.sliderValue / 3600000).toStringAsFixed(0))<10?'0'+(this.sliderValue / 3600000).toStringAsFixed(0):(this.sliderValue / 3600000).toStringAsFixed(0)}:${int.parse(((this.sliderValue % 3600000) / 60000).toStringAsFixed(0))<10?'0'+((this.sliderValue % 3600000) / 60000).toStringAsFixed(0):((this.sliderValue % 3600000) / 60000).toStringAsFixed(0)}:${int.parse(((this.sliderValue % 60000) / 1000).toStringAsFixed(0))<10?'0'+((this.sliderValue % 60000) / 1000).toStringAsFixed(0):((this.sliderValue % 60000) / 1000).toStringAsFixed(0)}', min: 0.0, max: maxDurations, divisions: 1000, onChanged: (val){ ///转化成double setState(() => this.sliderValue = val.floorToDouble()); /// 设置进度 player.seekTo(this.sliderValue.toInt()); // print(this.sliderValue); }, ), ), ), Text("${durrentPos} / ${duration}",style: TextStyle(color: Colors.white),), ], ), ), ], ) ), ), ); } @override void dispose() { /// 关闭监听 player.removeListener(_playerValueChanged); /// 关闭流回调 _currentPosSubs?.cancel(); super.dispose(); } } |