Flutter ChromeCast控制插件flutter_chrome_cast的使用

发布于 1周前 作者 phonegap100 来自 Flutter

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

1 回复

更多关于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 设备上。

注意

  1. 替换 https://www.example.com/path/to/your/video.mp4https://www.example.com/path/to/your/thumbnail.jpg 为你实际的媒体 URL 和缩略图 URL。
  2. 确保你的 ChromeCast 设备在同一个网络上,并且应用具有必要的网络权限。
  3. 这个示例没有处理所有可能的错误情况,比如设备连接失败、媒体加载失败等,你可以根据需要进行扩展。

希望这个示例能帮助你理解如何在 Flutter 应用中使用 flutter_chrome_cast 插件来控制 ChromeCast 设备。

回到顶部