11 KiB
Plugin plugins/google_cast – Come funziona
Questo documento descrive l’architettura e il flusso di funzionamento del plugin plugins/google_cast del progetto, sulla base della struttura riportata e dei file analizzati durante il debug.
1. Obiettivo del plugin
Il plugin google_cast fa da ponte tra Flutter/Dart e il layer nativo Android per gestire:
- Google Cast standard (Chromecast / Android TV con receiver standard)
- Custom Receiver (receiver web / CAF)
- il dialog nativo di selezione device
- comandi media (play, pause, seek, queue, volume, mute)
- eventi di ritorno verso Flutter (cast connesso, cast fermato, cambio item queue, errori, ecc.)
In pratica:
- Flutter decide quando connettersi e quale media mandare
- il plugin Android parla con le API Google Cast
- il receiver (standard o custom) riproduce davvero i contenuti sul TV/device Cast
2. Struttura del plugin
plugins/google_cast
|-- android
| |-- build.gradle.kts
| |-- settings.gradle.kts
| `-- src/main/kotlin/com/aves/google_cast
| |-- CastActivityProvider.kt
| |-- CastOptionsProvider.kt
| |-- FlutterCastFrameworkPlugin.kt
| `-- cast
| |-- CastContextManager.kt
| |-- CastCustomReceiver.kt
| |-- CastDialogLauncher.kt
| |-- CastMediaClient.kt
| |-- CastQueueManager.kt
| `-- CastSessionHandler.kt
|-- lib
| `-- google_cast.dart
`-- pubspec.yaml
3. Ruolo dei file principali
lib/google_cast.dart
È l’entry point Dart del package/plugin.
In molti plugin Flutter questo file fa una o più di queste cose:
- espone API pubbliche al codice Dart
- definisce wrapper leggeri verso il
MethodChannel - centralizza eventuali modelli / helper lato Flutter
Nel tuo progetto, il vero orchestratore lato app non è qui ma nei controller di app (cast_controller.dart, google_cast_controller.dart, custom_cast_controller.dart).
android/src/main/kotlin/com/aves/google_cast/FlutterCastFrameworkPlugin.kt
È il file principale del plugin Flutter lato Android.
Responsabilità
- registra il
MethodChannel('google_cast') - riceve le chiamate da Flutter (
showCastDialog,loadQueue,play,pause,seekTo,getVolume, ecc.) - smista ogni metodo verso i componenti modulari del package
cast/ - aggancia il plugin al ciclo di vita dell’
Activity - inizializza i componenti principali del Cast:
CastContextManagerCastSessionHandlerCastMediaClientCastQueueManagerCastDialogLauncherCastCustomReceiver
È il router centrale del plugin.
CastContextManager.kt
Gestisce il CastContext e l’accesso alla sessione corrente.
In pratica gestisce:
- l’
Activityattuale - l’inizializzazione di
CastContext - il recupero della sessione Cast attiva
- il recupero del
RemoteMediaClient
Metodi tipici/attesi
attachActivity(activity)detachActivity()ensureCastContext()getSession()getClient()
È il punto base da cui gli altri oggetti ottengono la sessione Cast corrente e il media client corrente.
CastSessionHandler.kt
Gestisce gli eventi di sessione Cast.
Di solito si occupa di:
onSessionStartingonSessionStartedonSessionResumedonSessionEnded- invio eventi verso Flutter, ad esempio:
onCastReadyonCastStopped
Nel tuo flusso attuale, onCastReady viene usato per segnalare una sessione connessa, ma non necessariamente un media già caricato.
CastDialogLauncher.kt
Si occupa di aprire il dialog nativo di scelta dispositivo Cast.
Tipicamente fa:
- crea/configura un
MediaRouteButton - invoca il bottone o mostra il picker Cast
- consente all’utente di scegliere il device
Quando Flutter chiama showCastDialog, il plugin passa il comando a CastDialogLauncher, che mostra il picker dispositivo.
CastMediaClient.kt
Gestisce i comandi media e il callback del RemoteMediaClient.
Responsabilità principali
play()pause()seek(position)getMediaStatus()getVolume()setVolume(v)setMute(m)- registrazione del callback sul
RemoteMediaClient
Perché è cruciale
Fa due cose fondamentali:
-
Comandi verso Google Cast
- play / pause
- seek
- volume / mute
-
Eventi da Google Cast verso Flutter
onQueueIndexChangedonVideoFinishedonCastError
È fondamentale per slideshow e sincronizzazione UI.
CastQueueManager.kt
Gestisce la queue dei media Cast.
Responsabilità tipiche
loadQueue(items, startIndex)next()prev()- costruzione di
MediaQueueItem - associazione metadati per item (titolo, mimeType, flags foto/video, customData)
È il file che determina come una lista Flutter di media diventa una vera queue Google Cast.
CastCustomReceiver.kt
Gestisce il Custom Receiver CAF / Web Receiver.
Responsabilità
- avvio del receiver custom con
receiverUrl - invio messaggi JSON al receiver web
- ricezione/gestione del namespace custom (se implementato)
Nel tuo progetto, Flutter costruisce un URL tipo:
https://.../cast_receiver/receiver.html?ws=...
e poi chiama startCustomReceiver.
CastActivityProvider.kt
Aiuta a dare accesso all’Activity giusta o a integrare il plugin con l’host Android.
CastOptionsProvider.kt
È il provider di configurazione Google Cast lato Android.
Tipicamente definisce:
- receiver application ID di default
- eventuali opzioni del framework Cast
4. Flusso end-to-end: Google Cast standard
A. Connessione del device
- Flutter preme l’icona Cast
- chiama
showCastDialog FlutterCastFrameworkPlugin.ktinoltra aCastDialogLauncher.kt- l’utente sceglie il device
CastSessionHandler.ktriceveonSessionStarted- il plugin manda a Flutter
onCastReady
B. Caricamento media
Quando l’utente preme:
- cast manuale
- fullscreen cast
- slideshow
Flutter costruisce una queue e chiama loadQueue.
Il plugin passa il tutto a CastQueueManager.loadQueue(...), che crea i MediaQueueItem e li carica nel RemoteMediaClient.
C. Controlli playback
Dai controller Flutter partono metodi come:
playCast()pauseCast()seekCast()nextManual()prevManual()setCastVolume()toggleMuteCast()
Questi passano dal MethodChannel, arrivano a FlutterCastFrameworkPlugin.kt e poi vengono gestiti da:
CastMediaClient.ktCastQueueManager.kt
D. Eventi di ritorno verso Flutter
Dal native tornano eventi come:
onCastReadyonCastStoppedonQueueIndexChangedonVideoFinishedonCastError
Questi aggiornano in Flutter:
- icona Cast
- indice corrente
- stato play/pause
- posizione/durata
- slideshow timer foto/video
5. Flusso end-to-end: Custom Receiver
A. Connessione/sessione
Flutter chiama:
showCastDialogstartCustomReceiver(appId, receiverUrl)
Il plugin passa a CastCustomReceiver.kt, che avvia il receiver web.
B. Caricamento media
Flutter non usa loadQueue standard, ma manda messaggi custom JSON al receiver, ad esempio:
LOAD_QUEUEPLAYPAUSESEEKNEXTPREVSET_VOLUMESET_MUTEGET_VOLUMEGET_MEDIA_STATUS
C. Eventi verso Flutter
Il receiver web risponde con messaggi come:
MEDIA_STATUSVOLUME_STATUSCAST_READYCAST_STOPPED
che il CustomCastController usa per aggiornare la UI Flutter.
6. Differenza tra Google Cast standard e Custom
Google Cast standard
- il volume è nativo via
CastSession - la queue è gestita dal framework Cast
- il passaggio video → next è naturale se la queue è corretta
RemoteMediaClient.Callbackè fondamentale
Custom Receiver
- il volume ha senso solo se il receiver web lo implementa davvero
- le foto/video vengono gestiti dalla tua logica JS/CAF
- gli eventi stato/volume dipendono dal receiver
- è più flessibile ma più facile da rompere
7. Punto delicato emerso nel debug
Durante il refactor si è visto che una distinzione è fondamentale:
Connettere il device != aver già caricato un media
Quindi il comportamento corretto è:
onCastReady= sessione pronta- NO polling immediato se la queue è ancora vuota
- il polling deve partire solo dopo
loadQueue
Altrimenti il plugin rischia di generare:
Invalid RequestMediaQueue errorIDLE_REASON_ERROR
8. Responsabilità lato Flutter (fuori dal plugin ma essenziali)
Anche se non sono dentro plugins/google_cast, lato app ci sono controller importanti:
cast_controller.dart→ controller unificato backendgoogle_cast_controller.dart→ orchestrazione Google standardcustom_cast_controller.dart→ orchestrazione custom receiver
Questi file decidono:
- quando aprire il dialog Cast
- quando connettere il device
- quando mandare davvero una queue
- quando mostrare controlli / slideshow / fullscreen viewer
Il plugin Android da solo non basta: è il layer Dart che decide il flusso UX.
9. Mappa rapida responsabilità
Flutter / UI
- pulsante Cast
- selection mode
- slideshow/manual/fullscreen
- bottom sheet controlli
cast_controller.dart
- facade unificata Google / Custom / DLNA
google_cast_controller.dart
- queue standard Cast
- slideshow foto/video lato Flutter
- polling media/volume
custom_cast_controller.dart
- invio messaggi JSON al receiver custom
- stato custom receiver
FlutterCastFrameworkPlugin.kt
- router MethodChannel lato Android
CastContextManager.kt
- CastContext + session + media client
CastSessionHandler.kt
- session lifecycle → eventi verso Flutter
CastQueueManager.kt
- queue standard Google Cast
CastMediaClient.kt
- play/pause/seek/volume + callback eventi media
CastCustomReceiver.kt
- custom receiver CAF/web
CastDialogLauncher.kt
- dialog selezione device
10. Se devi debuggare il plugin in futuro
Ordine consigliato per capire dove si rompe qualcosa:
Se non si connette il device
FlutterCastFrameworkPlugin.ktCastDialogLauncher.ktCastSessionHandler.ktCastContextManager.kt
Se si connette ma non parte il media
CastQueueManager.ktCastMediaClient.ktgoogle_cast_controller.dart
Se slideshow si ferma o salta male foto/video
google_cast_controller.dartCastMediaClient.kt(callbackonQueueIndexChanged)- eventualmente
CastQueueManager.kt
Se il Custom receiver ha problemi UI / progress / volume
CastCustomReceiver.kt- receiver web (
receiver.html, JS) custom_cast_controller.dart
Se il volume non si aggiorna
CastMediaClient.ktgoogle_cast_controller.dart- widget controlli Flutter (
cast_controls_sheet.dart, fullscreen viewer)
11. Conclusione
Il plugin google_cast del progetto è organizzato in modo modulare e separa bene le responsabilità:
- il file principale
FlutterCastFrameworkPlugin.ktriceve i comandi Flutter - il package
cast/contiene i moduli Android specializzati - i controller Flutter coordinano UX, queue e sincronizzazione stato
Il punto più delicato dell’architettura è la distinzione tra:
- sessione Cast connessa
- media realmente caricato sulla queue
Capire bene questa differenza evita gran parte dei bug visti in debug.