Flutter dincolo de mobile — cum am construit un produs full-stack 100% Dart
Lecții din construirea Table Online: joc multiplayer real-time, panou admin și backend, toate scrise în Dart. De ce „Flutter = mobile" e o limitare pe care am depășit-o cu o echipă mică.
Pe 24 aprilie 2026 am ținut un talk la PeakIT 008 în Brașov despre un subiect care mă obsedează de câțiva ani: de ce limităm Flutter la aplicații mobile? Articolul ăsta e versiunea scrisă a aceleiași teze, plus câteva detalii care nu au încăput în cele 30 de minute pe scenă.
Context personal — 16 ani de mobile
Am început să scriu cod pentru iOS în 2010, înainte ca iPad-ul să aibă ecran retina. Am trecut prin Vienna (forfone, app de VoIP), apoi Düsseldorf (Secusmart/BlackBerry, mobile pentru securitate guvernamentală), apoi din 2021 am intrat în lumea Flutter la IDT Corporation, unde conduc migrarea aplicațiilor native către un stack Flutter. În paralel, din 2023 sunt CEO la TAGonSoft, unde dezvoltăm produse Flutter end-to-end — inclusiv table-online.ro, jocul de table multiplayer pe care l-am folosit drept studiu de caz în talk.
Spun asta nu ca să mă laud, ci ca să stabilesc un context: nu sunt cineva care a sărit pe Flutter pentru că nu poate face native. Am respect adânc pentru native; am scris cod care nu putea fi compromis. Tocmai asta face migrarea mea către Flutter mai relevantă.
Costul real al „native peste tot”
Native dă lucruri pe care le iubești: performanță buttery smooth, fidelitate de platformă, acces complet la API-uri OS. Dar costul devine vizibil pe măsură ce echipa crește:
- Două echipe, două codebases, două liste de bug-uri. Aceeași funcționalitate ajunge în iOS în 2 săptămâni și în Android în 4. Sau invers. Mereu există un decalaj.
- „Funcționează pe iOS” ≠ „Funcționează pe Android” — fiecare incident de producție e o investigație pe două platforme.
- Hiring: cauți doi specialiști în loc de unul. Pentru o echipă mică în România/Europa de Est, asta e literalmente diferența între „livrăm” și „nu livrăm”.
- Silos de cunoștințe — developerul iOS nu poate ajuta cu un bug în Android, și viceversa.
Industria a încercat să rezolve asta de mai multe ori. PhoneGap/Cordova (web ascuns într-un wrapper). Xamarin (.NET peste tot, apoi cumpărat și sunset). React Native (JS peste un bridge către native). Toate au avut limite arhitecturale.
Ce face Flutter diferit (și de ce contează acum)
În 2017 a apărut Flutter, cu o decizie radicală: nu folosește widget-urile native, ci își pictează propriii pixeli. Asta sună minor. Nu e. Înseamnă că Flutter nu e legat de nicio platformă — poate randa orice, oriunde există un canvas de pixeli.
Trei consecințe directe:
- Motor propriu de rendering (Skia, acum Impeller) — nu mai e limitat de widget-urile platformei, deci poate rula identic pe iOS, Android, web, desktop, și chiar pe ecrane embedded de mașini.
- Compilare la cod nativ ARM — nu interpretat, nu „bridged”. Performanța e reală.
- Un singur limbaj — Dart — pentru frontend, backend, jocuri, CLI, totul.
Aceste trei lucruri împreună deblochează cazuri de utilizare la care nimeni nu se aștepta când Flutter a fost lansat ca framework mobile.
Momentul „aha”
Undeva în 2022 m-am prins că gândesc prea mic. Dacă Flutter deține rendering-ul, și Dart compilează peste tot, de ce construim doar aplicații mobile cu el?
Așa am decis să testez ideea. Nu cu un proiect-jucărie, ci cu un produs real care să trebuiască să funcționeze sub presiune.
Studiul de caz — Table Online, 100% Dart
Table Online e un joc clasic de table multiplayer real-time. Sună simplu, dar cere: matchmaking, sistem de rating, chat, reguli validate server-side, panou admin pentru moderare, IAP. Echipa: TAGonSoft, mică. Constrângerea: livrăm pe iOS, Android și web, plus backend și admin.
Abordarea tradițională ar fi fost: Unity pentru joc, Node.js/Go pentru backend, React pentru admin, Swift/Kotlin pentru variantele native. Patru limbaje, patru pipeline-uri CI/CD, patru tipuri de developeri.
Noi am ales: Flutter + Flame pentru joc, Flutter Web pentru jucători web, Flutter Web pentru panoul admin, Serverpod pentru backend. Un singur limbaj — Dart — peste tot.
┌──────────────────────────────────────────────────────┐
│ DART / FLUTTER │
├─────────────────┬─────────────────┬──────────────────┤
│ 🎮 Joc │ 🌐 Web │ 📊 Admin │
│ Flutter + │ Flutter Web │ Flutter Web │
│ Flame Engine │ (jucători) │ (moderare) │
├─────────────────┴─────────────────┴──────────────────┤
│ 🔧 Serverpod (Dart) │
│ API · WebSockets · Auth · DB │
├──────────────────────────────────────────────────────┤
│ PostgreSQL · Redis │
└──────────────────────────────────────────────────────┘
Flame — game engine peste Flutter
Flame e motorul 2D de joc construit peste Flutter. Nu e o jucărie — e production-ready. Ce-l face special: rulează pe Flutter, deci poți suprapune widget-uri Flutter standard peste joc. Chat-ul în partidă? Widget Flutter normal. Meniul de pauză? Flutter normal. Hot reload-ul funcționează și în joc — developeri Unity ar plăti pentru asta.
class BackgammonBoard extends FlameGame
with HasDraggables, HasTappables {
@override
Future<void> onLoad() async {
final board = await Sprite.load('board.png');
add(SpriteComponent(sprite: board, size: size));
for (final piece in gameState.pieces) {
add(GamePiece(piece));
}
}
@override
Widget build(BuildContext context) {
return GameWidget(
game: this,
overlayBuilderMap: {
'chat': (ctx, game) => ChatOverlay(),
'menu': (ctx, game) => GameMenu(),
},
);
}
}
Un joc Flame e un Widget Flutter. Poți să-l încorporezi oriunde, poți să suprapui orice peste el. Dacă știi să faci aplicații Flutter, știi să faci jocuri 2D.
Serverpod — backend Dart cu type safety end-to-end
Serverpod e ce face stack-ul cu adevărat puternic. Definești modelele o dată, iar Serverpod generează automat clientul. Schimbi un câmp pe server — codul client nu mai compilează până nu-l actualizezi. Compilatorul devine integration testul tău.
// Endpoint pe server (Dart)
class GameEndpoint extends Endpoint {
Future<GameState> makeMove(
Session session, int gameId, Move move,
) async {
final game = await Game.db.findById(session, gameId);
if (!game!.isValidMove(move))
throw GameException('Invalid move');
game.applyMove(move);
await Game.db.updateRow(session, game);
// Notifică adversarul prin WebSocket
session.messages.postMessage(
'game_$gameId', GameMoveMessage(move: move));
return game.state;
}
}
// Apel de pe client (același Dart, aceleași tipuri)
final newState = await client.game.makeMove(gameId, myMove);
Bug-urile de serializare JSON dispar. Bug-urile „client trimite int, server așteaptă string” dispar. Echipa câștigă timp pe care altfel l-ar pierde în debugging cross-stack.
Ce a ieșit bine
- Un developer poate lucra pe full stack. Nu există „ești om de mobile” sau „ești om de backend”. Toți sunt oameni de Dart.
- Hot reload pentru dezvoltarea jocului — sub-secundă. În Unity ai aștepta 30+ secunde.
- ~40% cod partajat între cele patru produse. Validarea regulilor de table e scrisă o singură dată și e folosită identic pe client și server.
- Type safety prin tot stack-ul a prins bug-uri la compile-time pe care altfel le-am fi văzut în producție.
- Echipa mică a livrat pe trei platforme în luni, nu ani.
Ce a fost greu (versiunea onestă)
Nu vând Flutter ca panaceu. Au fost lucruri grele:
- Performanța Flutter Web — mai ales pe primul load. Există trade-off-uri reale între rendererele Canvas și HTML.
- Ecosistemul Flame e mai mic decât Unity sau Godot. Nu e o problemă pentru jocuri 2D, dar pentru AAA 3D folosește altceva.
- Serverpod e tânăr — mai puține răspunsuri pe Stack Overflow. Trebuie să fii confortabil să citești cod sursă.
- Unele pachete pub.dev nu suportă toate platformele. Verifică înainte să te bazezi pe ele.
- Convins alți developeri că Dart e un limbaj „real” de backend — bătălie culturală mai mult decât tehnică.
Niciuna n-a fost blocant. Sunt trade-off-uri. În inginerie, mereu ai trade-off-uri.
Implicațiile pentru echipa ta
Dacă ești developer: învață Dart adânc, nu doar Flutter. Skill-urile tale se extind peste mobile, jocuri, backend, CLI-uri.
Dacă ești tech lead: evaluează Flutter pentru următorul proiect greenfield. Începe cu un internal tool sau un dashboard. Măsoară viteza echipei.
Dacă ești CTO sau fondator: gândește ROI de platformă, nu doar ROI mobile. O echipă, un limbaj, time-to-market mai mic, cost total mai mic. În experiența noastră, reducerea echipei pentru același output e între 30% și 50% — nu un număr de marketing, ci ce am observat în practică.
Când Flutter e răspunsul (și când nu)
Da, dacă ai produs care se întinde pe mai multe platforme, echipă mică-medie care are nevoie de leverage, joc 2D / experiență interactivă, internal tools sau dashboards, startup care are nevoie de viteză.
Nu (încă), dacă ai site bazat pe SEO din conținut imens, joc 3D AAA, feature-uri native foarte specializate, sau o echipă investită adânc într-un alt stack.
Dacă vrei să vezi rezultatul direct, intră pe table-online.ro și joacă o partidă. Tot ce atingi — de la cum se rotesc zarurile la cum apare scor-ul după partida câștigată — a fost scris de noi în Dart, de la pixel la query SQL.
Vrei să discutăm un proiect Flutter end-to-end (mobile + web + backend)? Scrie-ne. Și dacă te uiți după înregistrarea talk-ului PeakIT, urmărește canalul oficial de pe YouTube.