Flutter视频投射插件smart_video_cast的使用
Flutter视频投射插件smart_video_cast的使用
smart_video_cast
是一个用于在 Flutter 应用中连接到 Chromecast 和 Apple TV 等投射设备的插件。
安装
首先,在 pubspec.yaml
文件中添加 smart_video_cast
作为依赖项:
dependencies:
smart_video_cast: ^版本号
运行 flutter pub get
来获取新的依赖项。
iOS
设置最低的 iOS 目标版本为 11.0。在 ios/Runner/AppDelegate.m
中初始化 Cast 上下文:
import UIKit
import Flutter
import GoogleCast
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, GCKLoggerDelegate {
let kReceiverAppID = kGCKDefaultMediaReceiverApplicationID
let kDebugLoggingEnabled = true
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID)
let options = GCKCastOptions(discoveryCriteria: criteria)
GCKCastContext.setSharedInstanceWith(options)
GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true
GCKLogger.sharedInstance().delegate = self
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
在应用的 Info.plist
文件中添加以下属性以允许嵌入视图预览:
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>
Android
在模块(应用级别)的 Gradle
文件(通常位于 android/app/build.gradle
)中添加依赖项:
implementation 'com.google.android.gms:play-services-cast-framework:19.0.0'
implementation 'com.google.android.exoplayer:extension-cast:2.17.1'
在应用的 AndroidManifest.xml
文件中设置 MainActivity
的主题为 @style/Theme.AppCompat.NoActionBar
:
<manifest ...>
<application ...
<meta-data android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.google.android.exoplayer2.ext.cast.DefaultCastOptionsProvider"/>
...
<activity android:theme="@style/Theme.AppCompat.NoActionBar" ...>
确保 MainActivity
继承自 FlutterFragmentActivity
并初始化 Cast 上下文:
CastContext.getSharedInstance(applicationContext)
双端
现在可以在你的 Widget 树中添加一个 ChromeCastButton
小部件。
该按钮可以通过传递给 ChromeCastButton
的 onButtonCreated
回调的 ChromeCastController
进行控制。
import 'package:flutter/material.dart';
import 'package:smart_video_cast/smart_video_cast.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: CastSample(),
);
}
}
class CastSample extends StatefulWidget {
[@override](/user/override)
_CastSampleState createState() => _CastSampleState();
}
class _CastSampleState extends State<CastSample> {
ChromeCastController _controller;
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Cast Sample'),
actions: [
ChromeCastButton(
onButtonCreated: (controller) {
setState(() => _controller = controller);
_controller?.addSessionListener();
},
onSessionStarted: () {
_controller?.loadMedia('https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4');
},
),
],
),
);
}
}
示例代码
以下是完整的示例代码,展示了如何使用 smart_video_cast
插件进行视频投射。
import 'package:flutter/material.dart';
import 'package:smart_video_cast/smart_video_cast.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: CastSample(),
);
}
}
class CastSample extends StatefulWidget {
static const _iconSize = 50.0;
[@override](/user/override)
_CastSampleState createState() => _CastSampleState();
}
class _CastSampleState extends State<CastSample> {
ChromeCastController _controller;
AppState _state = AppState.idle;
bool _playing = false;
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Plugin example app'),
actions: [
AirPlayButton(
size: CastSample._iconSize,
color: Colors.white,
activeColor: Colors.amber,
onRoutesOpening: () => print('opening'),
onRoutesClosed: () => print('closed'),
),
ChromeCastButton(
size: CastSample._iconSize,
color: Colors.white,
onButtonCreated: _onButtonCreated,
onSessionStarted: _onSessionStarted,
onSessionEnded: () => setState(() => _state = AppState.idle),
onRequestCompleted: _onRequestCompleted,
onRequestFailed: _onRequestFailed,
),
],
),
body: Center(child: _handleState()),
);
}
Widget _handleState() {
switch (_state) {
case AppState.idle:
return Text('ChromeCast not connected');
case AppState.connected:
return Text('No media loaded');
case AppState.mediaLoaded:
return _mediaControls();
case AppState.error:
return Text('An error has occurred');
default:
return Container();
}
}
Widget _mediaControls() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_RoundIconButton(
icon: Icons.replay_10,
onPressed: () => _controller.seek(relative: true, interval: -10.0),
),
_RoundIconButton(
icon: _playing ? Icons.pause : Icons.play_arrow,
onPressed: _playPause),
_RoundIconButton(
icon: Icons.forward_10,
onPressed: () => _controller.seek(relative: true, interval: 10.0),
)
],
);
}
Future<void> _playPause() async {
final playing = await _controller.isPlaying();
if (playing) {
await _controller.pause();
} else {
await _controller.play();
}
setState(() => _playing = !playing);
}
Future<void> _onButtonCreated(ChromeCastController controller) async {
_controller = controller;
await _controller.addSessionListener();
}
Future<void> _onSessionStarted() async {
setState(() => _state = AppState.connected);
await _controller.loadMedia(
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4');
}
Future<void> _onRequestCompleted() async {
final playing = await _controller.isPlaying();
setState(() {
_state = AppState.mediaLoaded;
_playing = playing;
});
}
Future<void> _onRequestFailed(String error) async {
setState(() => _state = AppState.error);
print(error);
}
}
class _RoundIconButton extends StatelessWidget {
final IconData icon;
final VoidCallback onPressed;
_RoundIconButton({[@required](/user/required) this.icon, [@required](/user/required) this.onPressed});
[@override](/user/override)
Widget build(BuildContext context) {
return RaisedButton(
child: Icon(icon, color: Colors.white),
padding: EdgeInsets.all(16.0),
color: Colors.blue,
shape: CircleBorder(),
onPressed: onPressed);
}
}
enum AppState { idle, connected, mediaLoaded, error }
更多关于Flutter视频投射插件smart_video_cast的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter视频投射插件smart_video_cast的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中集成和使用smart_video_cast
插件的一个基本示例。smart_video_cast
是一个用于视频投射的Flutter插件,它可以帮助你将视频内容投射到支持DLNA或Chromecast的设备上。
1. 添加依赖
首先,你需要在pubspec.yaml
文件中添加smart_video_cast
依赖:
dependencies:
flutter:
sdk: flutter
smart_video_cast: ^最新版本号 # 请替换为实际的最新版本号
然后运行flutter pub get
来安装依赖。
2. 导入包
在你的Dart文件中导入smart_video_cast
包:
import 'package:smart_video_cast/smart_video_cast.dart';
3. 初始化投射功能
在你的应用中初始化投射功能,通常这会在应用的启动过程中进行:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 初始化投射设备扫描器
final castScanner = CastScanner();
// 开始扫描设备(通常这是一个耗时操作,可以在后台线程中执行)
castScanner.startScanning();
// 监听设备发现事件
castScanner.deviceDiscovered.listen((device) {
print('Device discovered: ${device.friendlyName}');
// 在这里可以保存设备信息,用于后续连接
});
// 运行Flutter应用
runApp(MyApp());
}
4. 连接到投射设备
一旦设备被发现,你可以连接到它。这里是一个简化的示例,假设你已经有了设备的IP地址或其他标识符:
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late CastDevice? _selectedDevice;
late CastSession? _session;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Video Cast Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// 假设这里有一个按钮来选择设备(实际实现中,你可能会有一个设备列表)
ElevatedButton(
onPressed: () async {
// 这里应该有一个逻辑来选择设备,这里我们假设已经选择了设备
_selectedDevice = /* 获取选中的设备 */;
if (_selectedDevice != null) {
// 连接到设备
_session = await CastSession.startSession(_selectedDevice!);
// 监听会话状态变化
_session!.sessionStatus.listen((status) {
print('Session status: $status');
});
// 加载视频(假设有一个视频URL)
final videoUrl = 'http://example.com/video.mp4';
_session!.loadMedia(videoUrl);
}
},
child: Text('Start Casting'),
),
],
),
),
),
);
}
@override
void dispose() {
// 停止扫描和会话(如果有的话)
_session?.stopSession();
// 注意:在实际应用中,你应该有一个更好的资源管理机制
super.dispose();
}
}
注意
- 设备选择:上面的代码示例中假设已经有了一个选择的设备,在实际应用中,你需要提供一个UI来选择发现的设备。
- 错误处理:在实际应用中,你应该添加适当的错误处理逻辑,比如处理设备连接失败、会话启动失败等情况。
- 资源管理:确保在不需要时停止扫描和会话,以避免不必要的资源消耗。
这个示例只是一个基本的起点,实际应用中你可能需要根据具体需求进行更多的定制和扩展。