qhd-prevention-flutter/lib/customWidget/full_screen_video_page.dart

266 lines
8.2 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 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:flutter/services.dart';
///弹窗组件VideoPlayerPopup
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 _controller;
bool _showControls = true;
@override
void initState() {
super.initState();
_controller = VideoPlayerController.networkUrl(
Uri.parse(widget.videoUrl),
)..initialize().then((_) {
setState(() {});
_controller.play();
});
// 自动隐藏控件
_controller.addListener(() {
if (_controller.value.isPlaying && _showControls) {
Future.delayed(const Duration(seconds: 3), () {
if (_controller.value.isPlaying && mounted) {
setState(() => _showControls = false);
}
});
}
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
Widget _buildControls() {
final pos = _controller.value.position;
final dur = _controller.value.duration;
String fmt(Duration d) =>
'${d.inMinutes.remainder(60).toString().padLeft(2, '0')}:'
'${d.inSeconds.remainder(60).toString().padLeft(2, '0')}';
return Positioned.fill(
child: AnimatedOpacity(
opacity: _showControls ? 1 : 0,
duration: const Duration(milliseconds: 300),
child: GestureDetector(
onTap: () => setState(() => _showControls = !_showControls),
child: Container(
color: Colors.black45,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
// 进度条
Slider(
value: pos.inMilliseconds.toDouble().clamp(0, dur.inMilliseconds.toDouble()),
max: dur.inMilliseconds.toDouble(),
onChanged: (v) {
_controller.seekTo(Duration(milliseconds: v.toInt()));
},
),
// 时间 + 控制按钮
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
children: [
IconButton(
icon: Icon(
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white,
),
onPressed: () {
setState(() {
_controller.value.isPlaying
? _controller.pause()
: _controller.play();
});
},
),
Text(
'${fmt(pos)} / ${fmt(dur)}',
style: const TextStyle(color: Colors.white),
),
const Spacer(),
IconButton(
icon: const Icon(Icons.fullscreen, color: Colors.white),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => FullScreenVideoPage(
controller: _controller)));
},
),
],
),
),
],
),
),
),
),
);
}
@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: MediaQuery.of(context).size.height * 0.8,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Stack(
children: [
// 视频内容
if (_controller.value.isInitialized)
AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
)
else
const Center(child: CircularProgressIndicator()),
// 关闭按钮
Positioned(
top: 4,
right: 4,
child: IconButton(
icon: const Icon(Icons.close, color: Colors.black54),
onPressed: () => Navigator.of(context).pop(),
),
),
// 播放控制
if (_controller.value.isInitialized) _buildControls(),
],
),
),
),
);
}
}
/// 全屏横屏播放页面FullScreenVideoPage
class FullScreenVideoPage extends StatefulWidget {
final VideoPlayerController controller;
const FullScreenVideoPage({Key? key, required this.controller}) : super(key: key);
@override
State<FullScreenVideoPage> createState() => _FullScreenVideoPageState();
}
class _FullScreenVideoPageState extends State<FullScreenVideoPage> {
VideoPlayerController get _controller => widget.controller;
@override
void initState() {
super.initState();
// 进入横屏、沉浸式
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
}
@override
void dispose() {
// 恢复竖屏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Center(
child: Stack(
children: [
// 全屏视频
if (_controller.value.isInitialized)
SizedBox.expand(child: VideoPlayer(_controller))
else
const Center(child: CircularProgressIndicator()),
// 简单控制:点击画面切换播放/暂停
GestureDetector(
onTap: () {
setState(() {
_controller.value.isPlaying
? _controller.pause()
: _controller.play();
});
},
),
// 返回按钮
Positioned(
top: 20,
left: 20,
child: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.white, size: 28),
onPressed: () => Navigator.of(context).pop(),
),
),
// 时间 & 进度条(简单版)
if (_controller.value.isInitialized)
Positioned(
bottom: 20,
left: 20,
right: 20,
child: Row(
children: [
Text(
'${_format(_controller.value.position)} / ${_format(_controller.value.duration)}',
style: const TextStyle(color: Colors.white),
),
const SizedBox(width: 12),
Expanded(
child: VideoProgressIndicator(
_controller,
allowScrubbing: true,
colors: VideoProgressColors(
playedColor: Colors.red,
bufferedColor: Colors.white54,
backgroundColor: Colors.white30,
),
),
),
],
),
),
],
),
),
);
}
String _format(Duration d) =>
'${d.inMinutes.remainder(60).toString().padLeft(2, '0')}:'
'${d.inSeconds.remainder(60).toString().padLeft(2, '0')}';
}