
Perché abbiamo buttato via la nostra app Flutter e ricostruito Cliqer Remote in nativo, su un core Rust

Il 26 settembre abbiamo iniziato a sviluppare Cliqer Remote, la nostra app per smartphone dedicata ai relatori, in Flutter. Cinque giorni dopo era su TestFlight e sul canale di test interno di Google Play, con tutte le funzioni dell'app web per relatori: il telecomando per presentazioni, lo schermo live, l'inchiostro, la chat, le note e le chiamate. Funzionava. Poi l'abbiamo misurata su telefoni veri, ne abbiamo visto uno raggiungere una temperatura superficiale di 42 °C solo per mostrare uno schermo condiviso, e l'abbiamo buttata via.
Nessuno al di fuori del nostro team ha mai visto quell'app. Al suo posto ricevi un'app nativa su ogni piattaforma: SwiftUI su iPhone, iPad e Apple Watch, Kotlin e Jetpack Compose su Android e Wear OS, e sotto un unico motore, scritto una sola volta in Rust. Questa è la storia del perché, con i numeri che abbiamo misurato lungo il percorso.
- Un telecomando per presentazioni ha un compito insolito per un'app per smartphone: restare in una mano calda per un'ora, mostrare il video live dello schermo e non perdere mai un tocco. Tempo di frame, calore e dimensioni contano più che per la maggior parte delle app.
- La nostra build Flutter era veloce da scrivere e bella da vedere, ma i suoi effetti vetro e il suo percorso video pesavano sulla GPU a ogni frame. Su un Galaxy S24+ abbiamo ridotto una schermata da 19,8 ms a 5,7 ms di lavoro GPU per frame, e continuavamo comunque a combattere contro il motore invece di costruire il prodotto.
- Poi abbiamo messo tre build sullo stesso telefono, nella stessa stanza, a guardare lo stesso schermo in movimento per dieci minuti ciascuna. La prima build Flutter disegnava 47,6 frame al secondo, scaldava il telefono di 5,9 °C e consumava 168 mAh. L'app nativa non disegnava nessun frame di interfaccia, lo scaldava di 0,2 °C e consumava 86 mAh: metà della batteria.
- Le app native sono più leggere: 10,8 MB per l'app iPhone con dentro la sua app per Apple Watch (quella Flutter pesava 27,6 MB) e un download Android di 5,8 MB (Flutter: 15,6 MB), misurati allo stesso modo.
- Nella stessa spinta abbiamo reso l'app desktop dieci volte più veloce nel trasformare un clic in un cambio di slide: 48 ms in PowerPoint e 42 ms in Keynote su Mac, dove prima ci voleva circa mezzo secondo.

Un telecomando è un'app strana da sviluppare
La maggior parte delle app si usa a brevi riprese. Un telecomando per presentazioni no. Resta in una mano per tutto l'intervento, spesso sotto le luci del palco, con lo schermo acceso e la radio occupata per tutto il tempo. Su Cliqer mostra anche un'immagine live dello schermo del relatore, disegna l'inchiostro sulle slide, gestisce chat e note e può mettere il relatore in video quando l'host lo invita.
Questo compito impone tre regole:
- Ogni tocco deve arrivare, subito. Un clic che arriva in ritardo è un relatore fermo in silenzio davanti al pubblico.
- Il telefono deve restare fresco. Un telefono caldo va in throttling, e un telefono in throttling perde frame, decodifica il video in ritardo e scarica la batteria più in fretta. Ed è anche sgradevole da tenere in mano per un'ora.
- L'app deve essere leggera e avviarsi in fretta. Le persone la installano sul posto, spesso con il Wi-Fi della conferenza, pochi minuti prima di salire sul palco.
Flutter riesce a fare tutte e tre le cose per la maggior parte delle app. La nostra si è rivelata il tipo di app in cui costa più di quanto dia.
Cosa Flutter ha fatto bene
Diamo a Flutter quel che è di Flutter. Ci ha portato da una cartella vuota a un'app completa su entrambi gli store in cinque giorni, con un'unica codebase, l'hot reload, un ottimo supporto ai test e un'interfaccia identica ovunque. Abbiamo scritto 266 test. Se stai sviluppando un'app di form e liste, una dashboard o un negozio online, te la consiglieremmo ancora senza pensarci due volte.
Ci ha anche insegnato molto. Quasi tutte le scoperte di questo articolo sono nate tracciando quella build Flutter su un telefono vero, e alcune delle correzioni ora vivono in ogni client Cliqer, web compreso.
Dove faceva male: il vetro costa uno schermo intero per frame
Lo stile di Cliqer è il vetro smerigliato: pannelli traslucidi che sfocano ciò che sta dietro. Su iPhone e iPad costava poco. Sul nostro telefono di test Android, un Galaxy S24+ con display a 120 Hz, no. A 120 Hz un frame ha 8,3 ms per essere disegnato, tutto compreso.
Abbiamo tracciato la GPU sul dispositivo e trovato la causa nel modo in cui il motore di rendering legge lo sfondo: ogni pannello di vetro copiava l'intero schermo, a ogni frame, con il multisampling in aggiunta. La schermata di accesso alla stanza aveva nove pannelli di vetro, quindi nove copie a schermo intero per frame. La GPU di quel telefono ha un'architettura di classe desktop che non è un tiler: non ha un modo economico per rileggere ciò che ha appena disegnato, e ogni nuova dimensione di texture significava una nuova allocazione di memoria che il kernel azzera prima. In una traccia, il 71% del tempo del thread di raster era speso ad allocare memoria grafica.

