import 'dart:io'; import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; import 'package:chewie/chewie.dart'; /// 弹窗组件:支持 本地文件 / 网络视频 自动识别 class VideoPlayerPopup extends StatefulWidget { final String videoUrl; const VideoPlayerPopup({Key? key, required this.videoUrl}) : super(key: key); @override State createState() => _VideoPlayerPopupState(); } class _VideoPlayerPopupState extends State { late VideoPlayerController _videoController; ChewieController? _chewieController; bool _isNetwork = false; bool _initializing = true; String? _error; @override void initState() { super.initState(); _initController(); } Future _initController() async { try { final uri = Uri.tryParse(widget.videoUrl); final scheme = uri?.scheme?.toLowerCase() ?? ''; // 判定是否网络视频(包括 http/https/rtsp/rtmp) _isNetwork = (scheme == 'http' || scheme == 'https' || scheme == 'rtsp' || scheme == 'rtmp'); if (_isNetwork) { // 网络视频 _videoController = VideoPlayerController.networkUrl(Uri.parse(widget.videoUrl)); } else { // 本地视频:支持 file:// 开头或直接是本地路径 if (scheme == 'file' && uri != null) { _videoController = VideoPlayerController.file(File(uri.toFilePath())); } else { // 直接当做本地路径处理(例如 /storage/... 或 沙盒内路径) _videoController = VideoPlayerController.file(File(widget.videoUrl)); } } // 初始化 VideoPlayerController await _videoController.initialize(); // 在视频初始化完成后创建 ChewieController(以确保 aspectRatio 可用) _chewieController?.dispose(); _chewieController = ChewieController( videoPlayerController: _videoController, autoPlay: true, looping: false, showOptions: false, allowFullScreen: true, allowPlaybackSpeedChanging: true, allowMuting: true, showControlsOnInitialize: true, // 不要在这里强制颜色(你可以自定义),但保留示例: materialProgressColors: ChewieProgressColors( playedColor: Colors.blue, backgroundColor: Colors.white, handleColor: Colors.blue, bufferedColor: Colors.red, ), aspectRatio: _videoController.value.aspectRatio > 0 ? _videoController.value.aspectRatio : 16 / 9, errorBuilder: (context, errorMessage) { return Center( child: Text( errorMessage ?? '视频播放错误', style: const TextStyle(color: Colors.white), ), ); }, ); if (!mounted) return; setState(() { _initializing = false; }); } catch (e, st) { // 捕获异常并展示错误信息 debugPrint('Video init error: $e\n$st'); if (!mounted) return; setState(() { _initializing = false; _error = e.toString(); }); } } @override void dispose() { _chewieController?.dispose(); _videoController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Center( child: Material( color: Colors.transparent, child: Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width * 0.9, maxHeight: 500, ), decoration: BoxDecoration( color: Colors.black, borderRadius: BorderRadius.circular(8), ), child: Stack( children: [ // 视频播放器 或 错误 / 加载指示 Positioned.fill( child: _buildPlayerBody(), ), // 关闭按钮 Positioned( top: 4, right: 4, child: IconButton( icon: const Icon(Icons.close, color: Colors.white), onPressed: () { Navigator.of(context).pop(); }, ), ), ], ), ), ), ); } Widget _buildPlayerBody() { if (_initializing) { return const Center(child: CircularProgressIndicator()); } if (_error != null) { return Center( child: Padding( padding: const EdgeInsets.all(16.0), child: Text( '播放失败:$_error', style: const TextStyle(color: Colors.white), ), ), ); } if (_chewieController != null && _videoController.value.isInitialized) { return Chewie(controller: _chewieController!); } return const Center(child: Text('无法播放视频', style: TextStyle(color: Colors.white))); } }