Promisiunea Flutter e că scrii un singur cod și rulează pe iOS, Android, Web, macOS, Linux și Windows. Am livrat două aplicații pe baza acestei promisiuni — PokiPaw, o aplicație de îngrijire a animalelor de companie, live pe App Store și Google Play, și Admitere Drept, un simulator pentru examenul de admitere la Drept, tot live pe ambele magazine — iar varianta onestă e mai interesantă decât sloganul. „Șase platforme" e adevărat în sensul că proiectul compilează și rulează în șase locuri. Nu e adevărat în sensul că ai șase produse gata de livrat. Distanța dintre aceste două afirmații e tot subiectul acestui articol.
Ambele aplicații sunt în Dart și Flutter, ambele gestionează starea cu Provider, ambele monetizează prin in-app purchases native, și fac alegeri de backend opuse. Contrastul ăsta e util, așa că le folosesc ca pe cele două puncte de referință pe parcurs.
Ținte reale versus ținte nominale
pubspec-ul de la PokiPaw vizează toate cele șase platforme. Ce se livrează și se folosește de fapt e iOS și Android. Build-urile de desktop și web există, compilează, rulează — dar sunt mai degrabă „compilează și rulează" decât „șlefuit și susținut". Linux și Windows în special sunt suprafață pe care o țin în viață, nu produse pe care le-aș arăta unui străin. Admitere Drept e și mai explicit: iOS și Android se livrează, iar desktop și web sunt scheleturi care stau în arbore.
Distincția asta — livrat versus schelet — e cea pe care mi-aș fi dorit s-o fi trasat cineva mai devreme pentru mine. Flutter face lista nominală de ținte ieftină. A adăuga windows sau linux în matricea de build e un flag. A face una din ele bună e un proiect separat, cu propriul model de input, gestiunea ferestrelor, comportamentul file-picker-ului și magazinul propriu (sau lipsa unuia, în cazul Linux). Faptul că un cod e capabil de șase platforme e valoare reală de inginerie. Nu e același lucru cu a fi livrat pe șase.
Deci când spun că PokiPaw e „pe șase platforme", mă refer la cod. Produsul e pe două. Încerc să nu confund niciodată cele două lucruri, fiindcă exact confuzia asta transformă afirmațiile cross-platform în ficțiune de marketing.
Unde „scrii o dată" chiar ține
Vestea bună e că partea pe care se vinde Flutter e cu adevărat reală: logica de business, modelele de date, networking-ul, starea și grosul UI-ului se scriu o singură dată. La PokiPaw, hub-ul de sănătate — fișe medicale, calendar de vaccinări, urmărirea medicației doză cu doză, scoringul specific pe rasă — e același cod peste tot. La Admitere Drept, motorul de examen — 45 de întrebări, cronometrul, statisticile pe categorii, calculatorul de medie finală — e o singură implementare.
Regula mea aproximativă după aceste două aplicații: cam 85% din muncă e partajată, iar ultimii 15% sunt specifici pe platformă. Iar acei 15% nu sunt distribuiți uniform. Se aglomerează în jurul lucrurilor care ating sistemul de operare: notificări, achiziții, sign-in, blocare biometrică, file-pickere, deep link-uri. Cu cât o funcționalitate stă mai aproape de platformă, cu atât scapă mai multă logică specifică, și cu atât „scrii o dată" devine o ficțiune politicoasă pentru ultima felie de UI și integrare a fiecărei ținte.
Backend: Supabase la una, Firebase la cealaltă
Cele două aplicații se despart cel mai clar la backend, iar motivele sunt instructive fiindcă nu au fost arbitrare.
PokiPaw rulează pe Supabase cap-coadă — Postgres cu row-level security, Storage, Auth, Realtime și Edge Functions. Cerința care a dictat alegerea a fost partajarea în familie în timp real, cu drepturi de scriere complete: mai multe persoane dintr-o gospodărie editează datele aceluiași animal, fiecare rând fiind delimitat la familia corectă. Postgres plus RLS plus Realtime se potrivește curat. RLS lasă chiar baza de date să impună ca un membru să atingă doar rândurile propriei gospodării, exact genul de garanție pe care îl vreau în stratul de date, nu în cod de aplicație pe care l-aș putea greși.
Admitere Drept rulează pe Firebase — Auth, Firestore și Cloud Functions. Forma produsului e diferită. Nu e o aplicație colaborativă cu mai mulți autori; e un singur elev care exersează teste, cu o consolă de admin separată în React (construită cu Lovable) care face CRUD pe întrebări, import CSV și triaj de rapoarte pe același backend. Modelul de documente al Firestore și componentele „out of the box" Auth și Functions de la Firebase s-au potrivit unui workload citit intens, cu un singur utilizator per cont, iar ecosistemul de tooling de admin din jurul Firebase a făcut partea de back-office mai rapid de asamblat.
Lecția nu e „Supabase bun, Firebase bun". E că backend-ul ar trebui să urmeze forma socială a datelor. Scrieri colaborative, relaționale, delimitate pe rând au tras PokiPaw spre Postgres și RLS. Exersare cu un singur utilizator plus o consolă de admin lipită deasupra au tras Admitere Drept spre Firestore. Flutter pe front-end nu s-a sinchisit în niciun fel, ceea ce e propriul argument discret pentru framework: nu vine cu o opinie de backend după el.
Managementul stării: Provider la ambele
Ambele aplicații folosesc Provider. Nu Riverpod, nu Bloc, nu cel mai nou lucru. Provider e plictisitor, bine înțeles și suficient de bun pentru aplicații de mărimea asta, iar „plictisitor și suficient de bun" e o calitate când ești o singură persoană care întreține un portofoliu de produse. Există un cost real în a alege o soluție de stare diferită per aplicație: fiecare comutare între proiecte e o taxă. Standardizarea pe Provider înseamnă că modelul mental e același fie că sunt în aplicația de animale, fie în cea de examen, iar consistența asta valorează pentru mine mai mult decât să stoarcem o arhitectură marginal mai curată în una din ele.
Plumbing-ul de monetizare pe două magazine
Ambele aplicații fac bani prin magazine, iar aici cross-platform încetează să mai fie despre UI și începe să fie despre regulile fiecărei platforme.
PokiPaw e freemium: nucleu gratuit, Premium la 2,99 USD/lună, Family Seats de la 1,99 la 5,99 USD/lună, pe in-app purchases native. Admitere Drept e tot freemium dar amestecă modele — un nivel gratuit cu reclame prin google_mobile_ads (AdMob) și o deblocare Pro unică la 19,99 Lei prin in_app_purchase, care scoate reclamele și deblochează teste nelimitate și explicații complete.
Plumbing-ul de aici e mai greu decât pare. in_app_purchase îți dă un singur API în Dart, dar în spate stau două sisteme de facturare complet separate — App Store și Play Billing — fiecare cu propria configurare de produs, testare în sandbox, validare de chitanță și așteptări la review. AdMob adaugă o a doua integrare cu propriul consimțământ, mediere și ciudățenii de platformă. Nimic din astea nu e greu, propriu-zis; doar că „pune IAP" înseamnă de fapt „pune IAP de două ori, plus reclame, plus testează ambele sandbox-uri, plus tratează cazurile de restore-purchases". Flutter unifică punctul de apel. Nu unifică magazinele din spate.
Trimiterea în magazine ca dev solo
Ultima sută de metri e cea de care niciun framework nu te scapă. Două aplicații înseamnă două intrări App Store și două intrări Play — com.trinxsolutions.pokipaw și com.rdmsolutions.admiteredrept — fiecare cu propriile metadate, capturi de ecran, declarații de confidențialitate, clasificări de vârstă și ciclu de review. PokiPaw e la v1.2.6+26, Admitere Drept la v1.0.16+16, și fiecare dintre acele numere de build e un drum dus-întors prin două procese de review.
Șase platforme înrăutățește asta în principiu, ceea ce e motivul real pentru care țin desktop și web ca scheleturi. Fiecare țintă livrată e o taxă de mentenanță: încă un magazin (sau mecanism de distribuție), încă un set de update-uri de OS de urmărit, încă o suprafață care se strică la fiecare versiune majoră de Flutter. Ca dev solo, mișcarea rațională e să livrezi cele două platforme unde sunt utilizatorii, să le ții pe celelalte patru compilabile, și să nu pretinzi că scheleturile sunt produse.
Ce ți-aș spune înainte să începi
Flutter livrează partea care contează: un singur cod chiar duce logica și grosul UI-ului peste ținte, și rămâne neutru față de backend, ca să poți alege Supabase sau Firebase pe merit. Ce nu face e să transforme „șase platforme" în ceva gratuit. Fiecare țintă reală te costă muncă de magazin, integrare cu platforma și mentenanță continuă, iar jocul onest pentru majoritatea proiectelor solo e două platforme livrate, restul ca schelet.
Dacă vrei să vezi cum a ieșit asta în practică, articolul despre PokiPaw acoperă partea de Supabase în timp real și taxa de mentenanță a celor șase platforme, iar articolul despre Admitere Drept acoperă partea de Firebase, AdMob și consolă de admin a aceleiași alegeri de framework. Ambele fac parte din povestea mai mare a construirii a 11 produse ca fondator solo. Dacă cântărești Flutter pentru ceva ce trebuie să fie în câteva locuri deodată, pagina de proiecte arată unde și-a meritat locul.