flutter_integrated_whb/lib/customWidget/full_screen_video_page.dart

168 lines
4.9 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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<VideoPlayerPopup> createState() => _VideoPlayerPopupState();
}
class _VideoPlayerPopupState extends State<VideoPlayerPopup> {
late VideoPlayerController _videoController;
ChewieController? _chewieController;
bool _isNetwork = false;
bool _initializing = true;
String? _error;
@override
void initState() {
super.initState();
_initController();
}
Future<void> _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)));
}
}