315 lines
12 KiB
Dart
315 lines
12 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:yumi/app/constants/sc_global_config.dart';
|
|
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
|
import 'package:video_player/video_player.dart';
|
|
|
|
class VideoPlayerPage extends StatefulWidget {
|
|
final String videoUrl;
|
|
|
|
const VideoPlayerPage({Key? key, required this.videoUrl}) : super(key: key);
|
|
|
|
@override
|
|
_VideoPlayerPageState createState() => _VideoPlayerPageState();
|
|
}
|
|
|
|
class _VideoPlayerPageState extends State<VideoPlayerPage> {
|
|
late VideoPlayerController _controller;
|
|
late Future<void> _initializeVideoPlayerFuture;
|
|
|
|
bool _isPlaying = false;
|
|
bool _showControls = true;
|
|
Duration _currentPosition = Duration.zero;
|
|
Duration _totalDuration = Duration.zero;
|
|
|
|
// 控制界面显示和隐藏的定时器
|
|
Timer? _hideControlsTimer;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_initializeVideoPlayer();
|
|
}
|
|
|
|
void _initializeVideoPlayer() {
|
|
final pathType = SCPathUtils.getPathType(widget.videoUrl);
|
|
if (pathType == PathType.file) {
|
|
_controller = VideoPlayerController.file(File(widget.videoUrl));
|
|
_initializeVideoPlayerFuture = _controller.initialize().then((_) {
|
|
setState(() {
|
|
_totalDuration = _controller.value.duration;
|
|
});
|
|
});
|
|
} else if (pathType == PathType.network) {
|
|
_controller = VideoPlayerController.networkUrl(
|
|
Uri.parse(widget.videoUrl),
|
|
);
|
|
_initializeVideoPlayerFuture = _controller.initialize().then((_) {
|
|
setState(() {
|
|
_totalDuration = _controller.value.duration;
|
|
});
|
|
});
|
|
}
|
|
|
|
// 添加监听器来更新进度
|
|
_controller.addListener(_updateProgress);
|
|
}
|
|
|
|
void _updateProgress() {
|
|
if (mounted) {
|
|
setState(() {
|
|
_currentPosition = _controller.value.position;
|
|
_totalDuration = _controller.value.duration;
|
|
_isPlaying = _controller.value.isPlaying;
|
|
});
|
|
}
|
|
}
|
|
|
|
void _togglePlayPause() {
|
|
setState(() {
|
|
if (_controller.value.isPlaying) {
|
|
_controller.pause();
|
|
_isPlaying = false;
|
|
} else {
|
|
_controller.play();
|
|
_isPlaying = true;
|
|
_resetHideControlsTimer();
|
|
}
|
|
});
|
|
}
|
|
|
|
void _seekToPosition(Duration position) {
|
|
_controller.seekTo(position);
|
|
}
|
|
|
|
void _resetHideControlsTimer() {
|
|
_hideControlsTimer?.cancel();
|
|
_hideControlsTimer = Timer(Duration(seconds: SCGlobalConfig.businessLogicStrategy.getVideoPlayerControlsDisplayDuration()), () {
|
|
if (mounted && _controller.value.isPlaying) {
|
|
setState(() {
|
|
_showControls = false;
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
void _toggleControls() {
|
|
setState(() {
|
|
_showControls = !_showControls;
|
|
});
|
|
if (_showControls && _controller.value.isPlaying) {
|
|
_resetHideControlsTimer();
|
|
}
|
|
}
|
|
|
|
String _formatDuration(Duration duration) {
|
|
String twoDigits(int n) => n.toString().padLeft(2, '0');
|
|
final hours = twoDigits(duration.inHours);
|
|
final minutes = twoDigits(duration.inMinutes.remainder(60));
|
|
final seconds = twoDigits(duration.inSeconds.remainder(60));
|
|
|
|
if (duration.inHours > 0) {
|
|
return '$hours:$minutes:$seconds';
|
|
} else {
|
|
return '$minutes:$seconds';
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_hideControlsTimer?.cancel();
|
|
_controller.removeListener(_updateProgress);
|
|
_controller.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: SCGlobalConfig.businessLogicStrategy.getVideoPlayerBackgroundColor(),
|
|
body: Center(
|
|
child: FutureBuilder(
|
|
future: _initializeVideoPlayerFuture,
|
|
builder: (context, snapshot) {
|
|
if (snapshot.connectionState == ConnectionState.done) {
|
|
return GestureDetector(
|
|
onTap: _toggleControls,
|
|
child: Stack(
|
|
alignment: Alignment.center,
|
|
children: [
|
|
// 视频播放器
|
|
AspectRatio(
|
|
aspectRatio: _controller.value.aspectRatio,
|
|
child: VideoPlayer(_controller),
|
|
),
|
|
|
|
// 控制界面
|
|
if (_showControls) ...[
|
|
// 半透明背景
|
|
Container(color: SCGlobalConfig.businessLogicStrategy.getVideoPlayerControlsBackgroundColor()),
|
|
|
|
// 播放/暂停按钮
|
|
IconButton(
|
|
icon: Icon(
|
|
_isPlaying ? Icons.pause : Icons.play_arrow,
|
|
color: SCGlobalConfig.businessLogicStrategy.getVideoPlayerIconColor(),
|
|
size: 50,
|
|
),
|
|
onPressed: _togglePlayPause,
|
|
),
|
|
|
|
// 底部控制栏
|
|
Positioned(
|
|
bottom: 0,
|
|
left: 0,
|
|
right: 0,
|
|
child: Container(
|
|
color: SCGlobalConfig.businessLogicStrategy.getVideoPlayerBottomControlsBackgroundColor(),
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
children: [
|
|
// 进度条
|
|
Slider(
|
|
value:
|
|
_currentPosition.inMilliseconds.toDouble(),
|
|
min: 0,
|
|
max: _totalDuration.inMilliseconds.toDouble(),
|
|
onChanged: (value) {
|
|
final newPosition = Duration(
|
|
milliseconds: value.toInt(),
|
|
);
|
|
_seekToPosition(newPosition);
|
|
},
|
|
onChangeStart: (_) {
|
|
_hideControlsTimer?.cancel();
|
|
},
|
|
onChangeEnd: (_) {
|
|
if (_controller.value.isPlaying) {
|
|
_resetHideControlsTimer();
|
|
}
|
|
},
|
|
activeColor: SCGlobalConfig.businessLogicStrategy.getVideoPlayerProgressBarActiveColor(),
|
|
inactiveColor: SCGlobalConfig.businessLogicStrategy.getVideoPlayerProgressBarInactiveColor(),
|
|
),
|
|
|
|
// 时间信息和控制按钮
|
|
Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
// 当前时间
|
|
Text(
|
|
_formatDuration(_currentPosition),
|
|
style: TextStyle(
|
|
color: SCGlobalConfig.businessLogicStrategy.getVideoPlayerTextColor(),
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
|
|
// 控制按钮
|
|
Row(
|
|
children: [
|
|
// 播放/暂停按钮
|
|
IconButton(
|
|
icon: Icon(
|
|
_isPlaying
|
|
? Icons.pause
|
|
: Icons.play_arrow,
|
|
color: SCGlobalConfig.businessLogicStrategy.getVideoPlayerIconColor(),
|
|
),
|
|
onPressed: _togglePlayPause,
|
|
),
|
|
|
|
// 10秒前进
|
|
IconButton(
|
|
icon: Icon(
|
|
Icons.forward_5,
|
|
color: SCGlobalConfig.businessLogicStrategy.getVideoPlayerIconColor(),
|
|
),
|
|
onPressed: () {
|
|
final newPosition =
|
|
_currentPosition +
|
|
const Duration(seconds: 5);
|
|
if (newPosition <= _totalDuration) {
|
|
_seekToPosition(newPosition);
|
|
} else {
|
|
_seekToPosition(_totalDuration);
|
|
}
|
|
},
|
|
),
|
|
|
|
// 10秒后退
|
|
IconButton(
|
|
icon: Icon(
|
|
Icons.replay_5,
|
|
color: SCGlobalConfig.businessLogicStrategy.getVideoPlayerIconColor(),
|
|
),
|
|
onPressed: () {
|
|
final newPosition =
|
|
_currentPosition -
|
|
const Duration(seconds: 5);
|
|
if (newPosition >= Duration.zero) {
|
|
_seekToPosition(newPosition);
|
|
} else {
|
|
_seekToPosition(Duration.zero);
|
|
}
|
|
},
|
|
),
|
|
],
|
|
),
|
|
|
|
// 总时长
|
|
Text(
|
|
_formatDuration(_totalDuration),
|
|
style: TextStyle(
|
|
color: SCGlobalConfig.businessLogicStrategy.getVideoPlayerTextColor(),
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
] else if (!_controller.value.isPlaying) ...[
|
|
// 当视频暂停但控制界面隐藏时,显示播放按钮
|
|
IconButton(
|
|
icon: Icon(
|
|
Icons.play_arrow,
|
|
color: SCGlobalConfig.businessLogicStrategy.getVideoPlayerIconColor(),
|
|
size: 50,
|
|
),
|
|
onPressed: _togglePlayPause,
|
|
),
|
|
],
|
|
|
|
// 加载指示器
|
|
if (snapshot.connectionState == ConnectionState.waiting)
|
|
CircularProgressIndicator(
|
|
valueColor: AlwaysStoppedAnimation<Color>(SCGlobalConfig.businessLogicStrategy.getVideoPlayerLoadingIndicatorColor()),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
} else {
|
|
return Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
CircularProgressIndicator(
|
|
valueColor: AlwaysStoppedAnimation<Color>(SCGlobalConfig.businessLogicStrategy.getVideoPlayerLoadingIndicatorColor()),
|
|
),
|
|
SizedBox(height: 16),
|
|
Text('loading...', style: TextStyle(color: SCGlobalConfig.businessLogicStrategy.getVideoPlayerTextColor())),
|
|
],
|
|
);
|
|
}
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|