import 'dart:io'; import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:video_player/video_player.dart'; import 'controller_widget.dart'; import 'video_player_control.dart'; import 'video_player_pan.dart'; enum VideoPlayerType { network, asset, file } class VideoPlayerUI extends StatefulWidget { const VideoPlayerUI.network({ Key? key, required String this.url, // 当前需要播放的地址 required this.width, // 播放器尺寸(大于等于视频播放区域) required this.height, this.title = '', // 视频需要显示的标题 }) : type = VideoPlayerType.network, super(key: key); const VideoPlayerUI.asset({ Key? key, required String dataSource, // 当前需要播放的地址 this.width = double.infinity, // 播放器尺寸(大于等于视频播放区域) this.height = double.infinity, this.title = '', // 视频需要显示的标题 }) : type = VideoPlayerType.asset, url = dataSource, super(key: key); const VideoPlayerUI.file({ Key? key, required File file, // 当前需要播放的地址 this.width = double.infinity, // 播放器尺寸(大于等于视频播放区域) this.height = double.infinity, this.title = '', // 视频需要显示的标题 }) : type = VideoPlayerType.file, url = file, super(key: key); final url; final VideoPlayerType type; final double width; final double height; final String title; @override _VideoPlayerUIState createState() => _VideoPlayerUIState(); } class _VideoPlayerUIState extends State { final GlobalKey _key = GlobalKey(); ///指示video资源是否加载完成,加载完成后会获得总时长和视频长宽比等信息 bool _videoInit = false; bool _videoError = false; late VideoPlayerController _controller; // video控件管理器 /// 记录是否全屏 bool get _isFullScreen => MediaQuery.of(context).orientation == Orientation.landscape; Size get _window => MediaQueryData.fromWindow(window).size; @override void initState() { super.initState(); _urlChange(); // 初始进行一次url加载 // Screen.keepOn(true); // 设置屏幕常亮 } @override void didUpdateWidget(VideoPlayerUI oldWidget) { if (oldWidget.url != widget.url) { _urlChange(); // url变化时重新执行一次url加载 } super.didUpdateWidget(oldWidget); } @override void dispose() async { super.dispose(); if (_controller != null) { _controller.removeListener(_videoListener); _controller.dispose(); } // Screen.keepOn(false); } @override Widget build(BuildContext context) { return SafeArea( top: !_isFullScreen, bottom: !_isFullScreen, left: !_isFullScreen, right: !_isFullScreen, child: Container( width: _isFullScreen ? _window.width : widget.width, height: _isFullScreen ? _window.height : widget.height, child: _isHadUrl(), ), ); } // 判断是否有url Widget _isHadUrl() { if (widget.url != null) { return ControllerWidget( controlKey: _key, controller: _controller, videoInit: _videoInit, title: widget.title, child: VideoPlayerPan( child: Container( alignment: Alignment.center, width: double.infinity, height: double.infinity, color: Colors.black, child: _isVideoInit(), ), ), ); } else { return Center( child: Text( '暂无视频信息'.tr, style: const TextStyle(color: Colors.white), ), ); } } // 加载url成功时,根据视频比例渲染播放器 Widget _isVideoInit() { if (_videoInit) { return AspectRatio( aspectRatio: _controller.value.aspectRatio, child: VideoPlayer(_controller), ); } else if (_controller != null && _videoError) { return Text( '加载出错'.tr, style: const TextStyle(color: Colors.white), ); } else { return const SizedBox( width: 30, height: 30, child: CircularProgressIndicator( strokeWidth: 2, ), ); } } void _urlChange() async { if (widget.url == null || widget.url == '') return; if (_controller != null) { /// 如果控制器存在,清理掉重新创建 _controller.removeListener(_videoListener); _controller.dispose(); } setState(() { /// 重置组件参数 _videoInit = false; _videoError = false; }); if (widget.type == VideoPlayerType.file) { _controller = VideoPlayerController.file(widget.url); } else if (widget.type == VideoPlayerType.asset) { _controller = VideoPlayerController.asset(widget.url); } else { _controller = VideoPlayerController.network(widget.url); } /// 加载资源完成时,监听播放进度,并且标记_videoInit=true加载完成 _controller.addListener(_videoListener); await _controller.initialize(); setState(() { _videoInit = true; _videoError = false; _controller.play(); }); } Future _videoListener() async { if (_controller.value.hasError) { setState(() { _videoError = true; }); } else { final Duration? res = await _controller.position; if (res! >= _controller.value.duration) { await _controller.seekTo(const Duration(seconds: 0)); await _controller.pause(); } if (_controller.value.isPlaying && _key.currentState != null) { /// 减少build次数 _key.currentState!.setPosition( position: res, totalDuration: _controller.value.duration, ); } } } }