L'abbiamo risolto, schermata per schermata. Siamo passati a una versione pre-release del motore per poter limitare una sfocatura al suo pannello (da 41 ms a 17 ms). Abbiamo raggruppato i nove pannelli della schermata di accesso in un'unica lettura condivisa (da 19,8 ms a 5,7 ms). Abbiamo smesso di animare le dimensioni di qualsiasi superficie di vetro, perché ogni nuova dimensione era una nuova allocazione, e abbiamo simulato le animazioni con livelli a dimensione fissa. Il frame peggiore dello scanner QR è passato da 285 ms a 18 ms.
Ognuna di queste correzioni era un modo per aggirare il motore, non una funzione per un relatore. E con il vetro che occupava dall'80 al 90% del tempo GPU in una stanza, eravamo sempre a un nuovo pannello di distanza dal ricominciare a perdere frame.
Dove faceva davvero male: il video, e il calore che produce
Lo schermo condiviso è la cosa più pesante che l'app mostra. Nella build Flutter, ogni frame video decodificato veniva passato all'interfaccia come texture, il che significava che l'interfaccia si ridisegnava per ogni frame video: la stanza, il vetro, i controlli, tutto, 45 volte al secondo, solo per spostare i pixel dentro un rettangolo.
L'abbiamo misurato per la prima volta sul Galaxy con le tracce di sistema il 1° ottobre. Durante la riproduzione del video, la GPU restava intorno ai 440 MHz. Al cambio di slide, i frame richiedevano da 25 a 33 ms e la GPU toccava il suo limite di 800 MHz per oltre metà della transizione. Il telefono era nello stato termico «moderate» del sistema con una temperatura superficiale di 42 °C, e limitava da solo la propria GPU per reggere. Il calore faceva anche mentire le nostre misure: un telefono caldo risultava circa il 20% più lento di uno freddo, quindi ogni confronto doveva alternare le build alla stessa temperatura.
Poi abbiamo provato l'esperimento ovvio: mettere il video su un livello nativo che il sistema operativo compone da sé, fuori dall'interfaccia. Stesso telefono, stessa app, stessa stanza: l'interfaccia ha smesso di ridisegnarsi per il video, la frequenza della GPU è scesa da 440 a 121 MHz e il frame al cambio di slide è passato da 25-33 ms a 3-7 ms. Abbiamo rilasciato quella correzione nell'app Flutter il 1° ottobre. Ci ha anche mostrato dove stava la vera risposta: in un'app nativa il video vive sempre su un livello di sistema, su iPhone e su Android, quindi tutta questa classe di problemi scompare per costruzione e non grazie a un espediente.
Stesso telefono, stessa stanza, dieci minuti
Le affermazioni sul calore costano poco, quindi l'abbiamo misurato. Il 5 ottobre abbiamo messo tre build sullo stesso Galaxy S24+, una dopo l'altra, ciascuna per dieci minuti, scollegato dalla corrente, nella stessa stanza, a guardare lo stesso schermo condiviso in movimento. Prima di ogni prova il telefono si è raffreddato di nuovo.
- Flutter, prima build: l'app come l'avevamo costruita all'inizio, con il video disegnato dall'interfaccia.
- Flutter, ottimizzata: la stessa app dopo le correzioni a vetro e video descritte sopra.
- Nativa: il nuovo Cliqer Remote.

