Flutter ChromeCast控制插件flutter_chrome_cast的使用
Flutter ChromeCast控制插件flutter_chrome_cast的使用
flutter_chrome_cast
是一个用于 Google Cast SDK 的 Flutter 插件。它支持 iOS 和 Android 平台。
开始使用
在 pubspec.yaml
文件中添加 flutter_chrome_cast
作为依赖项。
dependencies:
flutter_chrome_cast:
然后运行 flutter pub get
来安装插件。
iOS 设置
在 Info.plist
文件中添加以下内容:
<key>NSBonjourServices</key>
<array>
<string>_CC1AD845._googlecast._tcp</string>
<string>_googlecast._tcp</string>
</array>
完整的 iOS 示例
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSLocalNetworkUsageDescription</key>
<string>${PRODUCT_NAME} uses the local network to discover Cast-enabled devices on your WiFi network.</string>
<key>NSBonjourServices</key>
<array>
<string>_CC1AD845._googlecast._tcp</string>
<string>_googlecast._tcp</string>
</array>
<!-- 其他配置项 -->
</dict>
</plist>
Android 设置
在 AndroidManifest.xml
文件中添加以下内容:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="GoogleCastOptionsProvider" />
<service
android:name="com.google.android.gms.cast.framework.media.MediaNotificationService"
android:exported="false"
android:foregroundServiceType="mediaPlayback" />
完整的 Android 示例
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.google_cast_example">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<application
android:label="google_cast_example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.example.google_cast.GoogleCastOptionsProvider" />
<service
android:name="com.google.android.gms.cast.framework.media.MediaNotificationService"
android:exported="false"
android:foregroundServiceType="mediaPlayback" />
</application>
</manifest>
使用示例
导入库
import 'package:flutter_chrome_cast/lib.dart';
初始化库
Future<void> initPlatformState() async {
const appId = GoogleCastDiscoveryCriteria.kDefaultApplicationId;
GoogleCastOptions? options;
if (Platform.isIOS) {
options = IOSGoogleCastOptions(
GoogleCastDiscoveryCriteriaInitialize.initWithApplicationID(appId),
);
} else if (Platform.isAndroid) {
options = GoogleCastOptionsAndroid(
appId: appId,
);
}
GoogleCastContext.instance.setSharedInstanceWithOptions(options!);
}
开始发现
// 发现过程即使在应用处于前台时也会在后台运行,消耗低功耗
// 如果你想要硬性发现(例如打开设备对话框/模态),建议使用
GoogleCastDiscoveryManager.instance.startDiscovery();
/// 不要忘记在不需要时停止发现
GoogleCastDiscoveryManager.instance.stopDiscovery();
监听发现的设备
StreamBuilder<List<GoogleCastDevice>>(
stream: GoogleCastDiscoveryManager.instance.devicesStream,
builder: (context, snapshot) {
final devices = snapshot.data ?? [];
return Column(
children: [
Expanded(
child: ListView(
children: [
...devices.map((device) {
return ListTile(
title: Text(device.friendlyName),
subtitle: Text(device.modelName ?? ''),
onTap: () => _loadQueue(device),
);
})
],
),
),
],
);
},
)
连接到设备
await GoogleCastSessionManager.instance.startSessionWithDevice(device);
加载项目
await GoogleCastRemoteMediaClient.instance.queueLoadItems([
GoogleCastQueueItem(
activeTrackIds: [0],
mediaInformation: GoogleCastMediaInformationIOS(
contentId: '0',
streamType: CastMediaStreamType.BUFFERED,
contentUrl: Uri.parse(
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4'),
contentType: 'video/mp4',
metadata: GoogleCastMovieMediaMetadata(
title: 'The first Blender Open Movie from 2006',
studio: 'Blender Inc',
releaseDate: DateTime(2011),
subtitle: '...',
images: [
GoogleCastImage(
url: Uri.parse(
'https://i.ytimg.com/vi_webp/gWw23EYM9VM/maxresdefault.webp'),
height: 480,
width: 854,
),
],
),
tracks: [
GoogleCastMediaTrack(
trackId: 0,
type: TrackType.TEXT,
trackContentId: Uri.parse(
'https://raw.githubusercontent.com/felnanuke2/flutter_cast/master/example/assets/VEED-subtitles_Blender_Foundation_-_Elephants_Dream_1024.vtt')
.toString(),
trackContentType: 'text/vtt',
name: 'English',
language: RFC5646_LANGUAGE.PORTUGUESE_BRAZIL,
subtype: TextTrackType.SUBTITLES,
),
],
),
),
GoogleCastQueueItem(
preLoadTime: const Duration(seconds: 15),
mediaInformation: GoogleCastMediaInformationIOS(
contentId: '1',
streamType: CastMediaStreamType.BUFFERED,
contentUrl: Uri.parse(
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'),
contentType: 'video/mp4',
metadata: GoogleCastMovieMediaMetadata(
title: 'Big Buck Bunny',
releaseDate: DateTime(2011),
studio: 'Vlc Media Player',
images: [
GoogleCastImage(
url: Uri.parse(
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg'),
height: 480,
width: 854,
),
],
),
),
),
], options: GoogleCastQueueLoadOptions(
startIndex: 0,
playPosition: const Duration(seconds: 30),
));
监听连接状态变化
StreamBuilder(
stream: GoogleCastSessionManager.instance.currentSessionStream,
builder: (context, snapshot) {
final isConnected = GoogleCastSessionManager.instance.connectionState == GoogleCastConnectState.ConnectionStateConnected;
return Visibility(
visible: isConnected,
child: FloatingActionButton(
onPressed: _insertQueueItemAndPlay,
child: const Icon(Icons.add),
),
);
}
)
监听会话事件
StreamBuilder<GoogleCastSession?>(
stream: GoogleCastSessionManager.instance.currentSessionStream,
builder: (context, snapshot) {
final bool isConnected = GoogleCastSessionManager.instance.connectionState == GoogleCastConnectState.ConnectionStateConnected;
return IconButton(
onPressed: GoogleCastSessionManager.instance.endSessionAndStopCasting,
icon: Icon(isConnected ? Icons.cast_connected : Icons.cast),
);
}
)
一些实用功能
/// 跳转到特定时间
void _changeCurrentTime(double value) {
final seconds = GoogleCastRemoteMediaClient
.instance.mediaStatus?.mediaInformation?.duration?.inSeconds ?? 0;
final position = (value * seconds).floor();
GoogleCastRemoteMediaClient.instance
.seek(GoogleCastMediaSeekOption(position: Duration(seconds: position)));
}
/// 暂停或播放
void _togglePLayPause() {
final isPlaying = GoogleCastRemoteMediaClient.instance.mediaStatus?.playerState == CastMediaPlayerState.playing;
if (isPlaying) {
GoogleCastRemoteMediaClient.instance.pause();
} else {
GoogleCastRemoteMediaClient.instance.play();
}
}
/// 加载媒体
void _loadMedia(GoogleCastDevice device) async {
await GoogleCastSessionManager.instance.startSessionWithDevice(device);
GoogleCastRemoteMediaClient.instance.loadMedia(
GoogleCastMediaInformationIOS(
contentId: '',
streamType: CastMediaStreamType.BUFFERED,
contentUrl: Uri.parse(
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4'),
contentType: 'video/mp4',
metadata: GoogleCastTvShowMediaMetadata(
episode: 1,
season: 2,
seriesTitle: 'Big Buck Bunny',
originalAirDate: DateTime.now(),
images: [
GoogleCastImage(
url: Uri.parse(
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg'),
height: 480,
width: 854,
),
],
),
tracks: [
GoogleCastMediaTrack(
trackId: 0,
type: TrackType.TEXT,
trackContentId: Uri.parse(
'https://raw.githubusercontent.com/felnanuke2/flutter_cast/master/example/assets/VEED-subtitles_Blender_Foundation_-_Elephants_Dream_1024.vtt')
.toString(),
trackContentType: 'text/vtt',
name: 'English',
language: RFC5646_LANGUAGE.PORTUGUESE_BRAZIL,
subtype: TextTrackType.SUBTITLES,
),
],
),
autoPlay: true,
playPosition: const Duration(seconds: 0),
playbackRate: 2,
activeTrackIds: [0],
);
}
/// 加载队列
_loadQueue(GoogleCastDevice device) async {
await GoogleCastSessionManager.instance.startSessionWithDevice(device);
await GoogleCastRemoteMediaClient.instance.queueLoadItems([
GoogleCastQueueItem(
activeTrackIds: [0],
mediaInformation: GoogleCastMediaInformationIOS(
contentId: '0',
streamType: CastMediaStreamType.BUFFERED,
contentUrl: Uri.parse(
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4'),
contentType: 'video/mp4',
metadata: GoogleCastMovieMediaMetadata(
title: 'The first Blender Open Movie from 2006',
studio: 'Blender Inc',
releaseDate: DateTime(2011),
subtitle: '...',
images: [
GoogleCastImage(
url: Uri.parse(
'https://i.ytimg.com/vi_webp/gWw23EYM9VM/maxresdefault.webp'),
height: 480,
width: 854,
),
],
),
tracks: [
GoogleCastMediaTrack(
trackId: 0,
type: TrackType.TEXT,
trackContentId: Uri.parse(
'https://raw.githubusercontent.com/felnanuke2/flutter_cast/master/example/assets/VEED-subtitles_Blender_Foundation_-_Elephants_Dream_1024.vtt')
.toString(),
trackContentType: 'text/vtt',
name: 'English',
language: RFC5646_LANGUAGE.PORTUGUESE_BRAZIL,
subtype: TextTrackType.SUBTITLES,
),
],
),
),
GoogleCastQueueItem(
preLoadTime: const Duration(seconds: 15),
mediaInformation: GoogleCastMediaInformationIOS(
contentId: '1',
streamType: CastMediaStreamType.BUFFERED,
contentUrl: Uri.parse(
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'),
contentType: 'video/mp4',
metadata: GoogleCastMovieMediaMetadata(
title: 'Big Buck Bunny',
releaseDate: DateTime(2011),
studio: 'Vlc Media Player',
images: [
GoogleCastImage(
url: Uri.parse(
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg'),
height: 480,
width: 854,
),
],
),
),
),
], options: GoogleCastQueueLoadOptions(
startIndex: 0,
playPosition: const Duration(seconds: 30),
));
}
/// 跳到上一个项目
void _previous() {
GoogleCastRemoteMediaClient.instance.queuePrevItem();
}
/// 跳到下一个项目
void _next() {
GoogleCastRemoteMediaClient.instance.queueNextItem();
}
/// 在队列中插入新项目
void _insertQueueItem() {
GoogleCastRemoteMediaClient.instance.queueInsertItems([
GoogleCastQueueItem(
preLoadTime: const Duration(seconds: 15),
mediaInformation: GoogleCastMediaInformationIOS(
contentId: '3',
streamType: CastMediaStreamType.BUFFERED,
contentUrl: Uri.parse(
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4'),
contentType: 'video/mp4',
metadata: GoogleCastMovieMediaMetadata(
title: 'For Bigger Blazes',
subtitle: '...',
releaseDate: DateTime(2011),
studio: 'T-Series Regional',
images: [
GoogleCastImage(
url: Uri.parse(
'https://i.ytimg.com/vi/Dr9C2oswZfA/maxresdefault.jpg'),
height: 480,
width: 854,
),
],
),
),
)
], beforeItemWithId: 2);
}
/// 在队列中插入项目并立即播放
void _insertQueueItemAndPlay() {
GoogleCastRemoteMediaClient.instance.queueInsertItemAndPlay(
GoogleCastQueueItem(
preLoadTime: const Duration(seconds: 15),
mediaInformation: GoogleCastMediaInformationIOS(
contentId: '3',
streamType: CastMediaStreamType.BUFFERED,
contentUrl: Uri.parse(
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4'),
contentType: 'video/mp4',
metadata: GoogleCastMovieMediaMetadata(
title: 'For Bigger Blazes',
subtitle: '...',
releaseDate: DateTime(2011),
studio: 'T-Series Regional',
images: [
GoogleCastImage(
url: Uri.parse(
'https://i.ytimg.com/vi/Dr9C2oswZfA/maxresdefault.jpg'),
height: 480,
width: 854,
),
],
),
),
), beforeItemWithId: 2);
}
/// 获取元数据中的图像
String? _getImage(GoogleCastMediaMetadata? metadata) {
if (metadata == null) {
return null;
}
if (metadata.images?.isEmpty ?? true) {
return null;
}
return metadata.images!.first.url.toString();
}
远程媒体客户端
abstract class GoogleCastRemoteMediaClientPlatformInterface
implements PlatformInterface {
GoggleCastMediaStatus? get mediaStatus;
Stream<GoggleCastMediaStatus?> get mediaStatusStream;
Duration get playerPosition;
Stream<Duration> get playerPositionStream;
Stream<List<GoogleCastQueueItem>> get queueItemsStream;
List<GoogleCastQueueItem> get queueItems;
bool get queueHasNextItem;
bool get queueHasPreviousItem;
Future<void> loadMedia(
GoogleCastMediaInformation mediaInfo, {
bool autoPlay = true,
Duration playPosition = Duration.zero,
double playbackRate = 1.0,
List<int>? activeTrackIds,
String? credentials,
String? credentialsType,
});
Future<void> queueLoadItems(
List<GoogleCastQueueItem> queueItems, {
GoogleCastQueueLoadOptions? options,
});
Future<void> setPlaybackRate(double rate);
Future<void> setActiveTrackIDs(List<int> activeTrackIDs);
Future<void> setTextTrackStyle(TextTrackStyle textTrackStyle);
Future<void> pause();
Future<void> play();
Future<void> stop();
Future<void> queueNextItem();
Future<void> queuePrevItem();
Future<void> seek(GoogleCastMediaSeekOption option);
Future<void> queueInsertItems(
List<GoogleCastQueueItem> items, {
int? beforeItemWithId,
});
Future<void> queueInsertItemAndPlay(
GoogleCastQueueItem item, {
required int beforeItemWithId,
});
Future<void> queueRemoveItemsWithIds(
List<int> itemIds,
);
Future<void> queueJumpToItemWithId(int itemId);
Future<void> queueReorderItems({
required List<int> itemsIds,
required int? beforeItemWithId,
});
}
示例代码
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_chrome_cast/lib.dart';
import 'dart:async';
import 'package:flutter_chrome_cast/widgets/mini_controller.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
[@override](/user/override)
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
[@override](/user/override)
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
const appId = GoogleCastDiscoveryCriteria.kDefaultApplicationId;
GoogleCastOptions? options;
if (Platform.isIOS) {
options = IOSGoogleCastOptions(
GoogleCastDiscoveryCriteriaInitialize.initWithApplicationID(appId),
);
} else if (Platform.isAndroid) {
options = GoogleCastOptionsAndroid(
appId: appId,
);
}
GoogleCastContext.instance.setSharedInstanceWithOptions(options!);
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Stack(
children: [
Scaffold(
floatingActionButton: Container(
margin: const EdgeInsets.only(bottom: 40),
child: StreamBuilder(
stream: GoogleCastSessionManager.instance.currentSessionStream,
builder: (context, snapshot) {
final isConnected = GoogleCastSessionManager.instance.connectionState == GoogleCastConnectState.ConnectionStateConnected;
return Visibility(
visible: isConnected,
child: FloatingActionButton(
onPressed: _insertQueueItemAndPlay,
child: const Icon(Icons.add),
),
);
}),
),
appBar: AppBar(
title: const Text('Plugin example app'),
actions: [
StreamBuilder<GoogleCastSession?>(
stream: GoogleCastSessionManager.instance.currentSessionStream,
builder: (context, snapshot) {
final bool isConnected = GoogleCastSessionManager.instance.connectionState == GoogleCastConnectState.ConnectionStateConnected;
return IconButton(
onPressed: GoogleCastSessionManager.instance.endSessionAndStopCasting,
icon: Icon(isConnected ? Icons.cast_connected : Icons.cast));
})
],
),
body: StreamBuilder<List<GoogleCastDevice>>(
stream: GoogleCastDiscoveryManager.instance.devicesStream,
builder: (context, snapshot) {
final devices = snapshot.data ?? [];
return Column(
children: [
Expanded(
child: ListView(
children: [
...devices.map((device) {
return ListTile(
title: Text(device.friendlyName),
subtitle: Text(device.modelName ?? ''),
onTap: () => _loadQueue(device),
);
})
],
),
),
],
);
},
)),
const GoogleCastMiniController(),
],
),
);
}
void _changeCurrentTime(double value) {
final seconds = GoogleCastRemoteMediaClient
.instance.mediaStatus?.mediaInformation?.duration?.inSeconds ?? 0;
final position = (value * seconds).floor();
GoogleCastRemoteMediaClient.instance
.seek(GoogleCastMediaSeekOption(position: Duration(seconds: position)));
}
void _togglePLayPause() {
final isPlaying = GoogleCastRemoteMediaClient.instance.mediaStatus?.playerState == CastMediaPlayerState.playing;
if (isPlaying) {
GoogleCastRemoteMediaClient.instance.pause();
} else {
GoogleCastRemoteMediaClient.instance.play();
}
}
void _loadMedia(GoogleCastDevice device) async {
await GoogleCastSessionManager.instance.startSessionWithDevice(device);
GoogleCastRemoteMediaClient.instance.loadMedia(
GoogleCastMediaInformationIOS(
contentId: '',
streamType: CastMediaStreamType.BUFFERED,
contentUrl: Uri.parse(
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4'),
contentType: 'video/mp4',
metadata: GoogleCastTvShowMediaMetadata(
episode: 1,
season: 2,
seriesTitle: 'Big Buck Bunny',
originalAirDate: DateTime.now(),
images: [
GoogleCastImage(
url: Uri.parse(
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg'),
height: 480,
width: 854,
),
],
),
tracks: [
GoogleCastMediaTrack(
trackId: 0,
type: TrackType.TEXT,
trackContentId: Uri.parse(
'https://raw.githubusercontent.com/felnanuke2/flutter_cast/master/example/assets/VEED-subtitles_Blender_Foundation_-_Elephants_Dream_1024.vtt')
.toString(),
trackContentType: 'text/vtt',
name: 'English',
language: RFC5646_LANGUAGE.PORTUGUESE_BRAZIL,
subtype: TextTrackType.SUBTITLES,
),
],
),
autoPlay: true,
playPosition: const Duration(seconds: 0),
playbackRate: 2,
activeTrackIds: [0],
);
}
_loadQueue(GoogleCastDevice device) async {
await GoogleCastSessionManager.instance.startSessionWithDevice(device);
await GoogleCastRemoteMediaClient.instance.queueLoadItems([
GoogleCastQueueItem(
activeTrackIds: [0],
mediaInformation: GoogleCastMediaInformationIOS(
contentId: '0',
streamType: CastMediaStreamType.BUFFERED,
contentUrl: Uri.parse(
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4'),
contentType: 'video/mp4',
metadata: GoogleCastMovieMediaMetadata(
title: 'The first Blender Open Movie from 2006',
studio: 'Blender Inc',
releaseDate: DateTime(2011),
subtitle: '...',
images: [
GoogleCastImage(
url: Uri.parse(
'https://i.ytimg.com/vi_webp/gWw23EYM9VM/maxresdefault.webp'),
height: 480,
width: 854,
),
],
),
tracks: [
GoogleCastMediaTrack(
trackId: 0,
type: TrackType.TEXT,
trackContentId: Uri.parse(
'https://raw.githubusercontent.com/felnanuke2/flutter_cast/master/example/assets/VEED-subtitles_Blender_Foundation_-_Elephants_Dream_1024.vtt')
.toString(),
trackContentType: 'text/vtt',
name: 'English',
language: RFC5646_LANGUAGE.PORTUGUESE_BRAZIL,
subtype: TextTrackType.SUBTITLES,
),
],
),
),
GoogleCastQueueItem(
preLoadTime: const Duration(seconds: 15),
mediaInformation: GoogleCastMediaInformationIOS(
contentId: '1',
streamType: CastMediaStreamType.BUFFERED,
contentUrl: Uri.parse(
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'),
contentType: 'video/mp4',
metadata: GoogleCastMovieMediaMetadata(
title: 'Big Buck Bunny',
releaseDate: DateTime(2011),
studio: 'Vlc Media Player',
images: [
GoogleCastImage(
url: Uri.parse(
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg'),
height: 480,
width: 854,
),
],
),
),
),
], options: GoogleCastQueueLoadOptions(
startIndex: 0,
playPosition: const Duration(seconds: 30),
));
}
void _previous() {
GoogleCastRemoteMediaClient.instance.queuePrevItem();
}
void _next() {
GoogleCastRemoteMediaClient.instance.queueNextItem();
}
void _insertQueueItem() {
GoogleCastRemoteMediaClient.instance.queueInsertItems([
GoogleCastQueueItem(
preLoadTime: const Duration(seconds: 15),
mediaInformation: GoogleCastMediaInformationIOS(
contentId: '3',
streamType: CastMediaStreamType.BUFFERED,
contentUrl: Uri.parse(
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4'),
contentType: 'video/mp4',
metadata: GoogleCastMovieMediaMetadata(
title: 'For Bigger Blazes',
subtitle: '...',
releaseDate: DateTime(2011),
studio: 'T-Series Regional',
images: [
GoogleCastImage(
url: Uri.parse(
'https://i.ytimg.com/vi/Dr9C2oswZfA/maxresdefault.jpg'),
height: 480,
width: 854,
),
],
),
),
)
], beforeItemWithId: 2);
}
void _insertQueueItemAndPlay() {
GoogleCastRemoteMediaClient.instance.queueInsertItemAndPlay(
GoogleCastQueueItem(
preLoadTime: const Duration(seconds: 15),
mediaInformation: GoogleCastMediaInformationIOS(
contentId: '3',
streamType: CastMediaStreamType.BUFFERED,
contentUrl: Uri.parse(
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4'),
contentType: 'video/mp4',
metadata: GoogleCastMovieMediaMetadata(
title: 'For Bigger Blazes',
subtitle: '...',
releaseDate: DateTime(2011),
studio: 'T-Series Regional',
images: [
GoogleCastImage(
url: Uri.parse(
'https://i.ytimg.com/vi/Dr9C2oswZfA/maxresdefault.jpg'),
height: 480,
width: 854,
),
],
),
),
), beforeItemWithId: 2);
}
String? _getImage(GoogleCastMediaMetadata? metadata) {
if (metadata == null) {
return null;
}
if (metadata.images?.isEmpty ?? true) {
return null;
}
return metadata.images!.first.url.toString();
}
}
更多关于Flutter ChromeCast控制插件flutter_chrome_cast的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter ChromeCast控制插件flutter_chrome_cast的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何使用 flutter_chrome_cast
插件在 Flutter 应用中控制 ChromeCast 设备的代码示例。这个示例将展示如何发现设备、连接到设备以及发送媒体内容到 ChromeCast 设备。
首先,确保你的 Flutter 项目已经添加了 flutter_chrome_cast
依赖。在你的 pubspec.yaml
文件中添加以下依赖:
dependencies:
flutter:
sdk: flutter
flutter_chrome_cast: ^x.y.z # 替换为最新版本号
然后运行 flutter pub get
来获取依赖。
接下来,在你的 Flutter 应用中实现 ChromeCast 功能。以下是一个简单的示例:
import 'package:flutter/material.dart';
import 'package:flutter_chrome_cast/flutter_chrome_cast.dart';
import 'package:flutter_chrome_cast/media_info.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter ChromeCast Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ChromeCastScreen(),
);
}
}
class ChromeCastScreen extends StatefulWidget {
@override
_ChromeCastScreenState createState() => _ChromeCastScreenState();
}
class _ChromeCastScreenState extends State<ChromeCastScreen> with WidgetsBindingObserver {
ChromeCast? _chromeCast;
ChromeCastDevice? _selectedDevice;
bool _isCasting = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
_initializeChromeCast();
}
@override
void dispose() {
WidgetsBinding.instance!.removeObserver(this);
_chromeCast?.dispose();
super.dispose();
}
Future<void> _initializeChromeCast() async {
_chromeCast = ChromeCast();
_chromeCast!.devicesUpdated.listen((devices) {
setState(() {
// 更新设备列表,这里简单处理,只选择第一个设备
if (devices.isNotEmpty) {
_selectedDevice = devices.first;
} else {
_selectedDevice = null;
}
});
});
// 开始扫描设备
await _chromeCast!.startDiscovery();
}
Future<void> _castMedia() async {
if (_selectedDevice == null || _isCasting) return;
setState(() {
_isCasting = true;
});
MediaInfo mediaInfo = MediaInfo.newFromUrl(
'https://www.example.com/path/to/your/video.mp4',
metadata: MediaMetadata(
title: 'Example Video',
subTitle: 'A sample video',
images: [
MediaImage(
url: 'https://www.example.com/path/to/your/thumbnail.jpg',
),
],
),
);
try {
await _chromeCast!.loadMedia(mediaInfo, _selectedDevice!);
} catch (e) {
print('Failed to cast media: $e');
setState(() {
_isCasting = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter ChromeCast Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
if (_selectedDevice == null)
Text('No ChromeCast devices found.'),
else
Text('Selected Device: ${_selectedDevice!.friendlyName}'),
SizedBox(height: 20),
ElevatedButton(
onPressed: _isCasting ? null : _castMedia,
child: Text(_isCasting ? 'Casting...' : 'Cast Media'),
),
],
),
),
);
}
}
在这个示例中,我们创建了一个简单的 Flutter 应用,该应用能够扫描附近的 ChromeCast 设备,并选择第一个找到的设备进行媒体投射。点击按钮时,将尝试将指定的视频 URL 投射到选定的 ChromeCast 设备上。
注意:
- 替换
https://www.example.com/path/to/your/video.mp4
和https://www.example.com/path/to/your/thumbnail.jpg
为你实际的媒体 URL 和缩略图 URL。 - 确保你的 ChromeCast 设备在同一个网络上,并且应用具有必要的网络权限。
- 这个示例没有处理所有可能的错误情况,比如设备连接失败、媒体加载失败等,你可以根据需要进行扩展。
希望这个示例能帮助你理解如何在 Flutter 应用中使用 flutter_chrome_cast
插件来控制 ChromeCast 设备。