Flutter学习之第三方组件:视频播放器控件

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>

总结

  1. 简单需求 :使用 chewie + video_player
  2. 复杂需求 :使用 better_player
  3. 特殊格式 :使用 fijkplayer(支持更多格式)
  4. 自定义需求 :基于 video_player 自己封装
  5. 直播流:所有播放器都支持 HLS、DASH 等流媒体协议

选择合适的播放器组件,根据需求进行配置和自定义,可以大大提升视频播放体验。

相关推荐
徒 花2 小时前
HCIP学习16 RIP 与 OSPF 路由重分布综合实验
网络·学习·智能路由器·hcip·ensp
EasyCVR2 小时前
GB28181/RTSP/ONVIF/RTMP/SDK视频汇聚平台EasyCVR构建通信基站智慧安防可视化管理体系
音视频
landuochong2002 小时前
智能体闭环进展:从学习、记忆、决策到执行
人工智能·学习·claudecode
map1e_zjc2 小时前
Java SpringBoot学习记录(4)
java·开发语言·学习
日光明媚2 小时前
FFmpeg 视频生成推理 Pipeline:Python 版常用函数封装(可直接集成)
python·深度学习·ai作画·aigc·音视频
xian_wwq2 小时前
【学习笔记】3 种零防御 UAC 绕过技术
笔记·学习
●VON2 小时前
【AI工具】本地部署 Dify + Ollama 实现无限 Token 智能体搭建
人工智能·学习·dify·智能体·本地·von
夜瞬2 小时前
NLP学习笔记04:情感分析——从词典方法到 BERT
笔记·学习·自然语言处理
v132665623683 小时前
BK7258 wifi6音视频soc芯片应用分析
嵌入式硬件·物联网·音视频·iot·wifi6