La prima build Flutter ridisegnava l'intera interfaccia 47,6 volte al secondo solo per mostrare un video, teneva la GPU a una media di 366 MHz e usava 1,47 secondi di tempo CPU ogni secondo. L'app nativa non ha disegnato un solo frame di interfaccia in dieci minuti: si muoveva solo il livello video. La sua GPU era in media a 129 MHz, la CPU a 0,69 secondi al secondo, e ha consumato 86 mAh di batteria contro 168 mAh. Metà della batteria per lo stesso lavoro.

E poi il calore. In dieci minuti la prima build Flutter ha portato la batteria da 36,2 °C a 42,1 °C, e il telefono ha iniziato a limitarsi da solo. La build Flutter ottimizzata è salita di 1,9 °C. L'app nativa di 0,2 °C. Un intervento dura più di dieci minuti, ed è questa la differenza che senti in mano.
Per essere onesti con la build Flutter ottimizzata: ci è andata vicino. Le nostre correzioni dentro Flutter hanno eliminato gran parte del costo, e il nativo l'ha battuta con un margine più piccolo, il 12% di CPU in meno e il 25% di batteria in meno. Il punto è da dove parte ciascuna. La prima build era una buona app Flutter così come esce di serie; il nativo ottiene un telefono fresco senza espedienti.
Come abbiamo misurato, così puoi ripeterlo: tracce Perfetto sul dispositivo (timeline dei frame, frequenza GPU, contatori della batteria), tempo CPU del processo dal kernel e la temperatura della batteria rilevata dal telefono stesso, con la stessa animazione a 60 fps sullo schermo condiviso in ogni prova. Un telefono e una prova per build, quindi considera i numeri come un test sul campo accurato, non un benchmark di laboratorio. La prova nativa è anche partita circa 2 °C più fresca, ed è per questo che confrontiamo l'aumento.
Cosa abbiamo costruito al suo posto
Il nuovo Cliqer Remote è nativo su ogni piattaforma, costruito su un unico motore condiviso.
L'interfaccia è nativa su ogni piattaforma. Su iPhone e iPad è SwiftUI con il Liquid Glass del sistema. Su Android è Jetpack Compose, con una vera sfocatura dello sfondo dove la piattaforma lo consente. Anche le app per Apple Watch e Wear OS sono native, abbastanza leggere da portarti Avanti e Indietro al polso.
Il motore è scritto una sola volta, in Rust. Accedere a una stanza, riconnettersi e riprendere dopo un cambio di rete, ricevere il video dello schermo, inviare clic, laser e inchiostro, chat, note, il timer, le chiamate e l'abbinamento di un telefono con un computer vivono tutti in un'unica libreria Rust condivisa da entrambe le app. Ogni app si limita a disegnare l'interfaccia, decodificare il video con il decoder hardware del sistema e comunicare con fotocamera e microfono. Una correzione al protocollo arriva una volta sola e viene distribuita su entrambe le piattaforme, e le due app non possono divergere.
Rust era la scelta naturale per quel motore: nessun garbage collector che blocca un frame, sicurezza della memoria senza runtime, e si compila in un'unica piccola libreria sia per iOS sia per Android. È veloce dove conta. Su un Mac, il nostro motore pacchettizza un frame H.264 da 20 KB in 31 µs, completa un handshake sicuro in 198 µs per lato e, nei nostri test, passa un frame video da 100 KB all'interfaccia Android in 46 µs (prima di ottimizzare quel confine servivano 440 µs). Tramite relay, un round trip ha richiesto una mediana da 13 a 16 ms nei nostri test.
Anche più leggera

