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 小部件。

该按钮可以通过传递给 ChromeCastButtononButtonCreated 回调的 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

1 回复

更多关于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();
  }
}

注意

  1. 设备选择:上面的代码示例中假设已经有了一个选择的设备,在实际应用中,你需要提供一个UI来选择发现的设备。
  2. 错误处理:在实际应用中,你应该添加适当的错误处理逻辑,比如处理设备连接失败、会话启动失败等情况。
  3. 资源管理:确保在不需要时停止扫描和会话,以避免不必要的资源消耗。

这个示例只是一个基本的起点,实际应用中你可能需要根据具体需求进行更多的定制和扩展。

回到顶部