欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
🎯 前言:为什么需要 Chewie?
在移动应用开发中,虽然 video_player 提供了基础的视频播放能力,但它缺少播放控制UI。这时候就需要 chewie 了!
场景一 :短视频应用需要完整的播放控制界面
场景二 :在线教育应用需要专业的视频播放器
场景三 :直播应用需要自定义播放控制
场景四 :视频分享应用需要美观的播放界面
场景五:企业培训应用需要带字幕的视频播放
chewie 是基于 video_player 的高级封装,提供了开箱即用的播放控制UI,支持 Material Design 和 Cupertino 风格,让你无需从零开发播放器界面!
🚀 核心能力一览
| 功能特性 | 详细说明 | OpenHarmony 支持 |
|---|---|---|
| Material风格UI | Android风格的播放控制界面 | ✅ |
| Cupertino风格UI | iOS风格的播放控制界面 | ✅ |
| Desktop风格UI | 桌面端播放控制界面 | ✅ |
| 全屏播放 | 支持全屏模式切换 | ✅ |
| 字幕支持 | 内置字幕显示功能 | ✅ |
| 自定义控制 | 可自定义播放控制按钮 | ✅ |
| 播放速度调节 | 支持倍速播放 | ✅ |
| 自动播放 | 支持视频自动播放 | ✅ |
| 循环播放 | 支持视频循环播放 | ✅ |
| 进度条拖动 | 支持拖动进度条跳转 | ✅ |
| 音量控制 | 支持音量调节 | ✅ |
| 缩放平移 | 支持视频缩放和平移 | ✅ |
支持的视频源
| 视频源类型 | 用途 | OpenHarmony 支持 |
|---|---|---|
| 网络视频 | 播放在线视频 | ✅ |
| 本地文件 | 播放本地视频文件 | ✅ |
| 资源文件 | 播放应用内置视频 | ✅ |
📱 如何运行这些示例
运行步骤
- 创建新项目或使用现有项目
- 配置依赖(见下方)
- 配置权限(见下方)
- 复制示例代码到
lib/main.dart - 运行应用:
flutter run
⚠️ 常见问题
-
问题:视频无法播放
- 解决:检查网络权限配置,确保视频URL可访问
-
问题:控制界面不显示
- 解决:确保 ChewieController 正确初始化
⚙️ 环境准备:三步走
第一步:添加依赖
📄 pubspec.yaml:
yaml
dependencies:
flutter:
sdk: flutter
# 添加 video_player 依赖(使用 pub.dev 版本)
video_player: ^2.9.1
# 添加 chewie 依赖(OpenHarmony 适配版本)
chewie:
git:
url: https://atomgit.com/openharmony-sig/fluttertpc_chewie.git
dev_dependencies:
flutter_test:
sdk: flutter
# 添加 video_player_ohos 鸿蒙平台支持
video_player_ohos:
git:
url: "https://gitee.com/openharmony-sig/flutter_packages.git"
path: "packages/video_player/video_player_ohos"
# 添加 chewie_ohos 鸿蒙平台支持
chewie_ohos:
git:
url: https://atomgit.com/openharmony-sig/fluttertpc_chewie.git
path: ohos
执行命令:
bash
flutter pub get
第二步:配置权限
2.1 配置网络权限
📄 ohos/entry/src/main/module.json5:
json
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:network_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}
2.2 配置权限说明
📄 ohos/entry/src/main/resources/base/element/string.json:
json
{
"string": [
{
"name": "network_reason",
"value": "播放网络视频"
}
]
}
第三步:导入包
在 Dart 文件中导入:
dart
import 'package:flutter/material.dart';
import 'package:chewie/chewie.dart';
import 'package:video_player/video_player.dart';
📚 基础用法:从简单开始
示例1:基础视频播放器
最简单的视频播放器实现:
dart
class BasicVideoPlayer extends StatefulWidget {
const BasicVideoPlayer({super.key});
@override
State<BasicVideoPlayer> createState() => _BasicVideoPlayerState();
}
class _BasicVideoPlayerState extends State<BasicVideoPlayer> {
late VideoPlayerController _videoPlayerController;
ChewieController? _chewieController;
@override
void initState() {
super.initState();
_initializePlayer();
}
Future<void> _initializePlayer() async {
_videoPlayerController = VideoPlayerController.networkUrl(
Uri.parse('https://media.w3.org/2010/05/sintel/trailer.mp4'),
);
await _videoPlayerController.initialize();
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
autoPlay: true,
looping: false,
);
setState(() {});
}
@override
void dispose() {
_videoPlayerController.dispose();
_chewieController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('基础视频播放器')),
body: Center(
child: _chewieController != null &&
_chewieController!.videoPlayerController.value.isInitialized
? Chewie(controller: _chewieController!)
: const CircularProgressIndicator(),
),
);
}
}
核心要点:
- 先创建
VideoPlayerController - 等待视频初始化完成
- 创建
ChewieController并传入VideoPlayerController - 使用
Chewiewidget 显示播放器
🎨 进阶用法:自定义配置
示例2:自动播放和循环播放
dart
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
autoPlay: true, // 自动播放
looping: true, // 循环播放
showControls: true, // 显示控制界面
);
示例3:自定义控制界面显示时间
dart
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
autoPlay: true,
hideControlsTimer: const Duration(seconds: 3), // 3秒后隐藏控制界面
);
示例4:添加字幕支持
dart
final subtitles = [
Subtitle(
index: 0,
start: Duration.zero,
end: const Duration(seconds: 10),
text: '这是第一段字幕',
),
Subtitle(
index: 1,
start: const Duration(seconds: 10),
end: const Duration(seconds: 20),
text: '这是第二段字幕',
),
];
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
autoPlay: true,
subtitle: Subtitles(subtitles),
subtitleBuilder: (context, subtitle) => Container(
padding: const EdgeInsets.all(10.0),
child: Text(
subtitle.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 16,
backgroundColor: Colors.black54,
),
),
),
);
示例5:自定义附加选项
dart
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
autoPlay: true,
additionalOptions: (context) {
return <OptionItem>[
OptionItem(
onTap: () {
// 切换视频源
_switchVideoSource();
},
iconData: Icons.live_tv_sharp,
title: '切换视频',
),
OptionItem(
onTap: () {
// 分享视频
_shareVideo();
},
iconData: Icons.share,
title: '分享',
),
];
},
);
示例6:全屏播放控制
dart
// 进入全屏
_chewieController?.enterFullScreen();
// 退出全屏
_chewieController?.exitFullScreen();
// 切换全屏状态
_chewieController?.toggleFullScreen();
示例7:播放控制
dart
// 播放
await _chewieController?.videoPlayerController.play();
// 暂停
await _chewieController?.videoPlayerController.pause();
// 跳转到指定位置
await _chewieController?.videoPlayerController.seekTo(
const Duration(seconds: 30),
);
// 设置音量(0.0 - 1.0)
await _chewieController?.videoPlayerController.setVolume(0.5);
// 设置循环播放
await _chewieController?.videoPlayerController.setLooping(true);
🎯 完整示例:专业视频播放器
下面是一个功能完整的视频播放器示例,包含多视频源切换、全屏控制、字幕显示等功能:

