FLutter视频播放器
使用前的准备工作
1.添加video_player
flutter pub add video_player
2.添加chewie
flutter pub add chewie
3.添加相应的权限
-
如果是在android中,需要向AndroidManifest.xml文件中添加类似下面的内容
<uses-permission android:name="android.permission.INTERNET"/> -
在IOS中则需要在Info.plist中添加下面的内容
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>
在flutter中使用
import 'package:anime_video/page/player/video/video_ui_page.dart';
import 'package:flutter/material.dart';
import 'package:chewie/chewie.dart';
import 'package:video_player/video_player.dart';
class VideoPlayer extends StatefulWidget {
// 视频URL
final String videoUrl;
const VideoPlayer({super.key, required this.videoUrl});
@override
State<VideoPlayer> createState() => _VideoPlayerState();
}
class _VideoPlayerState extends State<VideoPlayer> {
late VideoPlayerController _videoPlayerController;
ChewieController? _chewieController;
bool isFullScreen = false;
@override
void initState() {
super.initState();
_initializePlayer();
}
Future<void> _initializePlayer() async {
_videoPlayerController = VideoPlayerController.networkUrl(
Uri.parse(widget.videoUrl),
);
await _videoPlayerController.initialize();
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
autoPlay: true,
looping: false,
aspectRatio: _videoPlayerController.value.aspectRatio,
// 自定义控制栏
customControls: VideoUiPage(
videoPlayerController: _videoPlayerController,
onToggleFullScreen: _toggleFullScreen,
),
);
setState(() {});
}
// 全屏、半屏切换
void _toggleFullScreen() {
if (_chewieController != null) {
if (_chewieController!.isFullScreen) {
_chewieController!.exitFullScreen();
} else {
_chewieController!.enterFullScreen();
}
}
}
@override
Widget build(BuildContext context) {
return Center(
// ChewieController如果没有初始化完成,则显示加载框
child: _chewieController != null
? AspectRatio(
aspectRatio: _videoPlayerController.value.aspectRatio,
child: Chewie(controller: _chewieController!),
)
: CircularProgressIndicator(),
);
}
@override
void dispose() {
_videoPlayerController.dispose();
_chewieController?.dispose();
super.dispose();
}
}
自定义控制栏
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:chewie/chewie.dart';
class VideoUiPage extends StatefulWidget {
// 视频播放器控制器
final VideoPlayerController videoPlayerController;
// 全屏切换回调
final VoidCallback onToggleFullScreen;
const VideoUiPage({
super.key,
required this.videoPlayerController,
required this.onToggleFullScreen,
});
@override
State<VideoUiPage> createState() => _VideoUiPageState();
}
class _VideoUiPageState extends State<VideoUiPage> {
// 播放器是否在播放
bool get isPlaying => widget.videoPlayerController.value.isPlaying;
// 当前播放位置
Duration get position => widget.videoPlayerController.value.position;
// 视频总时长
Duration get duration => widget.videoPlayerController.value.duration;
// 音量
double get videoVolume => widget.videoPlayerController.value.volume;
// 进度
double get progress => position.inMilliseconds / duration.inMilliseconds;
// 获取缓冲进度
double get bufferedProgress {
if (widget.videoPlayerController.value.buffered.isEmpty) {
return 0.0;
}
// 获取最新的缓冲位置
final buffered = widget.videoPlayerController.value.buffered.last;
//计算缓冲结束时间占总时长的比例,作为缓冲进度返回
return buffered.end.inMilliseconds / duration.inMilliseconds;
}
// 视频是否正在缓冲
bool get isBuffering => widget.videoPlayerController.value.isBuffering;
@override
void initState() {
super.initState();
widget.videoPlayerController.addListener(() {
if (mounted) {
setState(() {});
}
});
}
@override
Widget build(BuildContext context) {
// 通过上下文获取ChewieController
final chewieController = ChewieController.of(context);
return Stack(
children: [
// 顶部控制栏
Positioned(
top: 0,
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(2),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// 返回按钮
IconButton(
onPressed: () {
Navigator.of(context).pop();
},
icon: Icon(Icons.arrow_back, color: Colors.white, size: 24),
),
// 标题
// 设置按钮
IconButton(
onPressed: () {
_showSettingsDialog(context, widget.videoPlayerController);
},
icon: const Icon(
Icons.settings,
color: Colors.white,
size: 24,
),
),
],
),
),
),
// 底部控制栏
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Column(
children: [
// 时间显示
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
'${_formatDuration(position)} / ${_formatDuration(duration)}',
style: const TextStyle(color: Colors.white, fontSize: 12),
),
),
),
// 底部控制栏
Row(
children: [
// 播放/暂停按钮
IconButton(
onPressed: () {
if (isPlaying) {
widget.videoPlayerController.pause();
} else {
widget.videoPlayerController.play();
}
},
icon: Icon(
isBuffering
? Icons.pause
: isPlaying
? Icons.pause
: Icons.play_arrow,
color: Colors.white,
size: 35,
),
),
// 音量控制
IconButton(
onPressed: () {
final newVolume = videoVolume > 0 ? 0.0 : 1.0;
widget.videoPlayerController.setVolume(newVolume);
},
icon: Icon(
videoVolume > 0 ? Icons.volume_up : Icons.volume_off,
color: Colors.white,
size: 30,
),
),
// 进度条
Expanded(
child: SliderTheme(
data: SliderThemeData(
// 进度条已完成部分颜色
activeTrackColor: Colors.red,
// 进度条未完成部分颜色
inactiveTrackColor: Colors.white.withValues(alpha: 0.3),
// 缓冲进度条颜色
secondaryActiveTrackColor: Colors.white.withValues(
alpha: 0.5,
),
// 进度条小球颜色
thumbColor: Colors.red,
// 进度条小球样式
thumbShape: const RoundSliderThumbShape(
enabledThumbRadius: 6,
),
),
child: Slider(
value: progress.clamp(0.0, 1.0),
secondaryTrackValue: bufferedProgress.clamp(0.0, 1.0),
onChanged: (value) {
final newPosition = Duration(
milliseconds: (value * duration.inMilliseconds)
.round(),
);
widget.videoPlayerController.seekTo(newPosition);
},
),
),
),
// 全屏按钮
IconButton(
onPressed: () {
widget.onToggleFullScreen.call();
},
icon: Icon(
chewieController.isFullScreen
? Icons.fullscreen_exit
: Icons.fullscreen,
color: Colors.white,
size: 30,
),
),
],
),
],
),
),
],
);
}
// 格式化时间
String _formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final minutes = duration.inMinutes.remainder(60);
final seconds = duration.inSeconds.remainder(60);
return '${twoDigits(minutes)}:${twoDigits(seconds)}';
}
// 显示设置对话框
void _showSettingsDialog(
BuildContext context,
VideoPlayerController controller,
) {
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Colors.grey[900],
title: const Text('播放设置', style: TextStyle(color: Colors.white)),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
//播放速度
ListTile(
title: const Text('播放速度', style: TextStyle(color: Colors.white)),
subtitle: Text(
'${controller.value.playbackSpeed}x',
style: const TextStyle(color: Colors.white),
),
trailing: const Icon(Icons.speed, color: Colors.white),
onTap: () => _showSpeedDialog(context, controller),
),
],
),
),
);
}
// 显示速度选择对话框
void _showSpeedDialog(
BuildContext context,
VideoPlayerController controller,
) {
final speeds = [0.5, 0.75, 1.0, 1.25, 1.5, 2.0];
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Colors.grey[900],
title: const Text('选择播放速度', style: TextStyle(color: Colors.white)),
content: Column(
mainAxisSize: MainAxisSize.min,
children: speeds
.map(
(speed) => SizedBox(
height: 50,
child: ListView(
children: [
InkWell(
onTap: () {
controller.setPlaybackSpeed(speed);
Navigator.of(context).pop();
},
child: Text(
'${speed}x',
style: const TextStyle(color: Colors.white),
),
),
],
),
),
)
.toList(),
),
),
);
}
}