Flutter 第三方视频播放器组件详解
常用视频播放器组件对比
| 组件名称 | 特点 | 支持平台 | 推荐度 |
|---|---|---|---|
| video_player | 官方基础播放器 | iOS, Android, Web | ⭐⭐⭐⭐ |
| chewie | 基于video_player的UI封装 | iOS, Android, Web | ⭐⭐⭐⭐⭐ |
| better_player | 功能最全面的播放器 | iOS, Android | ⭐⭐⭐⭐⭐ |
| fijkplayer | 基于ijkplayer的Flutter播放器 | iOS, Android | ⭐⭐⭐⭐ |
| flutter_vlc_player | 基于VLC的播放器 | iOS, Android | ⭐⭐⭐ |
1. video_player(官方基础)
安装
yaml
dependencies:
video_player: ^2.7.0
基础使用
dart
import 'package:video_player/video_player.dart';
import 'package:flutter/material.dart';
class BasicVideoPlayer extends StatefulWidget {
@override
_BasicVideoPlayerState createState() => _BasicVideoPlayerState();
}
class _BasicVideoPlayerState extends State<BasicVideoPlayer> {
late VideoPlayerController _controller;
late Future<void> _initializeVideoPlayerFuture;
@override
void initState() {
super.initState();
// 网络视频
_controller = VideoPlayerController.network(
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
);
// 本地视频
// _controller = VideoPlayerController.asset('assets/videos/sample.mp4');
// 文件视频
// _controller = VideoPlayerController.file(File('/path/to/video.mp4'));
_initializeVideoPlayerFuture = _controller.initialize();
// 循环播放
_controller.setLooping(true);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('基础视频播放器')),
body: Center(
child: FutureBuilder(
future: _initializeVideoPlayerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
);
} else {
return CircularProgressIndicator();
}
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
if (_controller.value.isPlaying) {
_controller.pause();
} else {
_controller.play();
}
});
},
child: Icon(
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
2. chewie(最受欢迎的UI封装)
安装
yaml
dependencies:
chewie: ^1.5.0
video_player: ^2.7.0
基础使用
dart
import 'package:chewie/chewie.dart';
import 'package:video_player/video_player.dart';
import 'package:flutter/material.dart';
class ChewieVideoPlayer extends StatefulWidget {
@override
_ChewieVideoPlayerState createState() => _ChewieVideoPlayerState();
}
class _ChewieVideoPlayerState extends State<ChewieVideoPlayer> {
late VideoPlayerController _videoPlayerController;
late ChewieController _chewieController;
late Chewie _chewie;
@override
void initState() {
super.initState();
_initializePlayer();
}
Future<void> _initializePlayer() async {
_videoPlayerController = VideoPlayerController.network(
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
);
await _videoPlayerController.initialize();
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
autoPlay: true,
looping: true,
// 自定义播放器配置
materialProgressColors: ChewieProgressColors(
playedColor: Colors.red,
handleColor: Colors.red,
backgroundColor: Colors.grey,
bufferedColor: Colors.grey.shade400,
),
placeholder: Container(
color: Colors.black,
child: Center(child: CircularProgressIndicator()),
),
autoInitialize: true,
showControls: true,
allowMuting: true,
allowFullScreen: true,
fullScreenByDefault: false,
errorBuilder: (context, errorMessage) {
return Center(
child: Text(
errorMessage,
style: TextStyle(color: Colors.white),
),
);
},
);
_chewie = Chewie(controller: _chewieController);
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Chewie视频播放器')),
body: Column(
children: [
AspectRatio(
aspectRatio: 16 / 9,
child: _chewieController.videoPlayerController.value.isInitialized
? Chewie(controller: _chewieController)
: Center(child: CircularProgressIndicator()),
),
// 自定义控制按钮
Padding(
padding: EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(Icons.replay_10),
onPressed: () => _chewieController.seekTo(
_chewieController.videoPlayerController.value.position -
Duration(seconds: 10),
),
),
IconButton(
icon: Icon(Icons.forward_10),
onPressed: () => _chewieController.seekTo(
_chewieController.videoPlayerController.value.position +
Duration(seconds: 10),
),
),
IconButton(
icon: Icon(Icons.volume_up),
onPressed: () => _videoPlayerController.setVolume(1.0),
),
IconButton(
icon: Icon(Icons.volume_off),
onPressed: () => _videoPlayerController.setVolume(0.0),
),
],
),
),
],
),
);
}
@override
void dispose() {
_videoPlayerController.dispose();
_chewieController.dispose();
super.dispose();
}
}
3. better_player(功能最全面)
安装
yaml
dependencies:
better_player: ^0.0.83
完整功能示例
dart
import 'package:better_player/better_player.dart';
import 'package:flutter/material.dart';
class BetterPlayerExample extends StatefulWidget {
@override
_BetterPlayerExampleState createState() => _BetterPlayerExampleState();
}
class _BetterPlayerExampleState extends State<BetterPlayerExample> {
late BetterPlayerController _betterPlayerController;
late BetterPlayerConfiguration _betterPlayerConfiguration;
late BetterPlayerDataSource _dataSource;
// 播放列表
List<BetterPlayerDataSource> _playlist = [];
int _currentPlaylistIndex = 0;
@override
void initState() {
super.initState();
_setupPlayer();
}
void _setupPlayer() {
// 播放器配置
_betterPlayerConfiguration = BetterPlayerConfiguration(
aspectRatio: 16 / 9,
fit: BoxFit.contain,
autoPlay: false,
looping: false,
showPlaceholderUntilPlay: true,
placeholder: Center(
child: CircularProgressIndicator(),
),
controlsConfiguration: BetterPlayerControlsConfiguration(
enableSkips: true,
enableOverflowMenu: true,
enablePlaybackSpeed: true,
enableSubtitles: true,
enableAudioTracks: true,
enableFullscreen: true,
enableMute: true,
enableProgressText: true,
enableProgressBar: true,
enablePlayPause: true,
showControlsOnInitialize: true,
controlBarColor: Colors.black.withOpacity(0.7),
iconsColor: Colors.white,
overflowMenuIconsColor: Colors.white,
progressBarPlayedColor: Colors.red,
progressBarHandleColor: Colors.red,
progressBarBufferedColor: Colors.grey,
progressBarBackgroundColor: Colors.grey.shade800,
loadingColor: Colors.red,
),
eventListener: (BetterPlayerEvent event) {
if (event.betterPlayerEventType == BetterPlayerEventType.progress) {
// 播放进度更新
} else if (event.betterPlayerEventType ==
BetterPlayerEventType.play) {
print("开始播放");
} else if (event.betterPlayerEventType ==
BetterPlayerEventType.pause) {
print("暂停播放");
} else if (event.betterPlayerEventType ==
BetterPlayerEventType.finished) {
print("播放完成");
_playNextVideo();
}
},
);
// 数据源
_dataSource = BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
// 字幕
subtitles: [
BetterPlayerSubtitlesSource(
type: BetterPlayerSubtitlesSourceType.network,
url: 'https://example.com/subtitles.srt',
name: 'English',
),
],
// 视频质量
resolutions: {
"360p": "https://example.com/360p.mp4",
"720p": "https://example.com/720p.mp4",
"1080p": "https://example.com/1080p.mp4",
},
// 缓存配置
cacheConfiguration: BetterPlayerCacheConfiguration(
useCache: true,
maxCacheSize: 10 * 1024 * 1024, // 10MB
maxCacheFileSize: 5 * 1024 * 1024, // 5MB
),
// DRM支持
drmConfiguration: BetterPlayerDrmConfiguration(
drmType: BetterPlayerDrmType.widevine,
licenseUrl: 'https://example.com/license',
headers: {'Authorization': 'Bearer token'},
),
);
// 初始化播放列表
_playlist = [
BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
'https://example.com/video1.mp4',
subtitles: [
BetterPlayerSubtitlesSource(
type: BetterPlayerSubtitlesSourceType.network,
url: 'https://example.com/sub1.srt',
name: '中文',
),
],
),
BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
'https://example.com/video2.mp4',
),
BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
'https://example.com/video3.mp4',
),
];
_betterPlayerController = BetterPlayerController(
_betterPlayerConfiguration,
betterPlayerDataSource: _dataSource,
);
}
void _playNextVideo() {
if (_currentPlaylistIndex < _playlist.length - 1) {
_currentPlaylistIndex++;
_betterPlayerController.setupDataSource(_playlist[_currentPlaylistIndex]);
_betterPlayerController.play();
}
}
void _playPreviousVideo() {
if (_currentPlaylistIndex > 0) {
_currentPlaylistIndex--;
_betterPlayerController.setupDataSource(_playlist[_currentPlaylistIndex]);
_betterPlayerController.play();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Better Player'),
actions: [
// 画中画模式
IconButton(
icon: Icon(Icons.picture_in_picture),
onPressed: () {
_betterPlayerController.enablePictureInPicture(
'https://example.com/video.mp4',
);
},
),
// 下载视频
IconButton(
icon: Icon(Icons.download),
onPressed: () async {
await _betterPlayerController.downloadVideo(
'https://example.com/video.mp4',
'video.mp4',
);
},
),
],
),
body: Column(
children: [
// 播放器
AspectRatio(
aspectRatio: 16 / 9,
child: BetterPlayer(
controller: _betterPlayerController,
),
),
// 自定义控制区域
Padding(
padding: EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// 播放列表控制
IconButton(
icon: Icon(Icons.skip_previous),
onPressed: _playPreviousVideo,
),
// 播放速度控制
PopupMenuButton<double>(
itemBuilder: (context) => [0.5, 0.75, 1.0, 1.25, 1.5, 2.0]
.map((speed) => PopupMenuItem(
child: Text('${speed}x'),
value: speed,
))
.toList(),
onSelected: (speed) {
_betterPlayerController.setSpeed(speed);
},
child: Row(
children: [
Icon(Icons.speed),
SizedBox(width: 4),
Text('速度'),
],
),
),
// 字幕选择
PopupMenuButton<String>(
itemBuilder: (context) => ['关闭', '中文', '英文', '日文']
.map((subtitle) => PopupMenuItem(
child: Text(subtitle),
value: subtitle,
))
.toList(),
onSelected: (subtitle) {
if (subtitle == '关闭') {
_betterPlayerController.setSubtitles(null);
} else {
// 设置字幕
}
},
child: Row(
children: [
Icon(Icons.subtitles),
SizedBox(width: 4),
Text('字幕'),
],
),
),
// 音频轨道
PopupMenuButton<String>(
itemBuilder: (context) => ['中文', '英文']
.map((audio) => PopupMenuItem(
child: Text(audio),
value: audio,
))
.toList(),
onSelected: (audio) {
// 切换音轨
},
child: Row(
children: [
Icon(Icons.audiotrack),
SizedBox(width: 4),
Text('音轨'),
],
),
),
// 下一集
IconButton(
icon: Icon(Icons.skip_next),
onPressed: _playNextVideo,
),
],
),
),
// 播放列表
Expanded(
child: ListView.builder(
itemCount: _playlist.length,
itemBuilder: (context, index) {
return ListTile(
leading: Icon(Icons.play_circle_filled),
title: Text('视频 ${index + 1}'),
subtitle: Text('时长: 10:00'),
trailing: _currentPlaylistIndex == index
? Icon(Icons.play_arrow, color: Colors.red)
: null,
onTap: () {
setState(() {
_currentPlaylistIndex = index;
_betterPlayerController.setupDataSource(
_playlist[index],
);
_betterPlayerController.play();
});
},
);
},
),
),
],
),
// 底部控制栏
bottomNavigationBar: BottomAppBar(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// 当前播放时间
StreamBuilder<Duration>(
stream: _betterPlayerController.playerEventStream
.where((event) => event.betterPlayerEventType ==
BetterPlayerEventType.progress)
.map((event) => event.parameters!['progress'] as Duration),
builder: (context, snapshot) {
return Text(
_formatDuration(snapshot.data ?? Duration.zero),
style: TextStyle(fontSize: 12),
);
},
),
// 视频信息
Text(
'${_betterPlayerController.videoPlayerController?.value.size.width.toInt()}x'
'${_betterPlayerController.videoPlayerController?.value.size.height.toInt()}',
style: TextStyle(fontSize: 12),
),
// 网络状态
StreamBuilder<BetterPlayerEvent>(
stream: _betterPlayerController.playerEventStream,
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.data!.betterPlayerEventType ==
BetterPlayerEventType.exception) {
return Icon(Icons.error, color: Colors.red, size: 20);
}
return Icon(Icons.wifi, color: Colors.green, size: 20);
},
),
],
),
),
),
);
}
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));
return [if (duration.inHours > 0) hours, minutes, seconds].join(':');
}
@override
void dispose() {
_betterPlayerController.dispose();
super.dispose();
}
}
4. fijkplayer(基于ijkplayer)
安装
yaml
dependencies:
fijkplayer: ^0.10.1
使用示例
dart
import 'package:fijkplayer/fijkplayer.dart';
import 'package:flutter/material.dart';
class FijkPlayerExample extends StatefulWidget {
@override
_FijkPlayerExampleState createState() => _FijkPlayerExampleState();
}
class _FijkPlayerExampleState extends State<FijkPlayerExample> {
final FijkPlayer player = FijkPlayer();
@override
void initState() {
super.initState();
// 设置播放源
player.setDataSource(
'https://example.com/video.mp4',
autoPlay: true,
);
// 监听播放器事件
player.addListener(_playerListener);
}
void _playerListener() {
if (player.state == FijkState.completed) {
print('播放完成');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('FijkPlayer')),
body: Column(
children: [
// 播放器视图
FijkView(
player: player,
panelBuilder: fijkPanel2Builder(
// 自定义控制面板
backgroundColor: Colors.black54,
iconColor: Colors.white,
textStyle: TextStyle(color: Colors.white, fontSize: 12),
sliderColor: Colors.red,
),
),
// 自定义控制
Padding(
padding: EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(Icons.replay_10),
onPressed: () => player.seekTo(player.currentPos - 10000),
),
IconButton(
icon: Icon(Icons.volume_up),
onPressed: () => player.setVolume(1.0),
),
IconButton(
icon: Icon(Icons.high_quality),
onPressed: () {
// 切换清晰度
},
),
],
),
),
],
),
);
}
@override
void dispose() {
player.release();
super.dispose();
}
}
5. 自定义视频播放器
创建自定义播放器控件
dart
import 'package:video_player/video_player.dart';
import 'package:flutter/material.dart';
class CustomVideoPlayer extends StatefulWidget {
final String videoUrl;
final bool autoPlay;
final bool looping;
const CustomVideoPlayer({
Key? key,
required this.videoUrl,
this.autoPlay = false,
this.looping = false,
}) : super(key: key);
@override
_CustomVideoPlayerState createState() => _CustomVideoPlayerState();
}
class _CustomVideoPlayerState extends State<CustomVideoPlayer> {
late VideoPlayerController _controller;
bool _isPlaying = false;
bool _isBuffering = false;
bool _showControls = true;
Timer? _hideControlsTimer;
@override
void initState() {
super.initState();
_initializePlayer();
}
Future<void> _initializePlayer() async {
_controller = VideoPlayerController.network(widget.videoUrl)
..addListener(_videoListener)
..initialize().then((_) {
if (widget.autoPlay) {
_controller.play();
}
setState(() {});
});
}
void _videoListener() {
if (_controller.value.isBuffering) {
setState(() => _isBuffering = true);
} else {
setState(() => _isBuffering = false);
}
if (_controller.value.isPlaying != _isPlaying) {
setState(() => _isPlaying = _controller.value.isPlaying);
}
}
void _togglePlayPause() {
setState(() {
if (_controller.value.isPlaying) {
_controller.pause();
} else {
_controller.play();
}
});
}
void _seekForward() {
_controller.seekTo(_controller.value.position + Duration(seconds: 10));
}
void _seekBackward() {
_controller.seekTo(_controller.value.position - Duration(seconds: 10));
}
void _toggleFullScreen() {
// 实现全屏逻辑
}
void _showControlsTemporarily() {
setState(() => _showControls = true);
_hideControlsTimer?.cancel();
_hideControlsTimer = Timer(Duration(seconds: 3), () {
if (_controller.value.isPlaying) {
setState(() => _showControls = false);
}
});
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _showControlsTemporarily,
child: Stack(
alignment: Alignment.bottomCenter,
children: [
// 视频
if (_controller.value.isInitialized)
AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
)
else
Container(
color: Colors.black,
child: Center(child: CircularProgressIndicator()),
),
// 缓冲指示器
if (_isBuffering)
Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
// 控制层
if (_showControls && _controller.value.isInitialized)
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Colors.black.withOpacity(0.8),
Colors.transparent,
],
),
),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 进度条
_buildProgressBar(),
SizedBox(height: 8),
// 控制按钮
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// 左侧控制
Row(
children: [
IconButton(
icon: Icon(Icons.replay_10, color: Colors.white),
onPressed: _seekBackward,
),
IconButton(
icon: Icon(
_isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white,
),
onPressed: _togglePlayPause,
),
IconButton(
icon: Icon(Icons.forward_10, color: Colors.white),
onPressed: _seekForward,
),
],
),
// 右侧控制
Row(
children: [
IconButton(
icon: Icon(Icons.volume_up, color: Colors.white),
onPressed: () {
_controller.setVolume(1.0);
},
),
IconButton(
icon: Icon(Icons.fullscreen, color: Colors.white),
onPressed: _toggleFullScreen,
),
],
),
],
),
],
),
),
),
// 播放/暂停按钮(居中)
if (!_showControls && _controller.value.isInitialized)
GestureDetector(
onTap: _togglePlayPause,
child: Container(
color: Colors.transparent,
child: Center(
child: AnimatedOpacity(
opacity: _isPlaying ? 0 : 1,
duration: Duration(milliseconds: 300),
child: Icon(
Icons.play_circle_filled,
color: Colors.white.withOpacity(0.7),
size: 80,
),
),
),
),
),
],
),
);
}
Widget _buildProgressBar() {
return Row(
children: [
// 当前时间
Text(
_formatDuration(_controller.value.position),
style: TextStyle(color: Colors.white, fontSize: 12),
),
Expanded(
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: Colors.red,
inactiveTrackColor: Colors.grey,
trackHeight: 2,
thumbColor: Colors.red,
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 6),
overlayColor: Colors.red.withAlpha(32),
overlayShape: RoundSliderOverlayShape(overlayRadius: 12),
),
child: Slider(
value: _controller.value.position.inMilliseconds.toDouble(),
min: 0,
max: _controller.value.duration.inMilliseconds.toDouble(),
onChanged: (value) {
_controller.seekTo(Duration(milliseconds: value.toInt()));
},
onChangeStart: (_) {
_hideControlsTimer?.cancel();
},
onChangeEnd: (_) {
_showControlsTemporarily();
},
),
),
),
// 总时长
Text(
_formatDuration(_controller.value.duration),
style: TextStyle(color: Colors.white, fontSize: 12),
),
],
);
}
String _formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final minutes = twoDigits(duration.inMinutes);
final seconds = twoDigits(duration.inSeconds.remainder(60));
return '$minutes:$seconds';
}
@override
void dispose() {
_hideControlsTimer?.cancel();
_controller.removeListener(_videoListener);
_controller.dispose();
super.dispose();
}
}
6. 视频播放器最佳实践
性能优化
dart
class OptimizedVideoPlayer extends StatefulWidget {
@override
_OptimizedVideoPlayerState createState() => _OptimizedVideoPlayerState();
}
class _OptimizedVideoPlayerState extends State<OptimizedVideoPlayer>
with WidgetsBindingObserver {
late BetterPlayerController _controller;
bool _isInBackground = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
_initializePlayer();
}
void _initializePlayer() {
_controller = BetterPlayerController(
BetterPlayerConfiguration(
autoPlay: true,
looping: false,
// 预加载
preloadDuration: Duration(seconds: 10),
// 缓存配置
cacheConfiguration: BetterPlayerCacheConfiguration(
useCache: true,
maxCacheSize: 100 * 1024 * 1024, // 100MB
),
// 自适应比特率
abrAlgorithm: BetterPlayerAbrAlgorithm.quality,
// 网络超时
networkTimeout: Duration(seconds: 30),
// 错误重试
retryTimeout: Duration(seconds: 5),
retryCount: 3,
),
);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.paused) {
// 应用进入后台,暂停播放
_controller.pause();
_isInBackground = true;
} else if (state == AppLifecycleState.resumed && _isInBackground) {
// 应用回到前台,恢复播放
_controller.play();
_isInBackground = false;
}
}
@override
Widget build(BuildContext context) {
return BetterPlayer(controller: _controller);
}
@override
void dispose() {
WidgetsBinding.instance!.removeObserver(this);
_controller.dispose();
super.dispose();
}
}
错误处理
dart
class ErrorHandledVideoPlayer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BetterPlayer(
controller: BetterPlayerController(
BetterPlayerConfiguration(
errorBuilder: (context, errorMessage) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error, color: Colors.red, size: 50),
SizedBox(height: 16),
Text('播放失败: $errorMessage'),
SizedBox(height: 16),
ElevatedButton(
onPressed: () {
// 重试逻辑
},
child: Text('重试'),
),
],
);
},
),
),
);
}
}
7. 平台特定配置
Android配置
xml
<!-- android/app/src/main/AndroidManifest.xml -->
<manifest>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application>
<!-- 允许HTTP请求 -->
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
</application>
</manifest>
iOS配置
xml
<!-- ios/Runner/Info.plist -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<!-- 后台音频播放 -->
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
总结
- 简单需求 :使用
chewie+video_player - 复杂需求 :使用
better_player - 特殊格式 :使用
fijkplayer(支持更多格式) - 自定义需求 :基于
video_player自己封装 - 直播流:所有播放器都支持 HLS、DASH 等流媒体协议
选择合适的播放器组件,根据需求进行配置和自定义,可以大大提升视频播放体验。