├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── it │ └── aesys │ └── flutter_video_cast │ ├── ChromeCastController.kt │ ├── ChromeCastFactory.kt │ └── FlutterVideoCastPlugin.kt ├── example ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── it │ │ │ │ │ └── aesys │ │ │ │ │ └── flutter_video_cast_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ └── main.dart ├── pubspec.lock ├── pubspec.yaml └── test │ └── widget_test.dart ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── AirPlayController.swift │ ├── AirPlayFactory.swift │ ├── ChromeCastController.swift │ ├── ChromeCastFactory.swift │ ├── FlutterVideoCastPlugin.h │ ├── FlutterVideoCastPlugin.m │ └── SwiftFlutterVideoCastPlugin.swift └── flutter_video_cast.podspec ├── lib ├── flutter_video_cast.dart └── src │ ├── air_play │ ├── air_play_button.dart │ ├── air_play_event.dart │ ├── air_play_platform.dart │ └── method_channel_air_play.dart │ └── chrome_cast │ ├── chrome_cast_button.dart │ ├── chrome_cast_controller.dart │ ├── chrome_cast_event.dart │ ├── chrome_cast_platform.dart │ └── method_channel_chrome_cast.dart ├── pubspec.lock ├── pubspec.yaml └── test └── flutter_video_cast_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # Visual Studio Code related 19 | .vscode/ 20 | 21 | # Flutter/Dart/Pub related 22 | **/doc/api/ 23 | .dart_tool/ 24 | .flutter-plugins 25 | .packages 26 | .pub-cache/ 27 | .pub/ 28 | /build/ 29 | 30 | # Android related 31 | **/android/**/gradle-wrapper.jar 32 | **/android/.gradle 33 | **/android/captures/ 34 | **/android/gradlew 35 | **/android/gradlew.bat 36 | **/android/local.properties 37 | **/android/**/GeneratedPluginRegistrant.java 38 | 39 | # iOS/XCode related 40 | **/ios/**/*.mode1v3 41 | **/ios/**/*.mode2v3 42 | **/ios/**/*.moved-aside 43 | **/ios/**/*.pbxuser 44 | **/ios/**/*.perspectivev3 45 | **/ios/**/*sync/ 46 | **/ios/**/.sconsign.dblite 47 | **/ios/**/.tags* 48 | **/ios/**/.vagrant/ 49 | **/ios/**/DerivedData/ 50 | **/ios/**/Icon? 51 | **/ios/**/Pods/ 52 | **/ios/**/.symlinks/ 53 | **/ios/**/profile 54 | **/ios/**/xcuserdata 55 | **/ios/.generated/ 56 | **/ios/Flutter/App.framework 57 | **/ios/Flutter/Flutter.framework 58 | **/ios/Flutter/Generated.xcconfig 59 | **/ios/Flutter/app.flx 60 | **/ios/Flutter/app.zip 61 | **/ios/Flutter/flutter_assets/ 62 | **/ios/ServiceDefinitions.json 63 | **/ios/Runner/GeneratedPluginRegistrant.* 64 | 65 | # Exceptions to above rules. 66 | !**/ios/**/default.mode1v3 67 | !**/ios/**/default.mode2v3 68 | !**/ios/**/default.pbxuser 69 | !**/ios/**/default.perspectivev3 70 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 2ae34518b87dd891355ed6c6ea8cb68c4d52bb9d 8 | channel: stable 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | 3 | * First version. 4 | 5 | ## 1.0.1 6 | 7 | * First version. 8 | 9 | ## 1.0.2 10 | 11 | * Fix dependencies 12 | 13 | ## 1.0.3 14 | 15 | * Fix activity implementations 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Aesys 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_video_cast 2 | 3 | A Flutter plugin for iOS and Android for connecting to cast devices like Chromecast and Apple TV. 4 | 5 | ## Installation 6 | 7 | First, add `flutter_video_cast` as a [dependency in your pubspec.yaml file](https://flutter.io/using-packages/). 8 | 9 | ### iOS 10 | 11 | Set the minimum os target to iOS 11.0. 12 | 13 | Initialize the Cast context in the application delegate `ios/Runner/AppDelegate.m`: 14 | 15 | ```swift 16 | import UIKit 17 | import Flutter 18 | import GoogleCast 19 | 20 | @UIApplicationMain 21 | @objc class AppDelegate: FlutterAppDelegate, GCKLoggerDelegate { 22 | let kReceiverAppID = kGCKDefaultMediaReceiverApplicationID 23 | let kDebugLoggingEnabled = true 24 | 25 | override func application( 26 | _ application: UIApplication, 27 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 28 | ) -> Bool { 29 | GeneratedPluginRegistrant.register(with: self) 30 | let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID) 31 | let options = GCKCastOptions(discoveryCriteria: criteria) 32 | GCKCastContext.setSharedInstanceWith(options) 33 | GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true 34 | GCKLogger.sharedInstance().delegate = self 35 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 36 | } 37 | } 38 | ``` 39 | 40 | Opt-in to the embedded views preview by adding a boolean property to the app's `Info.plist` file 41 | with the key `io.flutter.embedded_views_preview` and the value `YES`. 42 | 43 | ### Android 44 | 45 | Add dependencies in your module (app-level) Gradle file (usually `android/app/build.gradle`): 46 | 47 | ```groovy 48 | implementation 'com.google.android.gms:play-services-cast-framework:19.0.0' 49 | implementation 'com.google.android.exoplayer:extension-cast:2.11.5' 50 | ``` 51 | 52 | Set the theme of the MainActivity to `@style/Theme.AppCompat.NoActionBar` in the application manifest `android/app/src/main/AndroidManifest.xml`: 53 | 54 | ```xml 55 | 59 | ... 60 | _CastSampleState(); 97 | } 98 | 99 | class _CastSampleState extends State { 100 | ChromeCastController _controller; 101 | 102 | @override 103 | Widget build(BuildContext context) { 104 | return Scaffold( 105 | appBar: AppBar( 106 | title: Text('Cast Sample'), 107 | actions: [ 108 | ChromeCastButton( 109 | onButtonCreated: (controller) { 110 | setState(() => _controller = controller); 111 | _controller?.addSessionListener(); 112 | }, 113 | onSessionStarted: () { 114 | _controller?.loadMedia('https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'); 115 | }, 116 | ), 117 | ], 118 | ), 119 | ); 120 | } 121 | } 122 | ``` 123 | 124 | See the `example` directory for a complete sample app. 125 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'it.aesys.flutter_video_cast' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | ext.kotlin_version = '1.3.50' 6 | repositories { 7 | google() 8 | jcenter() 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:3.5.0' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | rootProject.allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | } 22 | } 23 | 24 | apply plugin: 'com.android.library' 25 | apply plugin: 'kotlin-android' 26 | 27 | android { 28 | compileSdkVersion 28 29 | 30 | sourceSets { 31 | main.java.srcDirs += 'src/main/kotlin' 32 | } 33 | defaultConfig { 34 | minSdkVersion 16 35 | } 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | } 40 | 41 | dependencies { 42 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 43 | 44 | //ChromeCast dependencies 45 | implementation 'com.google.android.gms:play-services-cast-framework:15.0.0' 46 | implementation 'com.google.android.exoplayer:extension-cast:2.9.6' 47 | } 48 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'flutter_video_cast' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/kotlin/it/aesys/flutter_video_cast/ChromeCastController.kt: -------------------------------------------------------------------------------- 1 | package it.aesys.flutter_video_cast 2 | 3 | import android.content.Context 4 | import android.view.ContextThemeWrapper 5 | import androidx.mediarouter.app.MediaRouteButton 6 | import com.google.android.gms.cast.MediaInfo 7 | import com.google.android.gms.cast.MediaLoadOptions 8 | import com.google.android.gms.cast.framework.CastButtonFactory 9 | import com.google.android.gms.cast.framework.CastContext 10 | import com.google.android.gms.cast.framework.Session 11 | import com.google.android.gms.cast.framework.SessionManagerListener 12 | import com.google.android.gms.common.api.PendingResult 13 | import com.google.android.gms.common.api.Status 14 | import io.flutter.plugin.common.BinaryMessenger 15 | import io.flutter.plugin.common.MethodCall 16 | import io.flutter.plugin.common.MethodChannel 17 | import io.flutter.plugin.platform.PlatformView 18 | 19 | class ChromeCastController( 20 | messenger: BinaryMessenger, 21 | viewId: Int, 22 | context: Context? 23 | ) : PlatformView, MethodChannel.MethodCallHandler, SessionManagerListener, PendingResult.StatusListener { 24 | private val channel = MethodChannel(messenger, "flutter_video_cast/chromeCast_$viewId") 25 | private val chromeCastButton = MediaRouteButton(ContextThemeWrapper(context, R.style.Theme_AppCompat_NoActionBar)) 26 | private val sessionManager = CastContext.getSharedInstance()?.sessionManager 27 | 28 | init { 29 | CastButtonFactory.setUpMediaRouteButton(context, chromeCastButton) 30 | channel.setMethodCallHandler(this) 31 | } 32 | 33 | private fun loadMedia(args: Any?) { 34 | if (args is Map<*, *>) { 35 | val url = args["url"] as? String 36 | val media = MediaInfo.Builder(url).build() 37 | val options = MediaLoadOptions.Builder().build() 38 | val request = sessionManager?.currentCastSession?.remoteMediaClient?.load(media, options) 39 | request?.addStatusListener(this) 40 | } 41 | } 42 | 43 | private fun play() { 44 | val request = sessionManager?.currentCastSession?.remoteMediaClient?.play() 45 | request?.addStatusListener(this) 46 | } 47 | 48 | private fun pause() { 49 | val request = sessionManager?.currentCastSession?.remoteMediaClient?.pause() 50 | request?.addStatusListener(this) 51 | } 52 | 53 | private fun seek(args: Any?) { 54 | if (args is Map<*, *>) { 55 | val relative = (args["relative"] as? Boolean) ?: false 56 | var interval = args["interval"] as? Double 57 | interval = interval?.times(1000) 58 | if (relative) { 59 | interval = interval?.plus(sessionManager?.currentCastSession?.remoteMediaClient?.mediaStatus?.streamPosition ?: 0) 60 | } 61 | val request = sessionManager?.currentCastSession?.remoteMediaClient?.seek(interval?.toLong() ?: 0) 62 | request?.addStatusListener(this) 63 | } 64 | } 65 | 66 | private fun stop() { 67 | val request = sessionManager?.currentCastSession?.remoteMediaClient?.stop() 68 | request?.addStatusListener(this) 69 | } 70 | 71 | private fun isPlaying() = sessionManager?.currentCastSession?.remoteMediaClient?.isPlaying ?: false 72 | 73 | private fun isConnected() = sessionManager?.currentCastSession?.isConnected ?: false 74 | 75 | private fun addSessionListener() { 76 | sessionManager?.addSessionManagerListener(this) 77 | } 78 | 79 | private fun removeSessionListener() { 80 | sessionManager?.removeSessionManagerListener(this) 81 | } 82 | 83 | override fun getView() = chromeCastButton 84 | 85 | override fun dispose() { 86 | 87 | } 88 | 89 | // Flutter methods handling 90 | 91 | override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { 92 | when(call.method) { 93 | "chromeCast#wait" -> result.success(null) 94 | "chromeCast#loadMedia" -> { 95 | loadMedia(call.arguments) 96 | result.success(null) 97 | } 98 | "chromeCast#play" -> { 99 | play() 100 | result.success(null) 101 | } 102 | "chromeCast#pause" -> { 103 | pause() 104 | result.success(null) 105 | } 106 | "chromeCast#seek" -> { 107 | seek(call.arguments) 108 | result.success(null) 109 | } 110 | "chromeCast#stop" -> { 111 | stop() 112 | result.success(null) 113 | } 114 | "chromeCast#isPlaying" -> result.success(isPlaying()) 115 | "chromeCast#isConnected" -> result.success(isConnected()) 116 | "chromeCast#addSessionListener" -> { 117 | addSessionListener() 118 | result.success(null) 119 | } 120 | "chromeCast#removeSessionListener" -> { 121 | removeSessionListener() 122 | result.success(null) 123 | } 124 | } 125 | } 126 | 127 | // SessionManagerListener 128 | 129 | override fun onSessionStarted(p0: Session?, p1: String?) { 130 | channel.invokeMethod("chromeCast#didStartSession", null) 131 | } 132 | 133 | override fun onSessionEnded(p0: Session?, p1: Int) { 134 | channel.invokeMethod("chromeCast#didEndSession", null) 135 | } 136 | 137 | override fun onSessionResuming(p0: Session?, p1: String?) { 138 | 139 | } 140 | 141 | override fun onSessionResumed(p0: Session?, p1: Boolean) { 142 | 143 | } 144 | 145 | override fun onSessionResumeFailed(p0: Session?, p1: Int) { 146 | 147 | } 148 | 149 | override fun onSessionSuspended(p0: Session?, p1: Int) { 150 | 151 | } 152 | 153 | override fun onSessionStarting(p0: Session?) { 154 | 155 | } 156 | 157 | override fun onSessionEnding(p0: Session?) { 158 | 159 | } 160 | 161 | override fun onSessionStartFailed(p0: Session?, p1: Int) { 162 | 163 | } 164 | 165 | // PendingResult.StatusListener 166 | 167 | override fun onComplete(status: Status?) { 168 | if (status?.isSuccess == true) { 169 | channel.invokeMethod("chromeCast#requestDidComplete", null) 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /android/src/main/kotlin/it/aesys/flutter_video_cast/ChromeCastFactory.kt: -------------------------------------------------------------------------------- 1 | package it.aesys.flutter_video_cast 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import io.flutter.plugin.common.BinaryMessenger 6 | import io.flutter.plugin.platform.PlatformViewFactory 7 | import io.flutter.plugin.common.StandardMessageCodec 8 | import io.flutter.plugin.platform.PlatformView 9 | 10 | class ChromeCastFactory(private val binaryMessenger: BinaryMessenger) : PlatformViewFactory(StandardMessageCodec.INSTANCE) { 11 | var activity: Activity? = null 12 | 13 | override fun create( 14 | context: Context?, 15 | viewId: Int, 16 | args: Any? 17 | ): PlatformView = ChromeCastController( 18 | messenger = binaryMessenger, 19 | viewId = viewId, 20 | context = activity 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /android/src/main/kotlin/it/aesys/flutter_video_cast/FlutterVideoCastPlugin.kt: -------------------------------------------------------------------------------- 1 | package it.aesys.flutter_video_cast 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import io.flutter.embedding.engine.plugins.FlutterPlugin 6 | import io.flutter.embedding.engine.plugins.activity.ActivityAware 7 | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding 8 | import io.flutter.plugin.common.PluginRegistry.Registrar 9 | 10 | /** FlutterVideoCastPlugin */ 11 | public class FlutterVideoCastPlugin: FlutterPlugin, ActivityAware { 12 | private lateinit var chromeCastFactory: ChromeCastFactory 13 | 14 | override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { 15 | chromeCastFactory = ChromeCastFactory(flutterPluginBinding.binaryMessenger) 16 | flutterPluginBinding 17 | .platformViewRegistry 18 | .registerViewFactory( 19 | "ChromeCastButton", 20 | chromeCastFactory 21 | ) 22 | } 23 | 24 | // This static function is optional and equivalent to onAttachedToEngine. It supports the old 25 | // pre-Flutter-1.12 Android projects. You are encouraged to continue supporting 26 | // plugin registration via this function while apps migrate to use the new Android APIs 27 | // post-flutter-1.12 via https://flutter.dev/go/android-project-migration. 28 | // 29 | // It is encouraged to share logic between onAttachedToEngine and registerWith to keep 30 | // them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called 31 | // depending on the user's project. onAttachedToEngine or registerWith must both be defined 32 | // in the same class. 33 | companion object { 34 | @JvmStatic 35 | fun registerWith(registrar: Registrar) { 36 | registrar 37 | .platformViewRegistry() 38 | .registerViewFactory( 39 | "ChromeCastButton", 40 | ChromeCastFactory(registrar.messenger()) 41 | ) 42 | } 43 | } 44 | 45 | override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { 46 | 47 | } 48 | 49 | override fun onAttachedToActivity(binding: ActivityPluginBinding) { 50 | chromeCastFactory.activity = binding.activity 51 | } 52 | 53 | override fun onDetachedFromActivityForConfigChanges() { 54 | 55 | } 56 | 57 | override fun onDetachedFromActivity() { 58 | 59 | } 60 | 61 | override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Exceptions to above rules. 44 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 45 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 2ae34518b87dd891355ed6c6ea8cb68c4d52bb9d 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_video_cast_example 2 | 3 | Demonstrates how to use the flutter_video_cast plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 28 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "it.aesys.flutter_video_cast_example" 42 | minSdkVersion 16 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | } 47 | 48 | buildTypes { 49 | release { 50 | // TODO: Add your own signing config for the release build. 51 | // Signing with the debug keys for now, so `flutter run --release` works. 52 | signingConfig signingConfigs.debug 53 | } 54 | } 55 | } 56 | 57 | flutter { 58 | source '../..' 59 | } 60 | 61 | dependencies { 62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 63 | 64 | //ChromeCast dependencies 65 | implementation 'com.google.android.gms:play-services-cast-framework:15.0.0' 66 | implementation 'com.google.android.exoplayer:extension-cast:2.9.6' 67 | } 68 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 12 | 19 | 23 | 27 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 46 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/it/aesys/flutter_video_cast_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package it.aesys.flutter_video_cast_example 2 | 3 | import com.google.android.gms.cast.framework.CastContext 4 | import io.flutter.embedding.android.FlutterFragmentActivity 5 | import io.flutter.embedding.engine.FlutterEngine 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity: FlutterFragmentActivity() { 9 | override fun configureFlutterEngine(flutterEngine: FlutterEngine) { 10 | GeneratedPluginRegistrant.registerWith(flutterEngine) 11 | CastContext.getSharedInstance(applicationContext) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - flutter_video_cast (0.0.1): 4 | - Flutter 5 | - google-cast-sdk-no-bluetooth 6 | - google-cast-sdk-no-bluetooth (4.4.8): 7 | - google-cast-sdk-no-bluetooth/Core (= 4.4.8) 8 | - GTMSessionFetcher/Core (~> 1.0) 9 | - Protobuf (~> 3.12) 10 | - google-cast-sdk-no-bluetooth/Core (4.4.8): 11 | - GTMSessionFetcher/Core (~> 1.0) 12 | - Protobuf (~> 3.12) 13 | - GTMSessionFetcher/Core (1.4.0) 14 | - Protobuf (3.12.0) 15 | 16 | DEPENDENCIES: 17 | - Flutter (from `Flutter`) 18 | - flutter_video_cast (from `.symlinks/plugins/flutter_video_cast/ios`) 19 | 20 | SPEC REPOS: 21 | trunk: 22 | - google-cast-sdk-no-bluetooth 23 | - GTMSessionFetcher 24 | - Protobuf 25 | 26 | EXTERNAL SOURCES: 27 | Flutter: 28 | :path: Flutter 29 | flutter_video_cast: 30 | :path: ".symlinks/plugins/flutter_video_cast/ios" 31 | 32 | SPEC CHECKSUMS: 33 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 34 | flutter_video_cast: f83031b564b79903bc4702165d995ba774bdefe4 35 | google-cast-sdk-no-bluetooth: d5a60530c72b0907fb302d835952b6cb0ba5f128 36 | GTMSessionFetcher: 6f5c8abbab8a9bce4bb3f057e317728ec6182b10 37 | Protobuf: 2793fcd0622a00b546c60e7cbbcc493e043e9bb9 38 | 39 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 40 | 41 | COCOAPODS: 1.9.2 42 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 75EFD306EF133C2C4236B7E8 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D81ACBC917E37EE9F4441326 /* Pods_Runner.framework */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = ""; 24 | dstSubfolderSpec = 10; 25 | files = ( 26 | ); 27 | name = "Embed Frameworks"; 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXCopyFilesBuildPhase section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 34 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 35 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 36 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 37 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 38 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 39 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 40 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 41 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 43 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 44 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 45 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 46 | B42F16E92FB76C89D8D78834 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 47 | BD0DED2C98D6BF5E985AB886 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 48 | C23F3D7BC6E1A0421F0A2DE2 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 49 | D81ACBC917E37EE9F4441326 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | 75EFD306EF133C2C4236B7E8 /* Pods_Runner.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 2B6084F998F9D02950F48359 /* Pods */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | C23F3D7BC6E1A0421F0A2DE2 /* Pods-Runner.debug.xcconfig */, 68 | B42F16E92FB76C89D8D78834 /* Pods-Runner.release.xcconfig */, 69 | BD0DED2C98D6BF5E985AB886 /* Pods-Runner.profile.xcconfig */, 70 | ); 71 | path = Pods; 72 | sourceTree = ""; 73 | }; 74 | 60B137CA7850B091881F03F4 /* Frameworks */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | D81ACBC917E37EE9F4441326 /* Pods_Runner.framework */, 78 | ); 79 | name = Frameworks; 80 | sourceTree = ""; 81 | }; 82 | 9740EEB11CF90186004384FC /* Flutter */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 86 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 87 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 88 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 89 | ); 90 | name = Flutter; 91 | sourceTree = ""; 92 | }; 93 | 97C146E51CF9000F007C117D = { 94 | isa = PBXGroup; 95 | children = ( 96 | 9740EEB11CF90186004384FC /* Flutter */, 97 | 97C146F01CF9000F007C117D /* Runner */, 98 | 97C146EF1CF9000F007C117D /* Products */, 99 | 2B6084F998F9D02950F48359 /* Pods */, 100 | 60B137CA7850B091881F03F4 /* Frameworks */, 101 | ); 102 | sourceTree = ""; 103 | }; 104 | 97C146EF1CF9000F007C117D /* Products */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146EE1CF9000F007C117D /* Runner.app */, 108 | ); 109 | name = Products; 110 | sourceTree = ""; 111 | }; 112 | 97C146F01CF9000F007C117D /* Runner */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 116 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 117 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 118 | 97C147021CF9000F007C117D /* Info.plist */, 119 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 120 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 121 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 122 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 123 | ); 124 | path = Runner; 125 | sourceTree = ""; 126 | }; 127 | /* End PBXGroup section */ 128 | 129 | /* Begin PBXNativeTarget section */ 130 | 97C146ED1CF9000F007C117D /* Runner */ = { 131 | isa = PBXNativeTarget; 132 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 133 | buildPhases = ( 134 | 2A0C812C1886FD909B716390 /* [CP] Check Pods Manifest.lock */, 135 | 9740EEB61CF901F6004384FC /* Run Script */, 136 | 97C146EA1CF9000F007C117D /* Sources */, 137 | 97C146EB1CF9000F007C117D /* Frameworks */, 138 | 97C146EC1CF9000F007C117D /* Resources */, 139 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 140 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 141 | 26E1AAFF5EFC044523C443FF /* [CP] Embed Pods Frameworks */, 142 | FE80AFC75EBDA81334AC0CF2 /* [CP] Copy Pods Resources */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | ); 148 | name = Runner; 149 | productName = Runner; 150 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 151 | productType = "com.apple.product-type.application"; 152 | }; 153 | /* End PBXNativeTarget section */ 154 | 155 | /* Begin PBXProject section */ 156 | 97C146E61CF9000F007C117D /* Project object */ = { 157 | isa = PBXProject; 158 | attributes = { 159 | LastUpgradeCheck = 1020; 160 | ORGANIZATIONNAME = ""; 161 | TargetAttributes = { 162 | 97C146ED1CF9000F007C117D = { 163 | CreatedOnToolsVersion = 7.3.1; 164 | LastSwiftMigration = 1100; 165 | }; 166 | }; 167 | }; 168 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 169 | compatibilityVersion = "Xcode 9.3"; 170 | developmentRegion = en; 171 | hasScannedForEncodings = 0; 172 | knownRegions = ( 173 | en, 174 | Base, 175 | ); 176 | mainGroup = 97C146E51CF9000F007C117D; 177 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 178 | projectDirPath = ""; 179 | projectRoot = ""; 180 | targets = ( 181 | 97C146ED1CF9000F007C117D /* Runner */, 182 | ); 183 | }; 184 | /* End PBXProject section */ 185 | 186 | /* Begin PBXResourcesBuildPhase section */ 187 | 97C146EC1CF9000F007C117D /* Resources */ = { 188 | isa = PBXResourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 192 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 193 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 194 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXResourcesBuildPhase section */ 199 | 200 | /* Begin PBXShellScriptBuildPhase section */ 201 | 26E1AAFF5EFC044523C443FF /* [CP] Embed Pods Frameworks */ = { 202 | isa = PBXShellScriptBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | ); 206 | inputFileListPaths = ( 207 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", 208 | ); 209 | name = "[CP] Embed Pods Frameworks"; 210 | outputFileListPaths = ( 211 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | shellPath = /bin/sh; 215 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 216 | showEnvVarsInLog = 0; 217 | }; 218 | 2A0C812C1886FD909B716390 /* [CP] Check Pods Manifest.lock */ = { 219 | isa = PBXShellScriptBuildPhase; 220 | buildActionMask = 2147483647; 221 | files = ( 222 | ); 223 | inputFileListPaths = ( 224 | ); 225 | inputPaths = ( 226 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 227 | "${PODS_ROOT}/Manifest.lock", 228 | ); 229 | name = "[CP] Check Pods Manifest.lock"; 230 | outputFileListPaths = ( 231 | ); 232 | outputPaths = ( 233 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | shellPath = /bin/sh; 237 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 238 | showEnvVarsInLog = 0; 239 | }; 240 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 241 | isa = PBXShellScriptBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | ); 245 | inputPaths = ( 246 | ); 247 | name = "Thin Binary"; 248 | outputPaths = ( 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | shellPath = /bin/sh; 252 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 253 | }; 254 | 9740EEB61CF901F6004384FC /* Run Script */ = { 255 | isa = PBXShellScriptBuildPhase; 256 | buildActionMask = 2147483647; 257 | files = ( 258 | ); 259 | inputPaths = ( 260 | ); 261 | name = "Run Script"; 262 | outputPaths = ( 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | shellPath = /bin/sh; 266 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 267 | }; 268 | FE80AFC75EBDA81334AC0CF2 /* [CP] Copy Pods Resources */ = { 269 | isa = PBXShellScriptBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | ); 273 | inputFileListPaths = ( 274 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", 275 | ); 276 | name = "[CP] Copy Pods Resources"; 277 | outputFileListPaths = ( 278 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | shellPath = /bin/sh; 282 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; 283 | showEnvVarsInLog = 0; 284 | }; 285 | /* End PBXShellScriptBuildPhase section */ 286 | 287 | /* Begin PBXSourcesBuildPhase section */ 288 | 97C146EA1CF9000F007C117D /* Sources */ = { 289 | isa = PBXSourcesBuildPhase; 290 | buildActionMask = 2147483647; 291 | files = ( 292 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 293 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 294 | ); 295 | runOnlyForDeploymentPostprocessing = 0; 296 | }; 297 | /* End PBXSourcesBuildPhase section */ 298 | 299 | /* Begin PBXVariantGroup section */ 300 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 301 | isa = PBXVariantGroup; 302 | children = ( 303 | 97C146FB1CF9000F007C117D /* Base */, 304 | ); 305 | name = Main.storyboard; 306 | sourceTree = ""; 307 | }; 308 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 309 | isa = PBXVariantGroup; 310 | children = ( 311 | 97C147001CF9000F007C117D /* Base */, 312 | ); 313 | name = LaunchScreen.storyboard; 314 | sourceTree = ""; 315 | }; 316 | /* End PBXVariantGroup section */ 317 | 318 | /* Begin XCBuildConfiguration section */ 319 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 320 | isa = XCBuildConfiguration; 321 | buildSettings = { 322 | ALWAYS_SEARCH_USER_PATHS = NO; 323 | CLANG_ANALYZER_NONNULL = YES; 324 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 325 | CLANG_CXX_LIBRARY = "libc++"; 326 | CLANG_ENABLE_MODULES = YES; 327 | CLANG_ENABLE_OBJC_ARC = YES; 328 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 329 | CLANG_WARN_BOOL_CONVERSION = YES; 330 | CLANG_WARN_COMMA = YES; 331 | CLANG_WARN_CONSTANT_CONVERSION = YES; 332 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 333 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 334 | CLANG_WARN_EMPTY_BODY = YES; 335 | CLANG_WARN_ENUM_CONVERSION = YES; 336 | CLANG_WARN_INFINITE_RECURSION = YES; 337 | CLANG_WARN_INT_CONVERSION = YES; 338 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 339 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 340 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 341 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 342 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 343 | CLANG_WARN_STRICT_PROTOTYPES = YES; 344 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 345 | CLANG_WARN_UNREACHABLE_CODE = YES; 346 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 347 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 348 | COPY_PHASE_STRIP = NO; 349 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 350 | ENABLE_NS_ASSERTIONS = NO; 351 | ENABLE_STRICT_OBJC_MSGSEND = YES; 352 | GCC_C_LANGUAGE_STANDARD = gnu99; 353 | GCC_NO_COMMON_BLOCKS = YES; 354 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 355 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 356 | GCC_WARN_UNDECLARED_SELECTOR = YES; 357 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 358 | GCC_WARN_UNUSED_FUNCTION = YES; 359 | GCC_WARN_UNUSED_VARIABLE = YES; 360 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 361 | MTL_ENABLE_DEBUG_INFO = NO; 362 | SDKROOT = iphoneos; 363 | SUPPORTED_PLATFORMS = iphoneos; 364 | TARGETED_DEVICE_FAMILY = "1,2"; 365 | VALIDATE_PRODUCT = YES; 366 | }; 367 | name = Profile; 368 | }; 369 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 370 | isa = XCBuildConfiguration; 371 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 372 | buildSettings = { 373 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 374 | CLANG_ENABLE_MODULES = YES; 375 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 376 | DEVELOPMENT_TEAM = ""; 377 | ENABLE_BITCODE = NO; 378 | FRAMEWORK_SEARCH_PATHS = ( 379 | "$(inherited)", 380 | "$(PROJECT_DIR)/Flutter", 381 | ); 382 | INFOPLIST_FILE = Runner/Info.plist; 383 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 384 | LD_RUNPATH_SEARCH_PATHS = ( 385 | "$(inherited)", 386 | "@executable_path/Frameworks", 387 | ); 388 | LIBRARY_SEARCH_PATHS = ( 389 | "$(inherited)", 390 | "$(PROJECT_DIR)/Flutter", 391 | ); 392 | PRODUCT_BUNDLE_IDENTIFIER = it.aesys.flutterVideoCastExample; 393 | PRODUCT_NAME = "$(TARGET_NAME)"; 394 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 395 | SWIFT_VERSION = 5.0; 396 | VERSIONING_SYSTEM = "apple-generic"; 397 | }; 398 | name = Profile; 399 | }; 400 | 97C147031CF9000F007C117D /* Debug */ = { 401 | isa = XCBuildConfiguration; 402 | buildSettings = { 403 | ALWAYS_SEARCH_USER_PATHS = NO; 404 | CLANG_ANALYZER_NONNULL = YES; 405 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 406 | CLANG_CXX_LIBRARY = "libc++"; 407 | CLANG_ENABLE_MODULES = YES; 408 | CLANG_ENABLE_OBJC_ARC = YES; 409 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 410 | CLANG_WARN_BOOL_CONVERSION = YES; 411 | CLANG_WARN_COMMA = YES; 412 | CLANG_WARN_CONSTANT_CONVERSION = YES; 413 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 414 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 415 | CLANG_WARN_EMPTY_BODY = YES; 416 | CLANG_WARN_ENUM_CONVERSION = YES; 417 | CLANG_WARN_INFINITE_RECURSION = YES; 418 | CLANG_WARN_INT_CONVERSION = YES; 419 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 420 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 421 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 422 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 423 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 424 | CLANG_WARN_STRICT_PROTOTYPES = YES; 425 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 426 | CLANG_WARN_UNREACHABLE_CODE = YES; 427 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 428 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 429 | COPY_PHASE_STRIP = NO; 430 | DEBUG_INFORMATION_FORMAT = dwarf; 431 | ENABLE_STRICT_OBJC_MSGSEND = YES; 432 | ENABLE_TESTABILITY = YES; 433 | GCC_C_LANGUAGE_STANDARD = gnu99; 434 | GCC_DYNAMIC_NO_PIC = NO; 435 | GCC_NO_COMMON_BLOCKS = YES; 436 | GCC_OPTIMIZATION_LEVEL = 0; 437 | GCC_PREPROCESSOR_DEFINITIONS = ( 438 | "DEBUG=1", 439 | "$(inherited)", 440 | ); 441 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 442 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 443 | GCC_WARN_UNDECLARED_SELECTOR = YES; 444 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 445 | GCC_WARN_UNUSED_FUNCTION = YES; 446 | GCC_WARN_UNUSED_VARIABLE = YES; 447 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 448 | MTL_ENABLE_DEBUG_INFO = YES; 449 | ONLY_ACTIVE_ARCH = YES; 450 | SDKROOT = iphoneos; 451 | TARGETED_DEVICE_FAMILY = "1,2"; 452 | }; 453 | name = Debug; 454 | }; 455 | 97C147041CF9000F007C117D /* Release */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | ALWAYS_SEARCH_USER_PATHS = NO; 459 | CLANG_ANALYZER_NONNULL = YES; 460 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 461 | CLANG_CXX_LIBRARY = "libc++"; 462 | CLANG_ENABLE_MODULES = YES; 463 | CLANG_ENABLE_OBJC_ARC = YES; 464 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 465 | CLANG_WARN_BOOL_CONVERSION = YES; 466 | CLANG_WARN_COMMA = YES; 467 | CLANG_WARN_CONSTANT_CONVERSION = YES; 468 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 469 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 470 | CLANG_WARN_EMPTY_BODY = YES; 471 | CLANG_WARN_ENUM_CONVERSION = YES; 472 | CLANG_WARN_INFINITE_RECURSION = YES; 473 | CLANG_WARN_INT_CONVERSION = YES; 474 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 475 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 476 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 477 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 478 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 479 | CLANG_WARN_STRICT_PROTOTYPES = YES; 480 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 481 | CLANG_WARN_UNREACHABLE_CODE = YES; 482 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 483 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 484 | COPY_PHASE_STRIP = NO; 485 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 486 | ENABLE_NS_ASSERTIONS = NO; 487 | ENABLE_STRICT_OBJC_MSGSEND = YES; 488 | GCC_C_LANGUAGE_STANDARD = gnu99; 489 | GCC_NO_COMMON_BLOCKS = YES; 490 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 491 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 492 | GCC_WARN_UNDECLARED_SELECTOR = YES; 493 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 494 | GCC_WARN_UNUSED_FUNCTION = YES; 495 | GCC_WARN_UNUSED_VARIABLE = YES; 496 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 497 | MTL_ENABLE_DEBUG_INFO = NO; 498 | SDKROOT = iphoneos; 499 | SUPPORTED_PLATFORMS = iphoneos; 500 | SWIFT_COMPILATION_MODE = wholemodule; 501 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 502 | TARGETED_DEVICE_FAMILY = "1,2"; 503 | VALIDATE_PRODUCT = YES; 504 | }; 505 | name = Release; 506 | }; 507 | 97C147061CF9000F007C117D /* Debug */ = { 508 | isa = XCBuildConfiguration; 509 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 510 | buildSettings = { 511 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 512 | CLANG_ENABLE_MODULES = YES; 513 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 514 | DEVELOPMENT_TEAM = ""; 515 | ENABLE_BITCODE = NO; 516 | FRAMEWORK_SEARCH_PATHS = ( 517 | "$(inherited)", 518 | "$(PROJECT_DIR)/Flutter", 519 | ); 520 | INFOPLIST_FILE = Runner/Info.plist; 521 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 522 | LD_RUNPATH_SEARCH_PATHS = ( 523 | "$(inherited)", 524 | "@executable_path/Frameworks", 525 | ); 526 | LIBRARY_SEARCH_PATHS = ( 527 | "$(inherited)", 528 | "$(PROJECT_DIR)/Flutter", 529 | ); 530 | PRODUCT_BUNDLE_IDENTIFIER = it.aesys.flutterVideoCastExample; 531 | PRODUCT_NAME = "$(TARGET_NAME)"; 532 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 533 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 534 | SWIFT_VERSION = 5.0; 535 | VERSIONING_SYSTEM = "apple-generic"; 536 | }; 537 | name = Debug; 538 | }; 539 | 97C147071CF9000F007C117D /* Release */ = { 540 | isa = XCBuildConfiguration; 541 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 542 | buildSettings = { 543 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 544 | CLANG_ENABLE_MODULES = YES; 545 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 546 | DEVELOPMENT_TEAM = ""; 547 | ENABLE_BITCODE = NO; 548 | FRAMEWORK_SEARCH_PATHS = ( 549 | "$(inherited)", 550 | "$(PROJECT_DIR)/Flutter", 551 | ); 552 | INFOPLIST_FILE = Runner/Info.plist; 553 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 554 | LD_RUNPATH_SEARCH_PATHS = ( 555 | "$(inherited)", 556 | "@executable_path/Frameworks", 557 | ); 558 | LIBRARY_SEARCH_PATHS = ( 559 | "$(inherited)", 560 | "$(PROJECT_DIR)/Flutter", 561 | ); 562 | PRODUCT_BUNDLE_IDENTIFIER = it.aesys.flutterVideoCastExample; 563 | PRODUCT_NAME = "$(TARGET_NAME)"; 564 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 565 | SWIFT_VERSION = 5.0; 566 | VERSIONING_SYSTEM = "apple-generic"; 567 | }; 568 | name = Release; 569 | }; 570 | /* End XCBuildConfiguration section */ 571 | 572 | /* Begin XCConfigurationList section */ 573 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 574 | isa = XCConfigurationList; 575 | buildConfigurations = ( 576 | 97C147031CF9000F007C117D /* Debug */, 577 | 97C147041CF9000F007C117D /* Release */, 578 | 249021D3217E4FDB00AE95B9 /* Profile */, 579 | ); 580 | defaultConfigurationIsVisible = 0; 581 | defaultConfigurationName = Release; 582 | }; 583 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 584 | isa = XCConfigurationList; 585 | buildConfigurations = ( 586 | 97C147061CF9000F007C117D /* Debug */, 587 | 97C147071CF9000F007C117D /* Release */, 588 | 249021D4217E4FDB00AE95B9 /* Profile */, 589 | ); 590 | defaultConfigurationIsVisible = 0; 591 | defaultConfigurationName = Release; 592 | }; 593 | /* End XCConfigurationList section */ 594 | }; 595 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 596 | } 597 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | import GoogleCast 4 | 5 | @UIApplicationMain 6 | @objc class AppDelegate: FlutterAppDelegate, GCKLoggerDelegate { 7 | let kReceiverAppID = kGCKDefaultMediaReceiverApplicationID 8 | let kDebugLoggingEnabled = true 9 | 10 | override func application( 11 | _ application: UIApplication, 12 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 13 | ) -> Bool { 14 | GeneratedPluginRegistrant.register(with: self) 15 | let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID) 16 | let options = GCKCastOptions(discoveryCriteria: criteria) 17 | GCKCastContext.setSharedInstanceWith(options) 18 | GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true 19 | GCKLogger.sharedInstance().delegate = self 20 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | io.flutter.embedded_views_preview 6 | YES 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_video_cast_example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_video_cast/flutter_video_cast.dart'; 3 | 4 | void main() { 5 | runApp(MyApp()); 6 | } 7 | 8 | class MyApp extends StatelessWidget { 9 | @override 10 | Widget build(BuildContext context) { 11 | return MaterialApp( 12 | home: CastSample() 13 | ); 14 | } 15 | } 16 | 17 | class CastSample extends StatefulWidget { 18 | static const _iconSize = 50.0; 19 | 20 | @override 21 | _CastSampleState createState() => _CastSampleState(); 22 | } 23 | 24 | class _CastSampleState extends State { 25 | ChromeCastController _controller; 26 | AppState _state = AppState.idle; 27 | bool _playing = false; 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Scaffold( 32 | appBar: AppBar( 33 | title: Text('Plugin example app'), 34 | actions: [ 35 | AirPlayButton( 36 | size: CastSample._iconSize, 37 | color: Colors.white, 38 | activeColor: Colors.amber, 39 | onRoutesOpening: () => print('opening'), 40 | onRoutesClosed: () => print('closed'), 41 | ), 42 | ChromeCastButton( 43 | size: CastSample._iconSize, 44 | color: Colors.white, 45 | onButtonCreated: _onButtonCreated, 46 | onSessionStarted: _onSessionStarted, 47 | onSessionEnded: () => setState(() => _state = AppState.idle), 48 | onRequestCompleted: _onRequestCompleted, 49 | onRequestFailed: _onRequestFailed, 50 | ), 51 | ], 52 | ), 53 | body: Center(child: _handleState()), 54 | ); 55 | } 56 | 57 | Widget _handleState() { 58 | switch(_state) { 59 | case AppState.idle: 60 | return Text('ChromeCast not connected'); 61 | case AppState.connected: 62 | return Text('No media loaded'); 63 | case AppState.mediaLoaded: 64 | return _mediaControls(); 65 | case AppState.error: 66 | return Text('An error has occurred'); 67 | default: 68 | return Container(); 69 | } 70 | } 71 | 72 | Widget _mediaControls() { 73 | return Row( 74 | mainAxisAlignment: MainAxisAlignment.center, 75 | children: [ 76 | _RoundIconButton( 77 | icon: Icons.replay_10, 78 | onPressed: () => _controller.seek(relative: true, interval: -10.0), 79 | ), 80 | _RoundIconButton( 81 | icon: _playing 82 | ? Icons.pause 83 | : Icons.play_arrow, 84 | onPressed: _playPause 85 | ), 86 | _RoundIconButton( 87 | icon: Icons.forward_10, 88 | onPressed: () => _controller.seek(relative: true, interval: 10.0), 89 | ) 90 | ], 91 | ); 92 | } 93 | 94 | Future _playPause() async { 95 | final playing = await _controller.isPlaying(); 96 | if(playing) { 97 | await _controller.pause(); 98 | } else { 99 | await _controller.play(); 100 | } 101 | setState(() => _playing = !playing); 102 | } 103 | 104 | Future _onButtonCreated(ChromeCastController controller) async { 105 | _controller = controller; 106 | await _controller.addSessionListener(); 107 | } 108 | 109 | Future _onSessionStarted() async { 110 | setState(() => _state = AppState.connected); 111 | await _controller.loadMedia('https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'); 112 | } 113 | 114 | Future _onRequestCompleted() async { 115 | final playing = await _controller.isPlaying(); 116 | setState(() { 117 | _state = AppState.mediaLoaded; 118 | _playing = playing; 119 | }); 120 | } 121 | 122 | Future _onRequestFailed(String error) async { 123 | setState(() => _state = AppState.error); 124 | print(error); 125 | } 126 | } 127 | 128 | class _RoundIconButton extends StatelessWidget { 129 | final IconData icon; 130 | final VoidCallback onPressed; 131 | 132 | _RoundIconButton({ 133 | @required this.icon, 134 | @required this.onPressed 135 | }); 136 | 137 | @override 138 | Widget build(BuildContext context) { 139 | return RaisedButton( 140 | child: Icon( 141 | icon, 142 | color: Colors.white 143 | ), 144 | padding: EdgeInsets.all(16.0), 145 | color: Colors.blue, 146 | shape: CircleBorder(), 147 | onPressed: onPressed 148 | ); 149 | } 150 | } 151 | 152 | enum AppState { 153 | idle, 154 | connected, 155 | mediaLoaded, 156 | error 157 | } 158 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.4.2" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.0.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.0.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.1.3" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.0.1" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.14.13" 46 | cupertino_icons: 47 | dependency: "direct main" 48 | description: 49 | name: cupertino_icons 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "0.1.3" 53 | fake_async: 54 | dependency: transitive 55 | description: 56 | name: fake_async 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.1.0" 60 | flutter: 61 | dependency: "direct main" 62 | description: flutter 63 | source: sdk 64 | version: "0.0.0" 65 | flutter_test: 66 | dependency: "direct dev" 67 | description: flutter 68 | source: sdk 69 | version: "0.0.0" 70 | flutter_video_cast: 71 | dependency: "direct main" 72 | description: 73 | path: ".." 74 | relative: true 75 | source: path 76 | version: "1.0.1" 77 | matcher: 78 | dependency: transitive 79 | description: 80 | name: matcher 81 | url: "https://pub.dartlang.org" 82 | source: hosted 83 | version: "0.12.8" 84 | meta: 85 | dependency: transitive 86 | description: 87 | name: meta 88 | url: "https://pub.dartlang.org" 89 | source: hosted 90 | version: "1.1.8" 91 | path: 92 | dependency: transitive 93 | description: 94 | name: path 95 | url: "https://pub.dartlang.org" 96 | source: hosted 97 | version: "1.7.0" 98 | sky_engine: 99 | dependency: transitive 100 | description: flutter 101 | source: sdk 102 | version: "0.0.99" 103 | source_span: 104 | dependency: transitive 105 | description: 106 | name: source_span 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "1.7.0" 110 | stack_trace: 111 | dependency: transitive 112 | description: 113 | name: stack_trace 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "1.9.5" 117 | stream_channel: 118 | dependency: transitive 119 | description: 120 | name: stream_channel 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "2.0.0" 124 | stream_transform: 125 | dependency: transitive 126 | description: 127 | name: stream_transform 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.2.0" 131 | string_scanner: 132 | dependency: transitive 133 | description: 134 | name: string_scanner 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "1.0.5" 138 | term_glyph: 139 | dependency: transitive 140 | description: 141 | name: term_glyph 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "1.1.0" 145 | test_api: 146 | dependency: transitive 147 | description: 148 | name: test_api 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "0.2.17" 152 | typed_data: 153 | dependency: transitive 154 | description: 155 | name: typed_data 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "1.2.0" 159 | vector_math: 160 | dependency: transitive 161 | description: 162 | name: vector_math 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "2.0.8" 166 | sdks: 167 | dart: ">=2.9.0-14.0.dev <3.0.0" 168 | flutter: ">=1.20.0 <2.0.0" 169 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_video_cast_example 2 | description: Demonstrates how to use the flutter_video_cast plugin. 3 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 4 | 5 | environment: 6 | sdk: ">=2.7.0 <3.0.0" 7 | 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | 12 | flutter_video_cast: 13 | path: ../ 14 | 15 | cupertino_icons: ^0.1.3 16 | 17 | dev_dependencies: 18 | flutter_test: 19 | sdk: flutter 20 | 21 | flutter: 22 | 23 | uses-material-design: true 24 | 25 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:flutter_video_cast_example/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Verify Platform version', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that platform version is retrieved. 19 | expect( 20 | find.byWidgetPredicate( 21 | (Widget widget) => widget is Text && 22 | widget.data.startsWith('Running on:'), 23 | ), 24 | findsOneWidget, 25 | ); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalaTeam/flutter_video_cast/525103793e4b2850edd0bfe3c49d2c2a7577ec1c/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/AirPlayController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirPlayController.swift 3 | // flutter_video_cast 4 | // 5 | // Created by Alessio Valentini on 07/08/2020. 6 | // 7 | 8 | import Flutter 9 | import AVKit 10 | 11 | public class AirPlayController: NSObject, FlutterPlatformView { 12 | 13 | // MARK: - Internal properties 14 | 15 | private let channel: FlutterMethodChannel 16 | private let airPlayButton: AVRoutePickerView 17 | private let audioSession = AVAudioSession.sharedInstance() 18 | private let routeDetector = AVRouteDetector() 19 | 20 | // MARK: - Init 21 | 22 | init( 23 | withFrame frame: CGRect, 24 | viewIdentifier viewId: Int64, 25 | arguments args: Any?, 26 | registrar: FlutterPluginRegistrar 27 | ) { 28 | self.channel = FlutterMethodChannel(name: "flutter_video_cast/airPlay_\(viewId)", binaryMessenger: registrar.messenger()) 29 | self.airPlayButton = AVRoutePickerView(frame: frame) 30 | super.init() 31 | self.configure(arguments: args) 32 | } 33 | 34 | public func view() -> UIView { 35 | return airPlayButton 36 | } 37 | 38 | private func configure(arguments args: Any?) { 39 | airPlayButton.delegate = self 40 | setTintColor(arguments: args) 41 | setActiveTintColor(arguments: args) 42 | setMethodCallHandler() 43 | } 44 | 45 | // MARK: - Styling 46 | 47 | private func setTintColor(arguments args: Any?) { 48 | guard 49 | let args = args as? [String: Any], 50 | let red = args["red"] as? CGFloat, 51 | let green = args["green"] as? CGFloat, 52 | let blue = args["blue"] as? CGFloat, 53 | let alpha = args["alpha"] as? Int 54 | else { 55 | return 56 | } 57 | airPlayButton.tintColor = UIColor( 58 | red: red / 255, 59 | green: green / 255, 60 | blue: blue / 255, 61 | alpha: CGFloat(alpha) / 255 62 | ) 63 | } 64 | 65 | private func setActiveTintColor(arguments args: Any?) { 66 | guard 67 | let args = args as? [String: Any], 68 | let red = args["activeRed"] as? CGFloat, 69 | let green = args["activeGreen"] as? CGFloat, 70 | let blue = args["activeBlue"] as? CGFloat, 71 | let alpha = args["activeAlpha"] as? Int 72 | else { 73 | return 74 | } 75 | airPlayButton.activeTintColor = UIColor( 76 | red: red / 255, 77 | green: green / 255, 78 | blue: blue / 255, 79 | alpha: CGFloat(alpha) / 255 80 | ) 81 | } 82 | 83 | // MARK: - Flutter methods handling 84 | 85 | private func setMethodCallHandler() { 86 | channel.setMethodCallHandler { call, result in 87 | self.onMethodCall(call: call, result: result) 88 | } 89 | } 90 | 91 | private func onMethodCall(call: FlutterMethodCall, result: FlutterResult) { 92 | switch call.method { 93 | case "airPlay#wait": 94 | result(nil) 95 | break 96 | case "airPlay#isConnected": 97 | result(isConnected()) 98 | break 99 | default: 100 | break 101 | } 102 | } 103 | 104 | private func isConnected() -> Bool { 105 | return audioSession.currentRoute.outputs.contains { $0.portType == AVAudioSession.Port.airPlay } 106 | } 107 | } 108 | 109 | // MARK: - AVRoutePickerViewDelegate 110 | 111 | extension AirPlayController: AVRoutePickerViewDelegate { 112 | public func routePickerViewWillBeginPresentingRoutes(_ routePickerView: AVRoutePickerView) { 113 | channel.invokeMethod("airPlay#onRoutesOpening", arguments: nil) 114 | } 115 | 116 | public func routePickerViewDidEndPresentingRoutes(_ routePickerView: AVRoutePickerView) { 117 | channel.invokeMethod("airPlay#onRoutesClosed", arguments: nil) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /ios/Classes/AirPlayFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirPlayFactory.swift 3 | // flutter_video_cast 4 | // 5 | // Created by Alessio Valentini on 07/08/2020. 6 | // 7 | 8 | import Flutter 9 | 10 | public class AirPlayFactory: NSObject, FlutterPlatformViewFactory { 11 | let registrar: FlutterPluginRegistrar 12 | 13 | init(registrar: FlutterPluginRegistrar) { 14 | self.registrar = registrar 15 | } 16 | 17 | public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { 18 | return AirPlayController(withFrame: frame, viewIdentifier: viewId, arguments: args, registrar: registrar) 19 | } 20 | 21 | public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { 22 | return FlutterStandardMessageCodec.sharedInstance() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ios/Classes/ChromeCastController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChromeCastController.swift 3 | // flutter_video_cast 4 | // 5 | // Created by Alessio Valentini on 07/08/2020. 6 | // 7 | 8 | import Flutter 9 | import GoogleCast 10 | 11 | class ChromeCastController: NSObject, FlutterPlatformView { 12 | 13 | // MARK: - Internal properties 14 | 15 | private let channel: FlutterMethodChannel 16 | private let chromeCastButton: GCKUICastButton 17 | private let sessionManager = GCKCastContext.sharedInstance().sessionManager 18 | 19 | // MARK: - Init 20 | 21 | init( 22 | withFrame frame: CGRect, 23 | viewIdentifier viewId: Int64, 24 | arguments args: Any?, 25 | registrar: FlutterPluginRegistrar 26 | ) { 27 | self.channel = FlutterMethodChannel(name: "flutter_video_cast/chromeCast_\(viewId)", binaryMessenger: registrar.messenger()) 28 | self.chromeCastButton = GCKUICastButton(frame: frame) 29 | super.init() 30 | self.configure(arguments: args) 31 | } 32 | 33 | func view() -> UIView { 34 | return chromeCastButton 35 | } 36 | 37 | private func configure(arguments args: Any?) { 38 | setTint(arguments: args) 39 | setMethodCallHandler() 40 | } 41 | 42 | // MARK: - Styling 43 | 44 | private func setTint(arguments args: Any?) { 45 | guard 46 | let args = args as? [String: Any], 47 | let red = args["red"] as? CGFloat, 48 | let green = args["green"] as? CGFloat, 49 | let blue = args["blue"] as? CGFloat, 50 | let alpha = args["alpha"] as? Int else { 51 | print("Invalid color") 52 | return 53 | } 54 | chromeCastButton.tintColor = UIColor( 55 | red: red / 255, 56 | green: green / 255, 57 | blue: blue / 255, 58 | alpha: CGFloat(alpha) / 255 59 | ) 60 | } 61 | 62 | // MARK: - Flutter methods handling 63 | 64 | private func setMethodCallHandler() { 65 | channel.setMethodCallHandler { call, result in 66 | self.onMethodCall(call: call, result: result) 67 | } 68 | } 69 | 70 | private func onMethodCall(call: FlutterMethodCall, result: FlutterResult) { 71 | switch call.method { 72 | case "chromeCast#wait": 73 | result(nil) 74 | break 75 | case "chromeCast#loadMedia": 76 | loadMedia(args: call.arguments) 77 | result(nil) 78 | break 79 | case "chromeCast#play": 80 | play() 81 | result(nil) 82 | break 83 | case "chromeCast#pause": 84 | pause() 85 | result(nil) 86 | break 87 | case "chromeCast#seek": 88 | seek(args: call.arguments) 89 | result(nil) 90 | break 91 | case "chromeCast#stop": 92 | stop() 93 | result(nil) 94 | break 95 | case "chromeCast#isConnected": 96 | result(isConnected()) 97 | break 98 | case "chromeCast#isPlaying": 99 | result(isPlaying()) 100 | break 101 | case "chromeCast#addSessionListener": 102 | addSessionListener() 103 | result(nil) 104 | case "chromeCast#removeSessionListener": 105 | removeSessionListener() 106 | result(nil) 107 | default: 108 | result(nil) 109 | break 110 | } 111 | } 112 | 113 | private func loadMedia(args: Any?) { 114 | guard 115 | let args = args as? [String: Any], 116 | let url = args["url"] as? String, 117 | let mediaUrl = URL(string: url) else { 118 | print("Invalid URL") 119 | return 120 | } 121 | let mediaInformation = GCKMediaInformationBuilder(contentURL: mediaUrl).build() 122 | if let request = sessionManager.currentCastSession?.remoteMediaClient?.loadMedia(mediaInformation) { 123 | request.delegate = self 124 | } 125 | } 126 | 127 | private func play() { 128 | if let request = sessionManager.currentCastSession?.remoteMediaClient?.play() { 129 | request.delegate = self 130 | } 131 | } 132 | 133 | private func pause() { 134 | if let request = sessionManager.currentCastSession?.remoteMediaClient?.pause() { 135 | request.delegate = self 136 | } 137 | } 138 | 139 | private func seek(args: Any?) { 140 | guard 141 | let args = args as? [String: Any], 142 | let relative = args["relative"] as? Bool, 143 | let interval = args["interval"] as? Double else { 144 | return 145 | } 146 | let seekOptions = GCKMediaSeekOptions() 147 | seekOptions.relative = relative 148 | seekOptions.interval = interval 149 | if let request = sessionManager.currentCastSession?.remoteMediaClient?.seek(with: seekOptions) { 150 | request.delegate = self 151 | } 152 | } 153 | 154 | private func stop() { 155 | if let request = sessionManager.currentCastSession?.remoteMediaClient?.stop() { 156 | request.delegate = self 157 | } 158 | } 159 | 160 | private func isConnected() -> Bool { 161 | return sessionManager.currentCastSession?.remoteMediaClient?.connected ?? false 162 | } 163 | 164 | private func isPlaying() -> Bool { 165 | return sessionManager.currentCastSession?.remoteMediaClient?.mediaStatus?.playerState == GCKMediaPlayerState.playing 166 | } 167 | 168 | private func addSessionListener() { 169 | sessionManager.add(self) 170 | } 171 | 172 | private func removeSessionListener() { 173 | sessionManager.remove(self) 174 | } 175 | } 176 | 177 | // MARK: - GCKSessionManagerListener 178 | 179 | extension ChromeCastController: GCKSessionManagerListener { 180 | func sessionManager(_ sessionManager: GCKSessionManager, didStart session: GCKSession) { 181 | channel.invokeMethod("chromeCast#didStartSession", arguments: nil) 182 | } 183 | 184 | func sessionManager(_ sessionManager: GCKSessionManager, didEnd session: GCKSession, withError error: Error?) { 185 | channel.invokeMethod("chromeCast#didEndSession", arguments: nil) 186 | } 187 | } 188 | 189 | // MARK: - GCKRequestDelegate 190 | 191 | extension ChromeCastController: GCKRequestDelegate { 192 | func requestDidComplete(_ request: GCKRequest) { 193 | channel.invokeMethod("chromeCast#requestDidComplete", arguments: nil) 194 | } 195 | 196 | func request(_ request: GCKRequest, didFailWithError error: GCKError) { 197 | channel.invokeMethod("chromeCast#requestDidFail", arguments: ["error" : error.localizedDescription]) 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /ios/Classes/ChromeCastFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChromeCastFactory.swift 3 | // flutter_video_cast 4 | // 5 | // Created by Alessio Valentini on 07/08/2020. 6 | // 7 | 8 | import Flutter 9 | 10 | public class ChromeCastFactory: NSObject, FlutterPlatformViewFactory { 11 | let registrar: FlutterPluginRegistrar 12 | 13 | init(registrar: FlutterPluginRegistrar) { 14 | self.registrar = registrar 15 | } 16 | 17 | public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { 18 | return ChromeCastController(withFrame: frame, viewIdentifier: viewId, arguments: args, registrar: registrar) 19 | } 20 | 21 | public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { 22 | return FlutterStandardMessageCodec.sharedInstance() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ios/Classes/FlutterVideoCastPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface FlutterVideoCastPlugin : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /ios/Classes/FlutterVideoCastPlugin.m: -------------------------------------------------------------------------------- 1 | #import "FlutterVideoCastPlugin.h" 2 | #if __has_include() 3 | #import 4 | #else 5 | // Support project import fallback if the generated compatibility header 6 | // is not copied when this plugin is created as a library. 7 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 8 | #import "flutter_video_cast-Swift.h" 9 | #endif 10 | 11 | @implementation FlutterVideoCastPlugin 12 | + (void)registerWithRegistrar:(NSObject*)registrar { 13 | [SwiftFlutterVideoCastPlugin registerWithRegistrar:registrar]; 14 | } 15 | @end 16 | -------------------------------------------------------------------------------- /ios/Classes/SwiftFlutterVideoCastPlugin.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | public class SwiftFlutterVideoCastPlugin: NSObject, FlutterPlugin { 5 | public static func register(with registrar: FlutterPluginRegistrar) { 6 | let factory = AirPlayFactory(registrar: registrar) 7 | let chromeCastFactory = ChromeCastFactory(registrar: registrar) 8 | registrar.register(factory, withId: "AirPlayButton") 9 | registrar.register(chromeCastFactory, withId: "ChromeCastButton") 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ios/flutter_video_cast.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint flutter_video_cast.podspec' to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'flutter_video_cast' 7 | s.version = '0.0.1' 8 | s.summary = 'A new flutter plugin project.' 9 | s.description = <<-DESC 10 | A new flutter plugin project. 11 | DESC 12 | s.homepage = 'http://example.com' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'Your Company' => 'email@example.com' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.dependency 'Flutter' 18 | s.dependency 'google-cast-sdk-no-bluetooth' 19 | s.platform = :ios, '11.0' 20 | s.static_framework = true 21 | 22 | # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. 23 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } 24 | s.swift_version = '5.0' 25 | end 26 | -------------------------------------------------------------------------------- /lib/flutter_video_cast.dart: -------------------------------------------------------------------------------- 1 | library flutter_video_cast; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | import 'package:flutter_video_cast/src/chrome_cast/chrome_cast_platform.dart'; 7 | import 'package:flutter_video_cast/src/air_play/air_play_platform.dart'; 8 | 9 | part 'src/chrome_cast/chrome_cast_controller.dart'; 10 | part 'src/chrome_cast/chrome_cast_button.dart'; 11 | part 'src/air_play/air_play_button.dart'; 12 | -------------------------------------------------------------------------------- /lib/src/air_play/air_play_button.dart: -------------------------------------------------------------------------------- 1 | part of flutter_video_cast; 2 | 3 | final AirPlayPlatform _airPlayPlatform = AirPlayPlatform.instance; 4 | 5 | /// Widget that displays the AirPlay button. 6 | class AirPlayButton extends StatelessWidget { 7 | /// Creates a widget displaying a AirPlay button. 8 | AirPlayButton({ 9 | Key key, 10 | this.size = 30.0, 11 | this.color = Colors.black, 12 | this.activeColor = Colors.white, 13 | this.onRoutesOpening, 14 | this.onRoutesClosed, 15 | }) : super(key: key); 16 | 17 | /// The size of the button. 18 | final double size; 19 | 20 | /// The color of the button. 21 | final Color color; 22 | 23 | /// The color of the button when connected. 24 | final Color activeColor; 25 | 26 | /// Called while the AirPlay popup is opening. 27 | final VoidCallback onRoutesOpening; 28 | 29 | /// Called when the AirPlay popup has closed. 30 | final VoidCallback onRoutesClosed; 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | final Map args = { 35 | 'red': color.red, 36 | 'green': color.green, 37 | 'blue': color.blue, 38 | 'alpha': color.alpha, 39 | 'activeRed': activeColor.red, 40 | 'activeGreen': activeColor.green, 41 | 'activeBlue': activeColor.blue, 42 | 'activeAlpha': activeColor.alpha, 43 | }; 44 | if (defaultTargetPlatform == TargetPlatform.iOS) { 45 | return SizedBox( 46 | width: size, 47 | height: size, 48 | child: _airPlayPlatform.buildView(args, _onPlatformViewCreated), 49 | ); 50 | } 51 | return SizedBox(); 52 | } 53 | 54 | Future _onPlatformViewCreated(int id) async { 55 | await _airPlayPlatform.init(id); 56 | if (onRoutesOpening != null) { 57 | _airPlayPlatform.onRoutesOpening(id: id).listen((_) => onRoutesOpening()); 58 | } 59 | if (onRoutesClosed != null) { 60 | _airPlayPlatform 61 | .onRoutesClosed(id: id) 62 | .listen((event) => onRoutesClosed()); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/src/air_play/air_play_event.dart: -------------------------------------------------------------------------------- 1 | /// Generic Event coming from the native side. 2 | /// 3 | /// All AirPlayEvents contain the `id` that originated the event. This should 4 | /// never be `null`. 5 | class AirPlayEvent { 6 | /// The ID of the button this event is associated to. 7 | final int id; 8 | 9 | /// Build a AirPlay Event, that relates a id with a given value. 10 | /// 11 | /// The `id` is the id of the button that triggered the event. 12 | AirPlayEvent(this.id); 13 | } 14 | 15 | /// An event fired while the AirPlay popup is opening. 16 | class RoutesOpeningEvent extends AirPlayEvent { 17 | /// Build a RoutesOpening Event triggered from the button represented by `id`. 18 | RoutesOpeningEvent(int id) : super(id); 19 | } 20 | 21 | /// An event fired when the AirPlay popup has closed. 22 | class RoutesClosedEvent extends AirPlayEvent { 23 | /// Build a RoutesClosed Event triggered from the button represented by `id`. 24 | RoutesClosedEvent(int id) : super(id); 25 | } 26 | -------------------------------------------------------------------------------- /lib/src/air_play/air_play_platform.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_video_cast/src/air_play/air_play_event.dart'; 4 | import 'package:flutter_video_cast/src/air_play/method_channel_air_play.dart'; 5 | 6 | /// The interface that platform-specific implementations of `flutter_video_cast` must extend. 7 | abstract class AirPlayPlatform { 8 | static AirPlayPlatform _instance = MethodChannelAirPlay(); 9 | 10 | /// The default instance of [AirPlayPlatform] to use. 11 | /// 12 | /// Defaults to [MethodChannelAirPlay]. 13 | static get instance => _instance; 14 | 15 | /// Initializes the platform interface with [id]. 16 | /// 17 | /// This method is called when the plugin is first initialized. 18 | Future init(int id) { 19 | throw UnimplementedError('init() has not been implemented.'); 20 | } 21 | 22 | /// The route is opening. 23 | Stream onRoutesOpening({@required int id}) { 24 | throw UnimplementedError('onRoutesOpening() has not been implemented.'); 25 | } 26 | 27 | /// The route has closed. 28 | Stream onRoutesClosed({@required int id}) { 29 | throw UnimplementedError('onRoutesClosed() has not been implemented.'); 30 | } 31 | 32 | /// Returns a widget displaying the button. 33 | Widget buildView(Map arguments, 34 | PlatformViewCreatedCallback onPlatformViewCreated) { 35 | throw UnimplementedError('buildView() has not been implemented.'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/air_play/method_channel_air_play.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:flutter_video_cast/src/air_play/air_play_event.dart'; 7 | import 'package:flutter_video_cast/src/air_play/air_play_platform.dart'; 8 | import 'package:stream_transform/stream_transform.dart'; 9 | 10 | /// An implementation of [AirPlayPlatform] that uses [MethodChannel] to communicate with the native code. 11 | class MethodChannelAirPlay extends AirPlayPlatform { 12 | // Keep a collection of id -> channel 13 | // Every method call passes the int id 14 | final Map _channels = {}; 15 | 16 | /// Accesses the MethodChannel associated to the passed id. 17 | MethodChannel channel(int id) { 18 | return _channels[id]; 19 | } 20 | 21 | // The controller we need to broadcast the different events coming 22 | // from handleMethodCall. 23 | // 24 | // It is a `broadcast` because multiple controllers will connect to 25 | // different stream views of this Controller. 26 | final _eventStreamController = StreamController.broadcast(); 27 | 28 | // Returns a filtered view of the events in the _controller, by id. 29 | Stream _events(int id) => 30 | _eventStreamController.stream.where((event) => event.id == id); 31 | 32 | @override 33 | Future init(int id) { 34 | MethodChannel channel; 35 | if (!_channels.containsKey(id)) { 36 | channel = MethodChannel('flutter_video_cast/airPlay_$id'); 37 | channel.setMethodCallHandler((call) => _handleMethodCall(call, id)); 38 | _channels[id] = channel; 39 | } 40 | return channel.invokeMethod('airPlay#wait'); 41 | } 42 | 43 | @override 44 | Stream onRoutesOpening({@required int id}) { 45 | return _events(id).whereType(); 46 | } 47 | 48 | @override 49 | Stream onRoutesClosed({@required int id}) { 50 | return _events(id).whereType(); 51 | } 52 | 53 | Future _handleMethodCall(MethodCall call, int id) async { 54 | switch (call.method) { 55 | case 'airPlay#onRoutesOpening': 56 | _eventStreamController.add(RoutesOpeningEvent(id)); 57 | break; 58 | case 'airPlay#onRoutesClosed': 59 | _eventStreamController.add(RoutesClosedEvent(id)); 60 | break; 61 | default: 62 | throw MissingPluginException(); 63 | } 64 | } 65 | 66 | @override 67 | Widget buildView(Map arguments, 68 | PlatformViewCreatedCallback onPlatformViewCreated) { 69 | if (defaultTargetPlatform == TargetPlatform.iOS) { 70 | return UiKitView( 71 | viewType: 'AirPlayButton', 72 | onPlatformViewCreated: onPlatformViewCreated, 73 | creationParams: arguments, 74 | creationParamsCodec: const StandardMessageCodec(), 75 | ); 76 | } 77 | return SizedBox(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/src/chrome_cast/chrome_cast_button.dart: -------------------------------------------------------------------------------- 1 | part of flutter_video_cast; 2 | 3 | /// Callback method for when the button is ready to be used. 4 | /// 5 | /// Pass to [ChromeCastButton.onButtonCreated] to receive a [ChromeCastController] 6 | /// when the button is created. 7 | typedef void OnButtonCreated(ChromeCastController controller); 8 | 9 | /// Callback method for when a request has failed. 10 | typedef void OnRequestFailed(String error); 11 | 12 | /// Widget that displays the ChromeCast button. 13 | class ChromeCastButton extends StatelessWidget { 14 | /// Creates a widget displaying a ChromeCast button. 15 | ChromeCastButton( 16 | {Key key, 17 | this.size = 30.0, 18 | this.color = Colors.black, 19 | this.onButtonCreated, 20 | this.onSessionStarted, 21 | this.onSessionEnded, 22 | this.onRequestCompleted, 23 | this.onRequestFailed}) 24 | : assert( 25 | defaultTargetPlatform == TargetPlatform.iOS || 26 | defaultTargetPlatform == TargetPlatform.android, 27 | '$defaultTargetPlatform is not supported by this plugin'), 28 | super(key: key); 29 | 30 | /// The size of the button. 31 | final double size; 32 | 33 | /// The color of the button. 34 | /// This is only supported on iOS at the moment. 35 | final Color color; 36 | 37 | /// Callback method for when the button is ready to be used. 38 | /// 39 | /// Used to receive a [ChromeCastController] for this [ChromeCastButton]. 40 | final OnButtonCreated onButtonCreated; 41 | 42 | /// Called when a cast session has started. 43 | final VoidCallback onSessionStarted; 44 | 45 | /// Called when a cast session has ended. 46 | final VoidCallback onSessionEnded; 47 | 48 | /// Called when a cast request has successfully completed. 49 | final VoidCallback onRequestCompleted; 50 | 51 | /// Called when a cast request has failed. 52 | final OnRequestFailed onRequestFailed; 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | final Map args = { 57 | 'red': color.red, 58 | 'green': color.green, 59 | 'blue': color.blue, 60 | 'alpha': color.alpha 61 | }; 62 | return SizedBox( 63 | width: size, 64 | height: size, 65 | child: _chromeCastPlatform.buildView(args, _onPlatformViewCreated), 66 | ); 67 | } 68 | 69 | Future _onPlatformViewCreated(int id) async { 70 | final ChromeCastController controller = await ChromeCastController.init(id); 71 | if (onButtonCreated != null) { 72 | onButtonCreated(controller); 73 | } 74 | if (onSessionStarted != null) { 75 | _chromeCastPlatform 76 | .onSessionStarted(id: id) 77 | .listen((_) => onSessionStarted()); 78 | } 79 | if (onSessionEnded != null) { 80 | _chromeCastPlatform 81 | .onSessionEnded(id: id) 82 | .listen((_) => onSessionEnded()); 83 | } 84 | if (onRequestCompleted != null) { 85 | _chromeCastPlatform 86 | .onRequestCompleted(id: id) 87 | .listen((_) => onRequestCompleted()); 88 | } 89 | if (onRequestFailed != null) { 90 | _chromeCastPlatform 91 | .onRequestFailed(id: id) 92 | .listen((event) => onRequestFailed(event.error)); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /lib/src/chrome_cast/chrome_cast_controller.dart: -------------------------------------------------------------------------------- 1 | part of flutter_video_cast; 2 | 3 | final ChromeCastPlatform _chromeCastPlatform = ChromeCastPlatform.instance; 4 | 5 | /// Controller for a single ChromeCastButton instance running on the host platform. 6 | class ChromeCastController { 7 | /// The id for this controller 8 | final int id; 9 | 10 | ChromeCastController._({@required this.id}); 11 | 12 | /// Initialize control of a [ChromeCastButton] with [id]. 13 | static Future init(int id) async { 14 | assert(id != null); 15 | await _chromeCastPlatform.init(id); 16 | return ChromeCastController._(id: id); 17 | } 18 | 19 | /// Add listener for receive callbacks. 20 | Future addSessionListener() { 21 | return _chromeCastPlatform.addSessionListener(id: id); 22 | } 23 | 24 | /// Remove listener for receive callbacks. 25 | Future removeSessionListener() { 26 | return _chromeCastPlatform.removeSessionListener(id: id); 27 | } 28 | 29 | /// Load a new media by providing an [url]. 30 | Future loadMedia(String url) { 31 | return _chromeCastPlatform.loadMedia(url, id: id); 32 | } 33 | 34 | /// Plays the video playback. 35 | Future play() { 36 | return _chromeCastPlatform.play(id: id); 37 | } 38 | 39 | /// Pauses the video playback. 40 | Future pause() { 41 | return _chromeCastPlatform.pause(id: id); 42 | } 43 | 44 | /// If [relative] is set to false sets the video position to an [interval] from the start. 45 | /// 46 | /// If [relative] is set to true sets the video position to an [interval] from the current position. 47 | Future seek({bool relative = false, double interval = 10.0}) { 48 | return _chromeCastPlatform.seek(relative, interval, id: id); 49 | } 50 | 51 | /// Stop the current video. 52 | Future stop() { 53 | return _chromeCastPlatform.stop(id: id); 54 | } 55 | 56 | /// Returns `true` when a cast session is connected, `false` otherwise. 57 | Future isConnected() { 58 | return _chromeCastPlatform.isConnected(id: id); 59 | } 60 | 61 | /// Returns `true` when a cast session is playing, `false` otherwise. 62 | Future isPlaying() { 63 | return _chromeCastPlatform.isPlaying(id: id); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/src/chrome_cast/chrome_cast_event.dart: -------------------------------------------------------------------------------- 1 | /// Generic Event coming from the native side. 2 | /// 3 | /// All ChromeCastEvents contain the `id` that originated the event. This should 4 | /// never be `null`. 5 | class ChromeCastEvent { 6 | /// The ID of the button this event is associated to. 7 | final int id; 8 | 9 | /// Build a ChromeCast Event, that relates a id with a given value. 10 | /// 11 | /// The `id` is the id of the button that triggered the event. 12 | ChromeCastEvent(this.id); 13 | } 14 | 15 | /// An event fired when a session of a [id] started. 16 | class SessionStartedEvent extends ChromeCastEvent { 17 | /// Build a SessionStarted Event triggered from the button represented by `id`. 18 | SessionStartedEvent(int id) : super(id); 19 | } 20 | 21 | /// An event fired when a session of a [id] ended. 22 | class SessionEndedEvent extends ChromeCastEvent { 23 | /// Build a SessionEnded Event triggered from the button represented by `id`. 24 | SessionEndedEvent(int id) : super(id); 25 | } 26 | 27 | /// An event fired when a request of a [id] completed. 28 | class RequestDidCompleteEvent extends ChromeCastEvent { 29 | /// Build a RequestDidComplete Event triggered from the button represented by `id`. 30 | RequestDidCompleteEvent(int id) : super(id); 31 | } 32 | 33 | /// An event fired when a request of a [id] failed. 34 | class RequestDidFailEvent extends ChromeCastEvent { 35 | /// The error message. 36 | final String error; 37 | 38 | /// Build a RequestDidFail Event triggered from the button represented by `id`. 39 | RequestDidFailEvent(int id, this.error) : super(id); 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/chrome_cast/chrome_cast_platform.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_video_cast/src/chrome_cast/chrome_cast_event.dart'; 4 | import 'package:flutter_video_cast/src/chrome_cast/method_channel_chrome_cast.dart'; 5 | 6 | /// The interface that platform-specific implementations of `flutter_video_cast` must extend. 7 | abstract class ChromeCastPlatform { 8 | static ChromeCastPlatform _instance = MethodChannelChromeCast(); 9 | 10 | /// The default instance of [ChromeCastPlatform] to use. 11 | /// 12 | /// Defaults to [MethodChannelChromeCast]. 13 | static get instance => _instance; 14 | 15 | /// Initializes the platform interface with [id]. 16 | /// 17 | /// This method is called when the plugin is first initialized. 18 | Future init(int id) { 19 | throw UnimplementedError('init() has not been implemented.'); 20 | } 21 | 22 | /// Add listener for receive callbacks. 23 | Future addSessionListener({@required int id}) { 24 | throw UnimplementedError('addSessionListener() has not been implemented.'); 25 | } 26 | 27 | /// Remove listener for receive callbacks. 28 | Future removeSessionListener({@required int id}) { 29 | throw UnimplementedError( 30 | 'removeSessionListener() has not been implemented.'); 31 | } 32 | 33 | /// A session is started. 34 | Stream onSessionStarted({@required int id}) { 35 | throw UnimplementedError('onSessionStarted() has not been implemented.'); 36 | } 37 | 38 | /// A session is ended. 39 | Stream onSessionEnded({@required int id}) { 40 | throw UnimplementedError('onSessionEnded() has not been implemented.'); 41 | } 42 | 43 | /// A request has completed. 44 | Stream onRequestCompleted({@required int id}) { 45 | throw UnimplementedError('onRequestCompleted() has not been implemented.'); 46 | } 47 | 48 | /// A request has failed. 49 | Stream onRequestFailed({@required int id}) { 50 | throw UnimplementedError('onSessionEnded() has not been implemented.'); 51 | } 52 | 53 | /// Load a new media by providing an [url]. 54 | Future loadMedia( 55 | String url, { 56 | @required int id, 57 | }) { 58 | throw UnimplementedError('loadMedia() has not been implemented.'); 59 | } 60 | 61 | /// Plays the video playback. 62 | Future play({@required int id}) { 63 | throw UnimplementedError('play() has not been implemented.'); 64 | } 65 | 66 | /// Pauses the video playback. 67 | Future pause({@required int id}) { 68 | throw UnimplementedError('pause() has not been implemented.'); 69 | } 70 | 71 | /// If [relative] is set to false sets the video position to an [interval] from the start. 72 | /// 73 | /// If [relative] is set to true sets the video position to an [interval] from the current position. 74 | Future seek(bool relative, double interval, {@required int id}) { 75 | throw UnimplementedError('pause() has not been implemented.'); 76 | } 77 | 78 | /// Stop the current video. 79 | Future stop({@required int id}) { 80 | throw UnimplementedError('stop() has not been implemented.'); 81 | } 82 | 83 | /// Returns `true` when a cast session is connected, `false` otherwise. 84 | Future isConnected({@required int id}) { 85 | throw UnimplementedError('seek() has not been implemented.'); 86 | } 87 | 88 | /// Returns `true` when a cast session is playing, `false` otherwise. 89 | Future isPlaying({@required int id}) { 90 | throw UnimplementedError('isPlaying() has not been implemented.'); 91 | } 92 | 93 | /// Returns a widget displaying the button. 94 | Widget buildView(Map arguments, 95 | PlatformViewCreatedCallback onPlatformViewCreated) { 96 | throw UnimplementedError('buildView() has not been implemented.'); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/src/chrome_cast/method_channel_chrome_cast.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:flutter_video_cast/src/chrome_cast/chrome_cast_event.dart'; 7 | import 'package:flutter_video_cast/src/chrome_cast/chrome_cast_platform.dart'; 8 | import 'package:stream_transform/stream_transform.dart'; 9 | 10 | /// An implementation of [ChromeCastPlatform] that uses [MethodChannel] to communicate with the native code. 11 | class MethodChannelChromeCast extends ChromeCastPlatform { 12 | // Keep a collection of id -> channel 13 | // Every method call passes the int id 14 | final Map _channels = {}; 15 | 16 | /// Accesses the MethodChannel associated to the passed id. 17 | MethodChannel channel(int id) { 18 | return _channels[id]; 19 | } 20 | 21 | // The controller we need to broadcast the different events coming 22 | // from handleMethodCall. 23 | // 24 | // It is a `broadcast` because multiple controllers will connect to 25 | // different stream views of this Controller. 26 | final _eventStreamController = StreamController.broadcast(); 27 | 28 | // Returns a filtered view of the events in the _controller, by id. 29 | Stream _events(int id) => 30 | _eventStreamController.stream.where((event) => event.id == id); 31 | 32 | @override 33 | Future init(int id) { 34 | MethodChannel channel; 35 | if (!_channels.containsKey(id)) { 36 | channel = MethodChannel('flutter_video_cast/chromeCast_$id'); 37 | channel.setMethodCallHandler((call) => _handleMethodCall(call, id)); 38 | _channels[id] = channel; 39 | } 40 | return channel.invokeMethod('chromeCast#wait'); 41 | } 42 | 43 | @override 44 | Future addSessionListener({int id}) { 45 | return channel(id).invokeMethod('chromeCast#addSessionListener'); 46 | } 47 | 48 | @override 49 | Future removeSessionListener({int id}) { 50 | return channel(id).invokeMethod('chromeCast#removeSessionListener'); 51 | } 52 | 53 | @override 54 | Stream onSessionStarted({int id}) { 55 | return _events(id).whereType(); 56 | } 57 | 58 | @override 59 | Stream onSessionEnded({int id}) { 60 | return _events(id).whereType(); 61 | } 62 | 63 | @override 64 | Stream onRequestCompleted({int id}) { 65 | return _events(id).whereType(); 66 | } 67 | 68 | @override 69 | Stream onRequestFailed({int id}) { 70 | return _events(id).whereType(); 71 | } 72 | 73 | @override 74 | Future loadMedia(String url, {@required int id}) { 75 | final Map args = {'url': url}; 76 | return channel(id).invokeMethod('chromeCast#loadMedia', args); 77 | } 78 | 79 | @override 80 | Future play({@required int id}) { 81 | return channel(id).invokeMethod('chromeCast#play'); 82 | } 83 | 84 | @override 85 | Future pause({@required int id}) { 86 | return channel(id).invokeMethod('chromeCast#pause'); 87 | } 88 | 89 | @override 90 | Future seek(bool relative, double interval, {@required int id}) { 91 | final Map args = { 92 | 'relative': relative, 93 | 'interval': interval 94 | }; 95 | return channel(id).invokeMethod('chromeCast#seek', args); 96 | } 97 | 98 | @override 99 | Future stop({int id}) { 100 | return channel(id).invokeMethod('chromeCast#stop'); 101 | } 102 | 103 | @override 104 | Future isConnected({@required int id}) { 105 | return channel(id).invokeMethod('chromeCast#isConnected'); 106 | } 107 | 108 | @override 109 | Future isPlaying({@required int id}) { 110 | return channel(id).invokeMethod('chromeCast#isPlaying'); 111 | } 112 | 113 | Future _handleMethodCall(MethodCall call, int id) async { 114 | switch (call.method) { 115 | case 'chromeCast#didStartSession': 116 | _eventStreamController.add(SessionStartedEvent(id)); 117 | break; 118 | case 'chromeCast#didEndSession': 119 | _eventStreamController.add(SessionEndedEvent(id)); 120 | break; 121 | case 'chromeCast#requestDidComplete': 122 | _eventStreamController.add(RequestDidCompleteEvent(id)); 123 | break; 124 | case 'chromeCast#requestDidFail': 125 | _eventStreamController 126 | .add(RequestDidFailEvent(id, call.arguments['error'])); 127 | break; 128 | default: 129 | throw MissingPluginException(); 130 | } 131 | } 132 | 133 | @override 134 | Widget buildView(Map arguments, 135 | PlatformViewCreatedCallback onPlatformViewCreated) { 136 | if (defaultTargetPlatform == TargetPlatform.android) { 137 | return AndroidView( 138 | viewType: 'ChromeCastButton', 139 | onPlatformViewCreated: onPlatformViewCreated, 140 | creationParams: arguments, 141 | creationParamsCodec: const StandardMessageCodec(), 142 | ); 143 | } 144 | if (defaultTargetPlatform == TargetPlatform.iOS) { 145 | return UiKitView( 146 | viewType: 'ChromeCastButton', 147 | onPlatformViewCreated: onPlatformViewCreated, 148 | creationParams: arguments, 149 | creationParamsCodec: const StandardMessageCodec(), 150 | ); 151 | } 152 | return Text('$defaultTargetPlatform is not supported by ChromeCast plugin'); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.4.2" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.0.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.0.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.1.3" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.0.1" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.14.13" 46 | fake_async: 47 | dependency: transitive 48 | description: 49 | name: fake_async 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.1.0" 53 | flutter: 54 | dependency: "direct main" 55 | description: flutter 56 | source: sdk 57 | version: "0.0.0" 58 | flutter_test: 59 | dependency: "direct dev" 60 | description: flutter 61 | source: sdk 62 | version: "0.0.0" 63 | matcher: 64 | dependency: transitive 65 | description: 66 | name: matcher 67 | url: "https://pub.dartlang.org" 68 | source: hosted 69 | version: "0.12.8" 70 | meta: 71 | dependency: transitive 72 | description: 73 | name: meta 74 | url: "https://pub.dartlang.org" 75 | source: hosted 76 | version: "1.1.8" 77 | path: 78 | dependency: transitive 79 | description: 80 | name: path 81 | url: "https://pub.dartlang.org" 82 | source: hosted 83 | version: "1.7.0" 84 | sky_engine: 85 | dependency: transitive 86 | description: flutter 87 | source: sdk 88 | version: "0.0.99" 89 | source_span: 90 | dependency: transitive 91 | description: 92 | name: source_span 93 | url: "https://pub.dartlang.org" 94 | source: hosted 95 | version: "1.7.0" 96 | stack_trace: 97 | dependency: transitive 98 | description: 99 | name: stack_trace 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "1.9.5" 103 | stream_channel: 104 | dependency: transitive 105 | description: 106 | name: stream_channel 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "2.0.0" 110 | stream_transform: 111 | dependency: "direct main" 112 | description: 113 | name: stream_transform 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "1.2.0" 117 | string_scanner: 118 | dependency: transitive 119 | description: 120 | name: string_scanner 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "1.0.5" 124 | term_glyph: 125 | dependency: transitive 126 | description: 127 | name: term_glyph 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.1.0" 131 | test_api: 132 | dependency: transitive 133 | description: 134 | name: test_api 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "0.2.17" 138 | typed_data: 139 | dependency: transitive 140 | description: 141 | name: typed_data 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "1.2.0" 145 | vector_math: 146 | dependency: transitive 147 | description: 148 | name: vector_math 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "2.0.8" 152 | sdks: 153 | dart: ">=2.9.0-14.0.dev <3.0.0" 154 | flutter: ">=1.20.0 <2.0.0" 155 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_video_cast 2 | description: flutter_video_cast let you discover cast devices like Chrome Cast and Apple TV and connect to them. 3 | version: 1.0.3 4 | homepage: https://github.com/PalaTeam/flutter_video_cast 5 | 6 | environment: 7 | sdk: ">=2.7.0 <3.0.0" 8 | flutter: ">=1.20.0 <2.0.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | stream_transform: ^1.2.0 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | 20 | flutter: 21 | plugin: 22 | platforms: 23 | android: 24 | package: it.aesys.flutter_video_cast 25 | pluginClass: FlutterVideoCastPlugin 26 | ios: 27 | pluginClass: FlutterVideoCastPlugin 28 | -------------------------------------------------------------------------------- /test/flutter_video_cast_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | void main() { 5 | const MethodChannel channel = MethodChannel('flutter_video_cast'); 6 | 7 | TestWidgetsFlutterBinding.ensureInitialized(); 8 | 9 | setUp(() { 10 | channel.setMockMethodCallHandler((MethodCall methodCall) async { 11 | return '42'; 12 | }); 13 | }); 14 | 15 | tearDown(() { 16 | channel.setMockMethodCallHandler(null); 17 | }); 18 | 19 | test('getPlatformVersion', () async { 20 | 21 | }); 22 | } 23 | --------------------------------------------------------------------------------