Flutter视频播放插件chewie_video_player的使用
Flutter视频播放插件chewie_video_player的使用
安装
首先,在你的 pubspec.yaml
文件中添加 video_player
作为依赖项。
iOS
如果你需要通过 http
(而不是 https
)访问视频,请确保在你的应用的 Info.plist
文件中添加适当的 NSAppTransportSecurity
权限。该文件位于 <项目根目录>/ios/Runner/Info.plist
。参见 Apple 的文档 获取更多详细信息。
Android
如果你使用的是网络视频,请确保在你的 AndroidManifest.xml
文件中添加以下权限:
<uses-permission android:name="android.permission.INTERNET"/>
该文件位于 <项目根目录>/android/app/src/main/AndroidManifest.xml
。
Web
注意: Web 平台不支持 dart:io
,因此避免使用 VideoPlayerController.file
构造函数。尝试使用该构造函数会抛出 UnimplementedError
。
不同浏览器可能有不同的视频播放能力(支持格式、自动播放等)。请参阅 video_player_web 包 获取更多相关信息。
VideoPlayerOptions.mixWithOthers
选项在 Web 上目前无法实现。如果你在 Web 上使用此选项,它将被静默忽略。
支持的格式
- 在 iOS 上,后台播放器是
AVPlayer
。支持的格式取决于 iOS 的版本。你可以查询AVURLAsset
类的audiovisualTypes
属性来获取支持的音频视频格式。 - 在 Android 上,后台播放器是
ExoPlayer
。请参阅 这里 获取支持的格式列表。 - 在 Web 上,可用格式取决于用户的浏览器(供应商和版本)。请参阅 video_player_web 包 获取更多特定信息。
示例
以下是一个使用 video_player
插件的简单示例:
import 'package:video_player/video_player.dart';
import 'package:flutter/material.dart';
void main() => runApp(VideoApp());
class VideoApp extends StatefulWidget {
[@override](/user/override)
_VideoAppState createState() => _VideoAppState();
}
class _VideoAppState extends State<VideoApp> {
VideoPlayerController _controller;
[@override](/user/override)
void initState() {
super.initState();
_controller = VideoPlayerController.network(
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4')
..initialize().then((_) {
// 确保在视频初始化后显示第一帧,即使播放按钮尚未按下。
setState(() {});
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Video Demo',
home: Scaffold(
body: Center(
child: _controller.value.isInitialized
? AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
)
: Container(),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_controller.value.isPlaying
? _controller.pause()
: _controller.play();
});
},
child: Icon(
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
),
),
),
);
}
[@override](/user/override)
void dispose() {
super.dispose();
_controller.dispose();
}
}
使用方法
以下是使用 video_player
插件的更详细的用法信息。
播放速度
你可以在 _controller
(VideoPlayerController
的实例)上调用 setPlaybackSpeed
方法设置播放速度。setPlaybackSpeed
接受一个 double
类型的速度值,表示视频的播放速率。
例如,当给定值为 2.0
时,视频将以两倍于正常播放速度的速度播放。
要了解播放速度的限制,请参阅 setPlaybackSpeed 方法文档。
此外,请参阅示例应用程序以获取播放速度实现的示例。
示例代码
以下是一个完整的示例代码,展示了如何在一个应用中使用多个视频播放器:
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
void main() {
runApp(
MaterialApp(
home: _App(),
),
);
}
class _App extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
key: const ValueKey<String>('home_page'),
appBar: AppBar(
title: const Text('Video player example'),
actions: <Widget>[
IconButton(
key: const ValueKey<String>('push_tab'),
icon: const Icon(Icons.navigation),
onPressed: () {
Navigator.push<_PlayerVideoAndPopPage>(
context,
MaterialPageRoute<_PlayerVideoAndPopPage>(
builder: (BuildContext context) => _PlayerVideoAndPopPage(),
),
);
},
)
],
bottom: const TabBar(
isScrollable: true,
tabs: <Widget>[
Tab(
icon: Icon(Icons.cloud),
text: 'Remote',
),
Tab(icon: Icon(Icons.insert_drive_file), text: 'Asset'),
Tab(icon: Icon(Icons.list), text: 'List example'),
],
),
),
body: TabBarView(
children: <Widget>[
_BumbleBeeRemoteVideo(),
_ButterFlyAssetVideo(),
_ButterFlyAssetVideoInList(),
],
),
),
);
}
}
class _ButterFlyAssetVideoInList extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
const _ExampleCard(title: 'Item a'),
const _ExampleCard(title: 'Item b'),
const _ExampleCard(title: 'Item c'),
const _ExampleCard(title: 'Item d'),
const _ExampleCard(title: 'Item e'),
const _ExampleCard(title: 'Item f'),
const _ExampleCard(title: 'Item g'),
Card(
child: Column(children: <Widget>[
Column(
children: <Widget>[
const ListTile(
leading: Icon(Icons.cake),
title: Text('Video video'),
),
Stack(
alignment: FractionalOffset.bottomRight +
const FractionalOffset(-0.1, -0.1),
children: <Widget>[
_ButterFlyAssetVideo(),
Image.asset('assets/flutter-mark-square-64.png'),
]),
],
),
])),
const _ExampleCard(title: 'Item h'),
const _ExampleCard(title: 'Item i'),
const _ExampleCard(title: 'Item j'),
const _ExampleCard(title: 'Item k'),
const _ExampleCard(title: 'Item l'),
],
);
}
}
class _ExampleCard extends StatelessWidget {
const _ExampleCard({Key? key, required this.title}) : super(key: key);
final String title;
[@override](/user/override)
Widget build(BuildContext context) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: const Icon(Icons.airline_seat_flat_angled),
title: Text(title),
),
ButtonBar(
children: <Widget>[
TextButton(
child: const Text('BUY TICKETS'),
onPressed: () {
/* ... */
},
),
TextButton(
child: const Text('SELL TICKETS'),
onPressed: () {
/* ... */
},
),
],
),
],
),
);
}
}
class _ButterFlyAssetVideo extends StatefulWidget {
[@override](/user/override)
_ButterFlyAssetVideoState createState() => _ButterFlyAssetVideoState();
}
class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> {
late VideoPlayerController _controller;
[@override](/user/override)
void initState() {
super.initState();
_controller = VideoPlayerController.asset('assets/Butterfly-209.mp4');
_controller.addListener(() {
setState(() {});
});
_controller.setLooping(true);
_controller.initialize().then((_) => setState(() {}));
_controller.play();
}
[@override](/user/override)
void dispose() {
_controller.dispose();
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.only(top: 20.0),
),
const Text('With assets mp4'),
Container(
padding: const EdgeInsets.all(20),
child: AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
VideoPlayer(_controller),
_ControlsOverlay(controller: _controller),
VideoProgressIndicator(_controller, allowScrubbing: true),
],
),
),
),
],
),
);
}
}
class _BumbleBeeRemoteVideo extends StatefulWidget {
[@override](/user/override)
_BumbleBeeRemoteVideoState createState() => _BumbleBeeRemoteVideoState();
}
class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> {
late VideoPlayerController _controller;
Future<ClosedCaptionFile> _loadCaptions() async {
final String fileContents = await DefaultAssetBundle.of(context)
.loadString('assets/bumble_bee_captions.vtt');
return WebVTTCaptionFile(fileContents); // For vtt files, use WebVTTCaptionFile
}
[@override](/user/override)
void initState() {
super.initState();
_controller = VideoPlayerController.network(
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
closedCaptionFile: _loadCaptions(),
videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true),
);
_controller.addListener(() {
setState(() {});
});
_controller.setLooping(true);
_controller.initialize();
}
[@override](/user/override)
void dispose() {
_controller.dispose();
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
Container(padding: const EdgeInsets.only(top: 20.0)),
const Text('With remote mp4'),
Container(
padding: const EdgeInsets.all(20),
child: AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
VideoPlayer(_controller),
ClosedCaption(text: _controller.value.caption.text),
_ControlsOverlay(controller: _controller),
VideoProgressIndicator(_controller, allowScrubbing: true),
],
),
),
),
],
),
);
}
}
class _ControlsOverlay extends StatelessWidget {
const _ControlsOverlay({Key? key, required this.controller}) : super(key: key);
static const List<Duration> _exampleCaptionOffsets = [
Duration(seconds: -10),
Duration(seconds: -3),
Duration(seconds: -1, milliseconds: -500),
Duration(milliseconds: -250),
Duration(milliseconds: 0),
Duration(milliseconds: 250),
Duration(seconds: 1, milliseconds: 500),
Duration(seconds: 3),
Duration(seconds: 10),
];
static const List<double> _examplePlaybackRates = [
0.25,
0.5,
1.0,
1.5,
2.0,
3.0,
5.0,
10.0,
];
final VideoPlayerController controller;
[@override](/user/override)
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
AnimatedSwitcher(
duration: const Duration(milliseconds: 50),
reverseDuration: const Duration(milliseconds: 200),
child: controller.value.isPlaying
? const SizedBox.shrink()
: Container(
color: Colors.black26,
child: const Center(
child: Icon(
Icons.play_arrow,
color: Colors.white,
size: 100.0,
semanticLabel: 'Play',
),
),
),
),
GestureDetector(
onTap: () {
controller.value.isPlaying ? controller.pause() : controller.play();
},
),
Align(
alignment: Alignment.topLeft,
child: PopupMenuButton<Duration>(
initialValue: controller.value.captionOffset,
tooltip: 'Caption Offset',
onSelected: (Duration delay) {
controller.setCaptionOffset(delay);
},
itemBuilder: (BuildContext context) {
return <PopupMenuItem<Duration>>[
for (final Duration offsetDuration in _exampleCaptionOffsets)
PopupMenuItem<Duration>(
value: offsetDuration,
child: Text('${offsetDuration.inMilliseconds}ms'),
)
];
},
child: Padding(
padding: const EdgeInsets.symmetric(
// 使用较少的垂直填充,因为文本在水平方向上也较长,
// 所以感觉需要更多的水平空间(匹配视频的宽高比)。
vertical: 12,
horizontal: 16,
),
child: Text('${controller.value.captionOffset.inMilliseconds}ms'),
),
),
),
Align(
alignment: Alignment.topRight,
child: PopupMenuButton<double>(
initialValue: controller.value.playbackSpeed,
tooltip: 'Playback speed',
onSelected: (double speed) {
controller.setPlaybackSpeed(speed);
},
itemBuilder: (BuildContext context) {
return <PopupMenuItem<double>>[
for (final double speed in _examplePlaybackRates)
PopupMenuItem<double>(
value: speed,
child: Text('${speed}x'),
)
];
},
child: Padding(
padding: const EdgeInsets.symmetric(
// 使用较少的垂直填充,因为文本在水平方向上也较长,
// 所以感觉需要更多的水平空间(匹配视频的宽高比)。
vertical: 12,
horizontal: 16,
),
child: Text('${controller.value.playbackSpeed}x'),
),
),
),
],
);
}
}
class _PlayerVideoAndPopPage extends StatefulWidget {
[@override](/user/override)
_PlayerVideoAndPopPageState createState() => _PlayerVideoAndPopPageState();
}
class _PlayerVideoAndPopPageState extends State<_PlayerVideoAndPopPage> {
late VideoPlayerController _videoPlayerController;
bool startedPlaying = false;
[@override](/user/override)
void initState() {
super.initState();
_videoPlayerController =
VideoPlayerController.asset('assets/Butterfly-209.mp4');
_videoPlayerController.addListener(() {
if (startedPlaying && !_videoPlayerController.value.isPlaying) {
Navigator.pop(context);
}
});
}
[@override](/user/override)
void dispose() {
_videoPlayerController.dispose();
super.dispose();
}
Future<bool> started() async {
await _videoPlayerController.initialize();
await _videoPlayerController.play();
startedPlaying = true;
return true;
}
[@override](/user/override)
Widget build(BuildContext context) {
return Material(
elevation: 0,
child: Center(
child: FutureBuilder<bool>(
future: started(),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (snapshot.data ?? false) {
return AspectRatio(
aspectRatio: _videoPlayerController.value.aspectRatio,
child: VideoPlayer(_videoPlayerController),
);
} else {
return const Text('waiting for video to load');
}
},
),
),
);
}
}
更多关于Flutter视频播放插件chewie_video_player的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter视频播放插件chewie_video_player的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
chewie
是一个用于 Flutter 的视频播放器插件,它是对 Flutter 自带的 video_player
插件的封装,提供了更丰富的 UI 控制和自定义选项。使用 chewie
可以轻松地在 Flutter 应用中实现视频播放功能。
以下是如何在 Flutter 项目中使用 chewie
插件的详细步骤:
1. 添加依赖
在 pubspec.yaml
文件中添加 chewie
和 video_player
依赖:
dependencies:
flutter:
sdk: flutter
chewie: ^1.3.4 # 请使用最新版本
video_player: ^2.4.5 # 请使用最新版本
然后运行 flutter pub get
安装依赖。
2. 导入包
在需要使用 chewie
的文件中导入以下包:
import 'package:chewie/chewie.dart';
import 'package:video_player/video_player.dart';
3. 初始化视频播放器
在 StatefulWidget
中初始化 VideoPlayerController
和 ChewieController
:
class VideoPlayerScreen extends StatefulWidget {
[@override](/user/override)
_VideoPlayerScreenState createState() => _VideoPlayerScreenState();
}
class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
late VideoPlayerController _videoPlayerController;
late ChewieController _chewieController;
[@override](/user/override)
void initState() {
super.initState();
_initializeVideoPlayer();
}
void _initializeVideoPlayer() {
// 初始化 VideoPlayerController
_videoPlayerController = VideoPlayerController.network(
'https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4',
);
// 初始化 ChewieController
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
autoPlay: true, // 是否自动播放
looping: true, // 是否循环播放
aspectRatio: 16 / 9, // 视频宽高比
placeholder: Container(
color: Colors.black,
), // 视频加载时的占位符
autoInitialize: true, // 是否自动初始化
);
}
[@override](/user/override)
void dispose() {
_videoPlayerController.dispose();
_chewieController.dispose();
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Chewie Video Player'),
),
body: Center(
child: Chewie(
controller: _chewieController,
),
),
);
}
}
4. 运行应用
在 main.dart
中启动 VideoPlayerScreen
:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chewie Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: VideoPlayerScreen(),
);
}
}
5. 自定义选项
ChewieController
提供了许多自定义选项,例如:
autoPlay
: 是否自动播放。looping
: 是否循环播放。aspectRatio
: 视频宽高比。placeholder
: 视频加载时的占位符。showControls
: 是否显示播放控件。allowFullScreen
: 是否允许全屏播放。materialProgressColors
: 自定义进度条颜色。
例如,自定义进度条颜色:
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
materialProgressColors: ChewieProgressColors(
playedColor: Colors.red,
handleColor: Colors.blue,
backgroundColor: Colors.grey,
bufferedColor: Colors.lightGreen,
),
);
6. 注意事项
- 确保网络视频的 URL 有效。
- 在
dispose
方法中释放VideoPlayerController
和ChewieController
,以避免内存泄漏。 - 如果需要播放本地视频,可以使用
VideoPlayerController.asset
或VideoPlayerController.file
。
7. 示例代码
完整的示例代码如下:
import 'package:flutter/material.dart';
import 'package:chewie/chewie.dart';
import 'package:video_player/video_player.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chewie Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: VideoPlayerScreen(),
);
}
}
class VideoPlayerScreen extends StatefulWidget {
[@override](/user/override)
_VideoPlayerScreenState createState() => _VideoPlayerScreenState();
}
class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
late VideoPlayerController _videoPlayerController;
late ChewieController _chewieController;
[@override](/user/override)
void initState() {
super.initState();
_initializeVideoPlayer();
}
void _initializeVideoPlayer() {
_videoPlayerController = VideoPlayerController.network(
'https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4',
);
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
autoPlay: true,
looping: true,
aspectRatio: 16 / 9,
placeholder: Container(
color: Colors.black,
),
autoInitialize: true,
);
}
[@override](/user/override)
void dispose() {
_videoPlayerController.dispose();
_chewieController.dispose();
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Chewie Video Player'),
),
body: Center(
child: Chewie(
controller: _chewieController,
),
),
);
}
}