Abbiamo misurato entrambe le build allo stesso modo. L'app iPhone, così come caricata su Apple, è passata da 27,6 MB a 10,8 MB, e questo con l'app per Apple Watch (1,5 MB) al suo interno. Il download per un telefono Android, misurato con il bundle tool di Google per lo stesso telefono a 64 bit, è passato da 15,6 MB a 5,8 MB. Circa un terzo delle vecchie dimensioni fa la differenza quando qualcuno installa l'app con il Wi-Fi della location a cinque minuti dall'inizio.
La caccia ai millisecondi non si è fermata al telefono
Un'app per telefono più veloce è solo metà di un clic. L'altra metà è il computer che esegue la presentazione, e mentre ricostruivamo le app ci siamo occupati anche di quel lato.
Clic: da mezzo secondo a un decimo
Quando un clic raggiunge l'app desktop di Cliqer, deve far avanzare la presentazione e poi comunicare a tutti nella stanza quale slide è visualizzata. Quella seconda parte chiedeva all'app di presentazione il numero della slide dopo ogni clic, e quella richiesta era lenta.

Cliqer 2.3 mantiene aperta un'unica connessione con PowerPoint o Keynote e legge la posizione direttamente dal passaggio appena eseguito. Su un Mac, su 20 prove, la mediana da clic a slide è scesa da 509 ms a 48 ms in PowerPoint e da 408 ms a 42 ms in Keynote. Dieci pressioni rapide, quelle che un relatore fa per saltare una sezione, richiedevano 5,3 secondi per assestarsi. Ora si fondono in un unico spostamento e arrivano a destinazione in 254 ms.
Video che tiene il passo anche con un Wi-Fi scadente
All'inizio di quest'anno abbiamo scoperto perché uno schermo condiviso poteva bloccarsi per secondi su Wi-Fi. Il video dello schermo in Cliqer viaggia su un canale dati affidabile, e lo standard su cui si basa quel canale attende almeno un secondo intero prima di reinviare un pacchetto perso, raddoppiando a ogni ripetizione. Chrome usa 400 ms. Ogni frame dietro quello perso aspettava in coda, poi l'host forzava un aggiornamento completo in quella stessa coda e, su una connessione lenta, lo faceva ancora e ancora.

