Flutter虚拟现实播放插件vr_player的使用
Flutter虚拟现实播放插件vr_player的使用
VrPlayer
Crafted with passion by What the Flutter 🦜
Getting Started
VrPlayer
插件基于 Kaltura Playkit SDK,允许在 Android 和 iOS 平台上流畅地播放 360° 和 VR 视频。这些视频类型通常被称为沉浸式、360 或球形视频,是通过全向摄像机或多个摄像机同时记录整个全景视图来捕捉的。
使用方法
基本用法
VrPlayer(
x: 0,
y: 0,
onCreated: onViewPlayerCreated,
width: playerWidth,
height: playerHeight,
),
你需要实现 onViewPlayerCreated
来接收播放器事件:
void onViewPlayerCreated(
VrPlayerController controller,
VrPlayerObserver observer,
) {
_viewPlayerController = controller;
observer
..onStateChange = onReceiveState
..onDurationChange = onReceiveDuration
..onPositionChange = onChangePosition
..onFinishedChange = onReceiveEnded;
_viewPlayerController.loadVideo(
videoUrl:
'https://cdn.bitmovin.com/content/assets/playhouse-vr/m3u8s/105560.m3u8',
);
}
VrPlayerController
VrPlayerController
可用于更改 VrPlayer
的状态。注意,只有在 VrPlayer
创建后才能使用这些方法。
方法 | 描述 |
---|---|
loadVideo({String? videoUrl, String? videoPath}) |
根据配置初始化视频。可以通过 videoPath 加载本地文件,或通过 videoUrl 播放网络文件。本地文件仅支持 Android。 |
isPlaying() |
检查当前播放器状态。 |
play() |
播放视频。 |
pause() |
暂停视频。 |
seekTo() |
跳转到指定位置。 |
setVolume() |
设置音量级别,从 0(无声音)到 1(最大)。 |
fullScreen() |
(Android only) 切换全屏模式。iOS 需要传递新的宽高给 VrPlayer widget。 |
toggleVRMode() |
在 360° 模式和 VR 模式之间切换。 |
本地文件指南 (仅限 Android)
- 在
AndroidmManifest.xml
文件中添加<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
。 - 请求用户授予读取外部存储的权限。
- 使用正确的
videoPath
调用loadVideo()
。
示例代码
以下是一个完整的示例,展示了如何使用 VrPlayer
插件创建一个 VR 视频播放器。
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:vr_player/vr_player.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: HomePage(),
),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const VideoPlayerPage(),
),
);
},
child: const Text('Start Video'),
);
}
}
class VideoPlayerPage extends StatefulWidget {
const VideoPlayerPage({super.key});
@override
_VideoPlayerPageState createState() => _VideoPlayerPageState();
}
class _VideoPlayerPageState extends State<VideoPlayerPage>
with TickerProviderStateMixin {
late VrPlayerController _viewPlayerController;
late AnimationController _animationController;
late Animation<double> _animation;
bool _isShowingBar = false;
bool _isPlaying = false;
bool _isFullScreen = false;
bool _isVideoFinished = false;
bool _isLandscapeOrientation = false;
bool _isVolumeSliderShown = false;
bool _isVolumeEnabled = true;
late double _playerWidth;
late double _playerHeight;
String? _duration;
int? _intDuration;
bool isVideoLoading = false;
bool isVideoReady = false;
String? _currentPosition;
double _currentSliderValue = 0.1;
double _seekPosition = 0;
@override
void initState() {
_animationController =
AnimationController(vsync: this, duration: const Duration(seconds: 1));
_animation = Tween<double>(begin: 0, end: 1).animate(_animationController);
_toggleShowingBar();
super.initState();
}
void _toggleShowingBar() {
switchVolumeSliderDisplay(show: false);
_isShowingBar = !_isShowingBar;
if (_isShowingBar) {
_animationController.forward();
} else {
_animationController.reverse();
}
}
@override
Widget build(BuildContext context) {
_playerWidth = MediaQuery.of(context).size.width;
_playerHeight =
_isFullScreen ? MediaQuery.of(context).size.height : _playerWidth / 2;
_isLandscapeOrientation =
MediaQuery.of(context).orientation == Orientation.landscape;
return Scaffold(
appBar: AppBar(
title: const Text('VR Player'),
),
body: GestureDetector(
onTap: _toggleShowingBar,
child: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
VrPlayer(
x: 0,
y: 0,
onCreated: onViewPlayerCreated,
width: _playerWidth,
height: _playerHeight,
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: FadeTransition(
opacity: _animation,
child: ColoredBox(
color: Colors.black,
child: Row(
children: <Widget>[
IconButton(
icon: Icon(
_isVideoFinished
? Icons.replay
: _isPlaying
? Icons.pause
: Icons.play_arrow,
color: Colors.white,
),
onPressed: playAndPause,
),
Text(
_currentPosition?.toString() ?? '00:00',
style: const TextStyle(color: Colors.white),
),
Expanded(
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: Colors.amberAccent,
inactiveTrackColor: Colors.grey,
trackHeight: 5,
thumbColor: Colors.white,
thumbShape: const RoundSliderThumbShape(
enabledThumbRadius: 8,
),
overlayColor: Colors.purple.withAlpha(32),
overlayShape: const RoundSliderOverlayShape(
overlayRadius: 14,
),
),
child: Slider(
value: _seekPosition,
max: _intDuration?.toDouble() ?? 0,
onChangeEnd: (value) {
_viewPlayerController.seekTo(value.toInt());
},
onChanged: (value) {
onChangePosition(value.toInt());
},
),
),
),
Text(
_duration?.toString() ?? '99:99',
style: const TextStyle(color: Colors.white),
),
if (_isFullScreen || _isLandscapeOrientation)
IconButton(
icon: Icon(
_isVolumeEnabled
? Icons.volume_up_rounded
: Icons.volume_off_rounded,
color: Colors.white,
),
onPressed: () =>
switchVolumeSliderDisplay(show: true),
),
IconButton(
icon: Icon(
_isFullScreen
? Icons.fullscreen_exit
: Icons.fullscreen,
color: Colors.white,
),
onPressed: fullScreenPressed,
),
if (_isFullScreen)
IconButton(
icon: Image.asset(
'assets/icons/cardboard.png',
color: Colors.white,
),
onPressed: cardBoardPressed,
)
else
Container(),
],
),
),
),
),
Positioned(
height: 180,
right: 4,
top: MediaQuery.of(context).size.height / 4,
child: _isVolumeSliderShown
? RotatedBox(
quarterTurns: 3,
child: Slider(
value: _currentSliderValue,
divisions: 10,
onChanged: onChangeVolumeSlider,
),
)
: const SizedBox(),
),
],
),
),
);
}
void cardBoardPressed() {
_viewPlayerController.toggleVRMode();
}
Future<void> fullScreenPressed() async {
await _viewPlayerController.fullScreen();
setState(() {
_isFullScreen = !_isFullScreen;
});
if (_isFullScreen) {
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
]);
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [],
);
} else {
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: SystemUiOverlay.values,
);
}
}
Future<void> playAndPause() async {
if (_isVideoFinished) {
await _viewPlayerController.seekTo(0);
}
if (_isPlaying) {
await _viewPlayerController.pause();
} else {
await _viewPlayerController.play();
}
setState(() {
_isPlaying = !_isPlaying;
_isVideoFinished = false;
});
}
void onViewPlayerCreated(
VrPlayerController controller,
VrPlayerObserver observer,
) {
_viewPlayerController = controller;
observer
..onStateChange = onReceiveState
..onDurationChange = onReceiveDuration
..onPositionChange = onChangePosition
..onFinishedChange = onReceiveEnded;
_viewPlayerController.loadVideo(
videoUrl:
'https://cdn.bitmovin.com/content/assets/playhouse-vr/m3u8s/105560.m3u8',
);
}
void onReceiveState(VrState state) {
switch (state) {
case VrState.loading:
setState(() {
isVideoLoading = true;
});
break;
case VrState.ready:
setState(() {
isVideoLoading = false;
isVideoReady = true;
});
break;
case VrState.buffering:
case VrState.idle:
break;
}
}
void onReceiveDuration(int millis) {
setState(() {
_intDuration = millis;
_duration = millisecondsToDateTime(millis);
});
}
void onChangePosition(int millis) {
setState(() {
_currentPosition = millisecondsToDateTime(millis);
_seekPosition = millis.toDouble();
});
}
// ignore: avoid_positional_boolean_parameters
void onReceiveEnded(bool isFinished) {
setState(() {
_isVideoFinished = isFinished;
});
}
void onChangeVolumeSlider(double value) {
_viewPlayerController.setVolume(value);
setState(() {
_isVolumeEnabled = value != 0;
_currentSliderValue = value;
});
}
void switchVolumeSliderDisplay({required bool show}) {
setState(() {
_isVolumeSliderShown = show;
});
}
String millisecondsToDateTime(int milliseconds) =>
setDurationText(Duration(milliseconds: milliseconds));
String setDurationText(Duration duration) {
String twoDigits(int n) {
if (n >= 10) return '$n';
return '0$n';
}
final twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
final twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return '${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds';
}
}
这个示例展示了如何创建一个包含基本控制功能(如播放、暂停、全屏、音量调节等)的 VR 视频播放器。你可以根据需要进一步扩展和自定义此示例。
更多关于Flutter虚拟现实播放插件vr_player的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter虚拟现实播放插件vr_player的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用vr_player
插件来实现虚拟现实视频播放的代码示例。请注意,这个示例假设你已经有一个Flutter项目,并且已经添加了vr_player
插件到你的pubspec.yaml
文件中。
1. 添加依赖
首先,在你的pubspec.yaml
文件中添加vr_player
依赖:
dependencies:
flutter:
sdk: flutter
vr_player: ^最新版本号 # 替换为实际的最新版本号
然后运行flutter pub get
来安装依赖。
2. 导入包
在你的Dart文件中(比如main.dart
),导入vr_player
包:
import 'package:flutter/material.dart';
import 'package:vr_player/vr_player.dart';
3. 创建VR播放器页面
接下来,创建一个页面来使用VR播放器。下面是一个简单的示例:
class VRPlayerPage extends StatefulWidget {
@override
_VRPlayerPageState createState() => _VRPlayerPageState();
}
class _VRPlayerPageState extends State<VRPlayerPage> {
late VRPlayerController _controller;
@override
void initState() {
super.initState();
_controller = VRPlayerController();
// 加载视频资源,可以是本地资源或网络资源
_controller.setVideoSource('https://example.com/your-video-file.mp4');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('VR Player Demo'),
),
body: Center(
child: VRPlayer(
controller: _controller,
onReady: () {
// 播放器准备就绪时回调
print('VR Player is ready');
},
onError: (error) {
// 播放器出错时回调
print('VR Player error: $error');
},
onCompleted: () {
// 视频播放完成时回调
print('VR Player video completed');
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 控制播放/暂停
setState(() {
_controller.isPlaying ? _controller.pause() : _controller.play();
});
},
tooltip: _controller.isPlaying ? 'Pause' : 'Play',
child: Icon(_controller.isPlaying ? Icons.pause : Icons.play_arrow),
),
);
}
@override
void dispose() {
// 释放资源
_controller.dispose();
super.dispose();
}
}
4. 运行应用
最后,在你的main.dart
文件中引入并使用这个页面:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter VR Player Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: VRPlayerPage(),
);
}
}
注意事项
- 视频格式:确保你的视频文件是VR播放器支持的格式(如MP4)。
- 权限:如果你的视频资源是存储在设备上的,确保你的应用有访问存储的权限。
- 性能:虚拟现实视频播放对设备的性能要求较高,确保在支持的设备上测试你的应用。
这样,你就可以在Flutter应用中实现一个简单的VR视频播放功能了。如果你需要更多高级功能,比如自定义UI、控制音量等,可以参考vr_player
的官方文档。