dart
import 'package:flutter/material.dart';
import 'package:chewie/chewie.dart';
import 'package:video_player/video_player.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chewie 视频播放器',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const ProfessionalVideoPlayer(),
);
}
}
class ProfessionalVideoPlayer extends StatefulWidget {
const ProfessionalVideoPlayer({super.key});
@override
State<ProfessionalVideoPlayer> createState() => _ProfessionalVideoPlayerState();
}
class _ProfessionalVideoPlayerState extends State<ProfessionalVideoPlayer> {
late VideoPlayerController _videoPlayerController;
ChewieController? _chewieController;
int _currentVideoIndex = 0;
// 视频源列表
final List<String> _videoUrls = [
'https://media.w3.org/2010/05/sintel/trailer.mp4',
'http://vjs.zencdn.net/v/oceans.mp4',
];
@override
void initState() {
super.initState();
_initializePlayer();
}
Future<void> _initializePlayer() async {
_videoPlayerController = VideoPlayerController.networkUrl(
Uri.parse(_videoUrls[_currentVideoIndex]),
);
await _videoPlayerController.initialize();
_createChewieController();
setState(() {});
}
void _createChewieController() {
// 创建字幕
final subtitles = [
Subtitle(
index: 0,
start: Duration.zero,
end: const Duration(seconds: 10),
text: const TextSpan(
children: [
TextSpan(
text: '欢迎',
style: TextStyle(color: Colors.red, fontSize: 22),
),
TextSpan(
text: '使用',
style: TextStyle(color: Colors.green, fontSize: 20),
),
TextSpan(
text: 'Chewie',
style: TextStyle(color: Colors.blue, fontSize: 18),
),
],
),
),
Subtitle(
index: 1,
start: const Duration(seconds: 10),
end: const Duration(seconds: 20),
text: '这是一个专业的视频播放器',
),
];
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
autoPlay: true,
looping: true,
hideControlsTimer: const Duration(seconds: 2),
// 添加自定义选项
additionalOptions: (context) {
return <OptionItem>[
OptionItem(
onTap: _switchVideo,
iconData: Icons.live_tv_sharp,
title: '切换视频',
),
];
},
// 配置字幕
subtitle: Subtitles(subtitles),
subtitleBuilder: (context, dynamic subtitle) => Container(
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: Colors.black54,
borderRadius: BorderRadius.circular(8),
),
child: subtitle is InlineSpan
? RichText(text: subtitle)
: Text(
subtitle.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 16,
),
),
),
);
}
Future<void> _switchVideo() async {
await _videoPlayerController.pause();
_currentVideoIndex = (_currentVideoIndex + 1) % _videoUrls.length;
await _initializePlayer();
}
@override
void dispose() {
_videoPlayerController.dispose();
_chewieController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('专业视频播放器'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: Column(
children: [
// 视频播放器
Expanded(
child: Center(
child: _chewieController != null &&
_chewieController!.videoPlayerController.value.isInitialized
? Chewie(controller: _chewieController!)
: const Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 20),
Text('加载中...'),
],
),
),
),
// 控制按钮
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, -2),
),
],
),
child: Column(
children: [
// 全屏按钮
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: () {
_chewieController?.enterFullScreen();
},
icon: const Icon(Icons.fullscreen),
label: const Text('全屏播放'),
),
),
const SizedBox(height: 8),
// 视频信息
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
children: [
const Icon(Icons.video_library, color: Colors.blue),
const SizedBox(height: 4),
Text(
'视频 ${_currentVideoIndex + 1}/${_videoUrls.length}',
style: const TextStyle(fontSize: 12),
),
],
),
Column(
children: [
const Icon(Icons.subtitles, color: Colors.green),
const SizedBox(height: 4),
const Text(
'字幕已启用',
style: TextStyle(fontSize: 12),
),
],
),
Column(
children: [
const Icon(Icons.loop, color: Colors.orange),
const SizedBox(height: 4),
const Text(
'循环播放',
style: TextStyle(fontSize: 12),
),
],
),
],
),
],
),
),
],
),
);
}
}
完整示例说明:
- 多视频源切换:支持在多个视频之间切换
- 字幕显示:支持富文本字幕和普通文本字幕
- 自定义选项:在播放器菜单中添加自定义按钮
- 全屏控制:提供全屏播放功能
- 循环播放:视频自动循环播放
- 加载状态:显示加载进度指示器
🎨 UI风格切换
Chewie 支持三种UI风格:
Material Design 风格(Android)
dart
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
// 默认使用 Material Design 风格
);
Cupertino 风格(iOS)
通过设置主题的 platform 来切换:
dart
MaterialApp(
theme: ThemeData(
platform: TargetPlatform.iOS, // 使用 iOS 风格
),
home: YourWidget(),
);
Desktop 风格(桌面端)
dart
MaterialApp(
theme: ThemeData(
platform: TargetPlatform.windows, // 使用桌面风格
),
home: YourWidget(),
);
📊 ChewieController 核心属性
基础配置
| 属性 | 类型 | 说明 | 默认值 |
|---|---|---|---|
| videoPlayerController | VideoPlayerController | 视频播放控制器(必需) | - |
| autoPlay | bool | 是否自动播放 | false |
| looping | bool | 是否循环播放 | false |
| showControls | bool | 是否显示控制界面 | true |
| showControlsOnInitialize | bool | 初始化时是否显示控制界面 | true |
控制界面配置
| 属性 | 类型 | 说明 | 默认值 |
|---|---|---|---|
| hideControlsTimer | Duration | 控制界面自动隐藏时间 | 3秒 |
| showOptions | bool | 是否显示选项按钮 | true |
| optionsTranslation | OptionsTranslation | 选项按钮文本翻译 | null |
| draggableProgressBar | bool | 进度条是否可拖动 | true |
高级配置
| 属性 | 类型 | 说明 | 默认值 |
|---|---|---|---|
| startAt | Duration | 起始播放位置 | 0 |
| zoomAndPan | bool | 是否允许缩放和平移 | false |
| maxScale | double | 最大缩放比例 | 2.5 |
| subtitle | Subtitles | 字幕配置 | null |
| subtitleBuilder | SubtitleBuilder | 字幕构建器 | null |
| additionalOptions | AdditionalOptions | 自定义选项按钮 | null |
🎯 常用方法
播放控制方法
dart
// 播放
await _chewieController?.play();
// 暂停
await _chewieController?.pause();
// 跳转到指定位置
await _chewieController?.seekTo(Duration(seconds: 30));
// 设置音量
await _chewieController?.setVolume(0.5);
// 设置循环播放
await _chewieController?.setLooping(true);
全屏控制方法
dart
// 进入全屏
_chewieController?.enterFullScreen();
// 退出全屏
_chewieController?.exitFullScreen();
// 切换全屏状态
_chewieController?.toggleFullScreen();
视频切换方法
dart
// 切换视频源
Future<void> switchVideo(String newUrl) async {
await _videoPlayerController.pause();
_videoPlayerController = VideoPlayerController.networkUrl(
Uri.parse(newUrl),
);
await _videoPlayerController.initialize();
// 使用 copyWith 更新控制器
_chewieController = _chewieController!.copyWith(
videoPlayerController: _videoPlayerController,
autoPlay: true,
);
setState(() {});
}
🔧 高级技巧
技巧1:自定义进度条颜色
dart
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
materialProgressColors: ChewieProgressColors(
playedColor: Colors.red, // 已播放部分颜色
handleColor: Colors.blue, // 拖动手柄颜色
backgroundColor: Colors.grey, // 背景颜色
bufferedColor: Colors.lightGreen, // 缓冲部分颜色
),
);
技巧2:自定义占位图
dart
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
placeholder: Container(
color: Colors.grey,
child: const Center(
child: Icon(
Icons.play_circle_outline,
size: 100,
color: Colors.white,
),
),
),
);
技巧3:监听播放状态
dart
_videoPlayerController.addListener(() {
if (_videoPlayerController.value.isPlaying) {
print('视频正在播放');
} else {
print('视频已暂停');
}
if (_videoPlayerController.value.position ==
_videoPlayerController.value.duration) {
print('视频播放完成');
}
});
技巧4:处理错误
dart
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
errorBuilder: (context, errorMessage) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.error_outline,
color: Colors.red,
size: 60,
),
const SizedBox(height: 16),
Text(
'播放出错: $errorMessage',
style: const TextStyle(color: Colors.white),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
// 重新加载视频
_initializePlayer();
},
child: const Text('重试'),
),
],
),
);
},
);
💡 最佳实践
1. 资源管理
dart
@override
void dispose() {
// 先释放 ChewieController
_chewieController?.dispose();
// 再释放 VideoPlayerController
_videoPlayerController.dispose();
super.dispose();
}
2. 异步初始化
dart
Future<void> _initializePlayer() async {
try {
_videoPlayerController = VideoPlayerController.networkUrl(
Uri.parse(videoUrl),
);
await _videoPlayerController.initialize();
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
autoPlay: true,
);
if (mounted) {
setState(() {});
}
} catch (e) {
print('初始化失败: $e');
// 显示错误提示
}
}
3. 网络视频优化
dart
// 使用较低质量的视频作为预览
final previewUrl = 'https://example.com/video_preview.mp4';
final hdUrl = 'https://example.com/video_hd.mp4';
// 先加载预览版本
_videoPlayerController = VideoPlayerController.networkUrl(
Uri.parse(previewUrl),
);
// 用户点击高清按钮后切换
void switchToHD() async {
final currentPosition = await _videoPlayerController.position;
_videoPlayerController = VideoPlayerController.networkUrl(
Uri.parse(hdUrl),
);
await _videoPlayerController.initialize();
await _videoPlayerController.seekTo(currentPosition);
await _videoPlayerController.play();
}
🐛 常见问题与解决方案
问题1:视频无法播放
原因:
- 网络权限未配置
- 视频URL不可访问
- 视频格式不支持
解决方案:
dart
// 1. 检查权限配置
// 2. 验证视频URL
// 3. 使用支持的视频格式(MP4、WebM等)
// 添加错误处理
_videoPlayerController.addListener(() {
if (_videoPlayerController.value.hasError) {
print('播放错误: ${_videoPlayerController.value.errorDescription}');
}
});
问题2:控制界面不显示
原因:
- showControls 设置为 false
- ChewieController 未正确初始化
解决方案:
dart
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
showControls: true, // 确保设置为 true
showControlsOnInitialize: true,
);
问题3:全屏后无法返回
原因:
- 系统导航栏被隐藏
解决方案:
dart
// 监听全屏状态
_chewieController?.addListener(() {
if (_chewieController!.isFullScreen) {
// 进入全屏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
} else {
// 退出全屏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
}
});
问题4:内存占用过高
原因:
- 多个视频同时加载
- 未正确释放资源
解决方案:
dart
// 切换视频前先释放旧资源
Future<void> switchVideo(String newUrl) async {
await _videoPlayerController.pause();
await _videoPlayerController.dispose();
_videoPlayerController = VideoPlayerController.networkUrl(
Uri.parse(newUrl),
);
await _videoPlayerController.initialize();
setState(() {});
}
📚 API 参考
ChewieController 构造函数
dart
ChewieController({
required VideoPlayerController videoPlayerController,
bool autoPlay = false,
bool looping = false,
bool showControls = true,
bool showControlsOnInitialize = true,
Duration? hideControlsTimer,
bool showOptions = true,
OptionsTranslation? optionsTranslation,
bool draggableProgressBar = true,
Duration? startAt,
bool zoomAndPan = false,
double maxScale = 2.5,
Subtitles? subtitle,
SubtitleBuilder? subtitleBuilder,
AdditionalOptions? additionalOptions,
ChewieProgressColors? materialProgressColors,
Widget? placeholder,
ErrorBuilder? errorBuilder,
})
主要方法
| 方法 | 返回类型 | 说明 |
|---|---|---|
| play() | Future<void> |
播放视频 |
| pause() | Future<void> |
暂停视频 |
| seekTo(Duration) | Future<void> |
跳转到指定位置 |
| setVolume(double) | Future<void> |
设置音量 |
| setLooping(bool) | Future<void> |
设置循环播放 |
| enterFullScreen() | void | 进入全屏 |
| exitFullScreen() | void | 退出全屏 |
| toggleFullScreen() | void | 切换全屏状态 |
| dispose() | void | 释放资源 |
🎓 总结
通过本文,你已经掌握了:
✅ Chewie 的基本概念和核心能力
✅ 如何配置依赖和权限
✅ 基础视频播放器的实现
✅ 自定义播放控制和UI风格
✅ 字幕显示和自定义选项
✅ 全屏控制和视频切换
✅ 常见问题的解决方案
Chewie 让视频播放变得简单而专业!它基于 video_player 提供了完整的UI解决方案,让你无需从零开发播放器界面。无论是短视频应用、在线教育还是企业培训,Chewie 都能满足你的需求。
🔗 相关资源
💡 提示:如果你在使用过程中遇到问题,欢迎在社区提问交流!