Abbiamo allineato i timer di reinvio ai valori di Chrome e impedito agli aggiornamenti di accodarsi dietro l'arretrato. In una simulazione di rete con un modello di perdita Wi-Fi, una condivisione a 1080p è passata da 15-22 blocchi di un secondo o più ogni due minuti a 0-2, e il ritardo mediano rispetto al live da un massimo di 2,3 secondi a 12 ms. Abbiamo anche limitato gli aggiornamenti completi a uno per stream ogni mezzo secondo, perché ognuno di essi va a ogni spettatore: prima di quel limite, uno spettatore poteva ricevere 19 aggiornamenti completi al secondo, circa 40 Mbit/s.
Sul lato telefono, il decoder video hardware del Galaxy tratteneva un normale frame per circa 60 ms, all'incirca una volta al secondo. Dire al decoder di funzionare alla massima velocità ha portato a zero frame in ritardo in 50 secondi. Questa correzione è inclusa nell'app Android nativa.
Cosa diremmo a un altro team
Se sviluppi qualcosa in tempo reale su smartphone, queste lezioni ci sono costate giorni per impararle e richiedono minuti per applicarle:
- Misura sul tuo dispositivo reale peggiore, non sull'emulatore. Il nostro emulatore girava a 60 Hz su un backend grafico diverso e non poteva mostrare nemmeno uno dei problemi del vetro.
- Alterna le build alla stessa temperatura. Un telefono caldo risultava circa il 20% più lento. Confrontare una prova a freddo con una a caldo ti mentirà.
- Conta le letture per la sfocatura. Ogni sfocatura dello sfondo può costare una copia a schermo intero per frame. Raggruppale, delimitale e non animare mai le dimensioni di una superficie sfocata.
- Non ridisegnare mai l'interfaccia per il video. Metti il video su un livello composto dal sistema. È stato il singolo guadagno più grande che abbiamo misurato su GPU, batteria e calore.
- Chiedi al decoder hardware la velocità in tempo reale. Su Android, imposta l'operating rate del decoder. Una riga ha eliminato uno stallo di 60 ms ogni secondo.
- Controlla i timer del tuo trasporto. I canali dati affidabili ereditano i timer di reinvio da standard più vecchi. Un minimo di un secondo è un'eternità per il video live.
- Tratta gli aggiornamenti completi per quello che sono: frame costosi. Limitali per stream, perché ognuno va a ogni spettatore.
- Unisci le raffiche di input. Dieci pressioni devono essere un unico spostamento di dieci, non dieci round trip.
- Leggi ciò che i tuoi strumenti scrivono nella build. Una toolchain pre-release ha attivato un flag di rendering nel nostro manifest Android senza chiedere.
Cosa significa per te
10x
più veloce da clic a slide in PowerPoint e Keynote su Mac
5.8 MB
download Android, circa un terzo delle vecchie dimensioni
10.8 MB
app iPhone con dentro la sua app per Apple Watch
Half
della batteria della nostra prima build Flutter, stesso telefono, stessi 10 minuti
Cliqer Remote è nativo su iPhone, iPad, Android, Apple Watch e Wear OS. I clic sono più rapidi, il telefono resta fresco e consuma metà della batteria rispetto alla nostra prima build, il video viene disegnato nel modo in cui il telefono è progettato per farlo, e l'app pesa circa un terzo di prima. L'app web per relatori funziona ancora in qualsiasi browser senza alcuna installazione, così nessuno nella tua stanza resta mai escluso.
Latenza del telecomando per presentazioni: perché i millisecondi contano
Cosa succede davvero tra un tocco e il cambio di slide.
Domande frequenti
Cliqer Remote mostra video live, vetro smerigliato e un telecomando per presentazioni in un telefono che resta in mano a un relatore per un'ora. Nella build Flutter l'interfaccia si ridisegnava per ogni frame video e ogni pannello di vetro copiava l'intero schermo a ogni frame, con un costo in tempo GPU e calore su Android. Le app native disegnano il video su un livello di sistema per costruzione, e pesano circa un terzo nel download.
Nel nostro test su un Galaxy S24+, dieci minuti di video live dello schermo hanno scaldato il telefono di 0,2 °C con l'app nativa, contro 5,9 °C con la nostra prima build Flutter. L'app nativa non ha disegnato frame di interfaccia durante la riproduzione del video e ha consumato 86 mAh di batteria contro 168 mAh.
No. Flutter ci ha portato a un'app completa su entrambi gli store in cinque giorni e, dopo la nostra ottimizzazione, l'app nativa consumava solo il 25% di batteria in meno. Per la maggior parte delle app è una scelta eccellente. La nostra app combina video live continuo, molte superfici di vetro sfocate e sessioni lunghe su un telefono caldo, che è proprio il caso in cui il suo modello di rendering ci è costato di più.
Esegue tutto ciò che non è disegno: accedere e riconnettersi a una stanza, ricevere lo schermo condiviso, inviare clic, laser e inchiostro, chat, note, il timer, le chiamate e l'abbinamento di un telefono con un computer. Le app iPhone e Android lo condividono, quindi una correzione arriva su entrambe contemporaneamente.
Su un Mac con Cliqer 2.3, l'app desktop trasforma un clic in un cambio di slide con una mediana di 48 ms in PowerPoint e 42 ms in Keynote, misurata su 20 prove. La rete aggiunge il proprio round trip, che nei nostri test via relay ha avuto una mediana da 13 a 16 ms.
No. L'app web per relatori funziona in qualsiasi browser moderno senza installazione. Le app aggiungono uno scanner QR nativo, Face ID o la biometria per controllare l'host, uno schermo che resta acceso e i telecomandi per presentazioni su Apple Watch e Wear OS.
Perché abbiamo tolto il realtime di Cliqer da Cloudflare (e perché ne restiamo fan)
Quattro mesi sull'edge di Cloudflare: cosa si è rotto, cosa abbiamo costruito per farvi fronte e perché oggi Cliqer gira sui nostri server dedicati.
Controllare una presentazione dal telefono: tutte le app a confronto
PowerPoint, Keynote, Canva, Unified Remote, PPTControl, Cliqer e altre: quali app per il telefono funzionano via Internet, mostrano le note e fanno per te.
Gestisci il tuo prossimo evento con Cliqer
Continua a leggere
Altre novità sul prodotto per il tuo prossimo evento


Come abbiamo reso la condivisione dello schermo quattro volte più veloce

Cliqer vs Ecamm Live: quale studio live è adatto al tuo show?
Ricevi la prossima guida prima del tuo prossimo evento
Guide pratiche per relatori, tecnici AV e team di eventi, più le release di Cliqer che contano davvero. Niente spam, ti disiscrivi con un clic.