Mobilni razvoj
FlutterRazvoj mobilnih aplikacijaAnimacijePerformanseUI Engineering

Flutter animacije u produkciji: implicitne vs. eksplicitne, Rive i Lottie te savjeti za performanse

AO
Adrijan Omićević
·13 min čitanja

# Što ovaj vodič za Flutter animacije pokriva#

Animacija u produkciji nije “da se nešto miče”. Radi se o tome da je kretanje predvidljivo, prekidivo, testabilno i brzo na uređajima srednje klase.

Ovaj vodič daje praktičan alatni set za Flutter animacije: implicitne animacije za svakodnevno UI poliranje, eksplicitne animacije s AnimationController za složene tokove, Hero i shared-element prijelaze te jasna pravila odlučivanja za Rive i Lottie. Pokriva i stvarna ograničenja poput repaintova, raster opterećenja i zastajkivanja zbog kompilacije shadera, kao i strategije testiranja koje hvataju regresije prije korisnika.

Ako lovite konzistentnih 60fps i želite dublje obrasce profiliranja, pročitajte Flutter optimizacija performansi za 60fps. Za strukturu koda koja drži animacije održivima u velikim aplikacijama, uparite ovo s Clean Architecture feature-first u Flutteru i Flutter upravljanje stanjem u 2026.

# Produkcijska ograničenja za koja morate dizajnirati#

Kvaliteta animacije ograničena je frame budžetom. Na 60Hz imate oko 16.67ms po frameu ukupno, podijeljeno između posla na UI threadu i raster threadu. Na 120Hz to je 8.33ms, što znači da je “meni je glatko na mom mobitelu” slab kriterij prihvaćanja.

Najčešći izvori trzanja u produkciji svode se na nekoliko kategorija.

Izvor trzanjaŠto viditeZašto se događaUblažavanje
Previše rebuildovaPadovi frameova tijekom promjena stanjaPrevelik widget subtree se rebuilda po tickuDržite animirani subtree malim, koristite AnimatedBuilder, ValueListenableBuilder
Previše repaintovaJank i kad su rebuildovi maliVelika područja se repaintaju zbog setState ili neograničenog crtanjaKoristite RepaintBoundary, izbjegavajte animiranje velikih Clip područja
Skupi shaderiJednokratni jank na gradijentima, blurovima, maskamaGPU kompilira shadere tijekom prvih frameovaZagrijte shadere, izbjegnite teške efekte na prvom zaslonu
Dekodiranje i upload slikaJank pri prikazu novih slikaDecode na CPU-u, upload na GPUPrecache slika, ispravne dimenzije, koristite cached_network_image
Layout thrashJank pri animiranju veličineLayout se ponavlja ili su constrainti skupiPreferirajte transformacije, opacity i clipping umjesto promjena koje su teške za layout

⚠️ Upozorenje: Nemojte benchmarkati animacije u debug modu. Debug dodaje instrumentaciju i gasi ključne optimizacije. Koristite profile mode na reprezentativnom uređaju, idealno Android uređaju srednje klase, jer raster ograničenja tamo prvo isplivaju.

# Implicitne animacije: brzo za isporuku, lako za održavanje#

Implicitne animacije sjaje kada animirate jedno svojstvo ili mali skup svojstava na temelju stanja. Smanjuju boilerplate i teže ih je krivo koristiti, što ih čini idealnima za product timove koji brzo iteriraju.

Kada su implicitne animacije pravi alat#

Koristite implicitne animacije kada su sve ove tvrdnje istinite:

  • Animaciju pokreće jednostavna promjena stanja, npr. prošireno ili sklopljeno.
  • Ne trebate preciznu koordinaciju kroz više widgeta.
  • Možete tolerirati zadane curveove ili jedan curve.
  • Ne trebate pauzirati, okrenuti smjer na pola ili “skočiti” na određeni timestamp.

Tipični produkcijski slučajevi su feedback na pritisak gumba, expand/collapse stavki liste, prijelazi teme i male prilagodbe layouta.

Osnovni implicitni widgeti koje ćete najčešće koristiti#

WidgetNajbolje zaČesta zamkaSigurnija alternativa
AnimatedContainerPromjene veličine, paddinga, boje, borderaAnimiranje layouta složenih subtreeova uzrokuje layout posaoGdje je moguće animirajte transformaciju s AnimatedScale ili Transform.scale
AnimatedOpacityFade in/outI dalje builda child svaki frame ako se loše kombiniraOmotajte statični child u RepaintBoundary
AnimatedSwitcherZamjena widgeta s prijelazomNema keyeva pa prijelazi budu pogrešniDajte stabilan ValueKey po vizualnom stanju
TweenAnimationBuilderJednokratni custom tweenRebuild teškog child subtreeaKoristite child parametar da izbjegnete rebuild

Primjer: proširenje kartice s AnimatedContainer i AnimatedSwitcher#

Dart
class ExpandableCard extends StatelessWidget {
  const ExpandableCard({
    super.key,
    required this.expanded,
    required this.onTap,
  });
 
  final bool expanded;
  final VoidCallback onTap;
 
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 250),
        curve: Curves.easeOutCubic,
        padding: EdgeInsets.all(expanded ? 20 : 12),
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(expanded ? 24 : 16),
          boxShadow: const [
            BoxShadow(blurRadius: 16, color: Color(0x14000000)),
          ],
        ),
        child: AnimatedSwitcher(
          duration: const Duration(milliseconds: 200),
          child: expanded
              ? const _ExpandedBody(key: ValueKey('expanded'))
              : const _CollapsedBody(key: ValueKey('collapsed')),
        ),
      ),
    );
  }
}

Ovaj obrazac se brzo isporučuje i ostaje čitljiv. Najveći rizik je da slučajno animirate layout-teške subtreeove. Ako proširenje okida puno re-layouta, razmislite o animiranju transformacije ili izolirajte repaint na najmanje moguće područje.

💡 Savjet: U AnimatedSwitcher uvijek postavite keyeve prema vizualnim stanjima. Bez keyeva Flutter može tretirati widgete kao isti child i preskočiti prijelaz ili animirati pogrešno.

# Eksplicitne animacije: obrasci s AnimationController koji skaliraju#

Eksplicitne animacije su mjesto gdje produkcijske aplikacije postaju glatke i kontrolirane — ili neodržive. Razlika je u tome kako strukturirate controllere, izolirate rebuildove i definirate “tko je vlasnik” animacijskog stanja.

Kada trebate eksplicitne animacije#

Prijeđite na eksplicitne animacije kada trebate jedno ili više od sljedećeg:

  • Više tweenova koji moraju ostati sinkronizirani.
  • Sekvenciranje, stagger ili orkestraciju kroz komponente.
  • Prekide, npr. user scrubbing ili animacije vođene gestama.
  • Play, pause, reverse, repeat ili seeking.
  • Dijeljene controllere kroz widgete, npr. sklopljivi header + fade title.

Obrazac 1: AnimationController + AnimatedBuilder sa stabilnim childom#

Ovo je najpouzdaniji obrazac za performanse i čitljivost. Ograničava rebuildove na najmanji subtree i izbjegava ponovno računanje statičnih widgeta na svakom ticku.

Dart
class PulseIcon extends StatefulWidget {
  const PulseIcon({super.key});
 
  @override
  State<PulseIcon> createState() => _PulseIconState();
}
 
class _PulseIconState extends State<PulseIcon> with SingleTickerProviderStateMixin {
  late final AnimationController _c = AnimationController(
    vsync: this,
    duration: const Duration(milliseconds: 900),
  )..repeat(reverse: true);
 
  late final Animation<double> _scale = Tween(begin: 1.0, end: 1.12).animate(
    CurvedAnimation(parent: _c, curve: Curves.easeInOut),
  );
 
  @override
  void dispose() {
    _c.dispose();
    super.dispose();
  }
 
  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _scale,
      child: const Icon(Icons.notifications, size: 28),
      builder: (context, child) {
        return Transform.scale(scale: _scale.value, child: child);
      },
    );
  }
}

Ovo koristi transformaciju umjesto layouta, što obično drži raster trošak niskim i izbjegava layout posao.

Obrazac 2: Staggered sekvence s Interval na jednom controlleru#

U produkciji je lakše upravljati s manje controllera. Čest pristup je jedan controller s više animacija definiranih preko Interval.

Dio animacijeIntervalCurveSvojstvo
Fade in0.00 do 0.35easeOutopacity
Pomak gore0.10 do 0.60easeOutCubicoffset
“Smirivanje” skale0.40 do 1.00easeOutBackscale

Ova struktura olakšava reverse cijele sekvence i zadržava sve poravnato.

Obrazac 3: Eksplicitne animacije vođene gestama#

Ako korisnik kontrolira progres, nemojte se boriti s implicitnim animacijama. Koristite controller i postavite controller.value prema progresu draga, a na kraju napravite fling s fizikom.

Animacije vođene gestama držite determinističnima. Aplikacija uvijek treba završiti u valjanom stanju čak i ako korisnik prekine na pola.

🎯 Ključna poruka: U produkciji eksplicitne animacije su stvar kontrole i rukovanja prekidima. Ako korisnici mogu brzo mijenjati stanje, trebate eksplicitne obrasce kako se animacije nikad ne bi “naslagale” u pokvaren UI.

# Hero i shared element prijelazi bez vizualnih bugova#

Hero animacije mogu učiniti navigaciju trenutnom, ali također otkrivaju nepodudarnosti layouta, clipping probleme i kašnjenja pri učitavanju slika. Cilj je da prijelaz izgleda kontinuirano, čak i ako odredišni zaslon učitava dodatne podatke.

Praktična pravila za pouzdane Hero prijelaze#

  1. 1
    Koristite stabilne tagove koji identificiraju sadržaj, ne instancu widgeta. Product id je dobar tag.
  2. 2
    Pobrinite se da Hero na izvoru i odredištu imaju slične oblike i omjere stranica.
  3. 3
    Ako je moguće, preloadajte odredišnu sliku ili koristite isti ImageProvider na oba mjesta.
  4. 4
    Omotajte Hero child u Material pri prijelazu između material konteksta kako biste izbjegli “ink” ili artefakte elevacije.

Primjer: Hero s konzistentnim oblikom i materialom#

Dart
Hero(
  tag: 'product-image-42',
  child: Material(
    color: Colors.transparent,
    child: ClipRRect(
      borderRadius: BorderRadius.circular(16),
      child: Image.network(
        imageUrl,
        fit: BoxFit.cover,
      ),
    ),
  ),
)

Ako vidite “flash” tijekom prijelaza, često je razlog to što odredište učita sliku druge veličine ili slika još nije u cacheu. Pre-caching rješava mnoge takve probleme.

Shared element prijelazi izvan Hero-a#

Za složenije shared element prijelaze možete kombinirati Hero za glavni element s prijelazima routea za ostatak ekrana. Prijelaze routea držite suptilnima kako bi Hero ostao fokus.

# Rive vs Lottie: kada koristiti koji u produkciji#

Rive i Lottie rješavaju različite probleme. Najbrži način da odaberete pravi alat jest odlučiti je li animacija interaktivna i stateful ili je uglavnom linearna i dekorativna.

Tablica odluke: Rive vs Lottie vs native Flutter animacije#

ZahtjevNative FlutterLottieRive
Jednostavne animacije UI svojstavaNajboljePretjeranoPretjerano
Složena, interaktivna animacijska stanjaTeškoOgraničenoNajbolje
Workflow predaje dizajneraSrednjeOdlično kroz AE exportOdlično kroz Rive editor
Runtime kontrola preko parametaraDobroOgraničenoIzvrsno sa state machine-ovima
Predvidljivost veličine datotekeDobraVarira po složenosti JSON-aU pravilu dobra, ovisi o assetima
Performanse na slabijim uređajimaDobre uz optimizacijuDobre za jednostavne animacije, mogu biti teškeUglavnom dobre, ali testirajte složene rigove

Koristite Lottie kada#

  • Animacija je uglavnom linearna i loopana, npr. onboarding, empty stateovi ili lagane “proslave”.
  • Dizajneri već rade u After Effects-u i mogu exportati preko Bodymovin-a.
  • Možete prihvatiti ograničenja u dinamičkom tekstu, maskiranju i određenim efektima ovisno o exportu.

Lottie animacije držite malima. U produkciji su JSON datoteke od nekoliko stotina kilobajta česte, ali višemegabajtne datoteke obično postaju problem za startup i memoriju.

Koristite Rive kada#

  • Trebate interaktivnu animaciju vođenu stanjem aplikacije, npr. mikro-interakcije gumba, reakcije avatara ili indikator napretka sa stanjima.
  • Želite state machine koji reagira na inpute poput “loading”, “success”, “error”.
  • Trebate parametrizaciju u runtimeu, ne samo play i stop.

Rive je često najbolji izbor kada je animacija dio produktne logike, a ne samo dekoracija.

ℹ️ Napomena: Rive i Lottie tretirajte kao UI ovisnosti s verzioniranjem. Datoteke animacija su “kod pod maskom”. Spremite ih u repo, pregledavajte diffs i namećite ograničenja poput maksimalne veličine datoteke i maksimalne složenosti artboarda.

# Savjeti za performanse: repaintovi, overdraw i kompilacija shadera#

Glatkoća je kombinacija troška na UI threadu (build) i troška na raster threadu (paint). Animacije često padaju u produkciji jer developeri optimiziraju pogrešnu stranu.

Smanjite repaintove s RepaintBoundary i stabilnim subtreeovima#

Ako se u svakom frameu mijenja samo mali dio UI-ja, izolirajte ga. RepaintBoundary prisiljava Flutter da cacheira nacrtani rezultat i sprječava repaint susjednih elemenata.

Koristite ga za animirane ikone, loading indikatore i bilo koju komponentu koja kontinuirano “tika”.

Praktične heuristike:

  • Ako animacija radi kontinuirano, izolirajte je.
  • Ako parent mijenja opacity ili transform, može invalidirati velika područja. Spustite animaciju niže u stablu gdje god možete.

Preferirajte transformacije i opacity umjesto layout promjena#

Animiranje width, height ili paddinga okida layout. Layout nije uvijek spor, ali postaje skup kada je subtree složen ili ugniježđen u scrollable.

Preferirajte prvo:

  • Transform.translate, Transform.scale, AnimatedSlide
  • Opacity ili FadeTransition

Layout animacije koristite samo kada su potrebne za UX, npr. proširenje sekcije koja mora gurnuti sadržaj prema dolje.

Izbjegavajte skupo clippinganje tijekom animacije#

Clipping može biti skup, posebno s anti-aliasingom i velikim površinama. Čest primjer je animiranje velikog ClipRRect na svakom ticku.

Ako morate clipati:

  • Držite clipped područje malim.
  • Izbjegavajte animiranje radiusa clippa na ogromnim containerima.
  • Testirajte na Android uređajima gdje raster trošak može naglo skočiti.

Rukujte zastajkivanjem zbog kompilacije shadera#

Određeni efekti okidaju kompilaciju shadera u runtimeu, što uzrokuje jednokratan jank. Najčešći krivci su gradijenti, blurovi i složene maske.

Ublažavanja:

  • Izbjegnite teške vizualne efekte na prvom zaslonu nakon cold starta.
  • Zagrijte ključne shadere tijekom skrivene warm-up faze.
  • Ponovno koristite iste efekte konzistentno, jer se kompilacija događa po varijanti shadera.

Praktičan pristup je prikazati lagan početni UI, zatim prijeći na teže efekte nakon kratke odgode ili nakon prve interakcije. Korisnici doživljavaju aplikaciju brzom, čak i ako se shader kompilira u pozadini.

Mjerite prave metrike u DevTools#

U Flutter DevTools fokusirajte se na:

  • Frame chart za broj “janky frameova” i spikeove.
  • UI thread time za build i layout troškove.
  • Raster thread time za paint, clip i shader probleme.
  • Overlays “Repaint rainbow” i “Track widget rebuilds” za brzu dijagnostiku.

Za dublji checklist i workflow profiliranja, koristite Flutter optimizacija performansi za 60fps.

# Strategije testiranja animacija u produkciji#

Regresije animacija su česte jer je kretanje teško pregledati u PR-ovima i razlikuje se po uređajima. Produkcijska strategija kombinira deterministične testove i profiliranje.

Widget testovi s kontroliranim vremenom#

Koristite widget testove da provjerite dosegne li widget očekivana stanja u određenim vremenskim točkama. Ključ je pumpati s trajanjem kako bi animacija napredovala predvidljivo.

Dart
testWidgets('expands on tap', (tester) async {
  var expanded = false;
 
  await tester.pumpWidget(
    MaterialApp(
      home: StatefulBuilder(
        builder: (context, setState) {
          return ExpandableCard(
            expanded: expanded,
            onTap: () => setState(() => expanded = !expanded),
          );
        },
      ),
    ),
  );
 
  await tester.tap(find.byType(ExpandableCard));
  await tester.pump(const Duration(milliseconds: 300));
 
  expect(find.byKey(const ValueKey('expanded')), findsOneWidget);
});

Ovo neće uhvatiti probleme s frame budžetom, ali će uhvatiti slomljene prijelaze, nedostajuće keyeve i regresije u logici.

Golden testovi za ključne frameove#

Golden testovi su dobar izbor kada možete definirati “ovako UI izgleda na 0ms, 150ms, 300ms”. Držite goldene za kritične tokove poput onboardinga, checkouta i glavnih navigacijskih prijelaza.

Stabilizirajte goldene kontrolirajući:

  • Text scale factor
  • Fontove
  • Veličinu uređaja
  • Platformu

Testiranje regresija performansi#

Pokrenite provjere performansi u CI-ju za ključne ekrane u profile modu gdje god je moguće, ili barem zahtijevajte ručno profiliranje prije izdanja.

Praktičan workflow:

  1. 1
    Identificirajte 3 do 5 “animation-heavy” ekrana.
  2. 2
    Snimite DevTools performance traceove za fiksni skript interakcija.
  3. 3
    Pratite metrike poput 90. percentila frame timea i broja janky frameova kroz 10 sekundi.
  4. 4
    Usporedite traceove između release candidate verzija.

Ovdje je disciplina inženjeringa presudna. Ako je codebase modularan i state management predvidljiv, dijagnosticiranje regresija animacija je znatno brže. Zato arhitektura i izbor stanja izravno utječu na kvalitetu animacija. Za to pročitajte Clean Architecture feature-first u Flutteru i Flutter upravljanje stanjem u 2026.

# Ključne poruke#

  • Koristite implicitne animacije za lokalne prijelaze vođene stanjem, a prijeđite na eksplicitne obrasce s AnimationController kada trebate sekvenciranje, rukovanje prekidima ili više sinkroniziranih tweenova.
  • Držite rebuildove i repaintove malima koristeći AnimatedBuilder sa stabilnim child widgetima i izoliranjem kontinuiranih animacija s RepaintBoundary.
  • Preferirajte transformacije i opacity umjesto layout-teških animacija te budite oprezni s velikim clipovima i blur efektima koji dižu raster vrijeme.
  • Hero prijelaze radite sa stabilnim tagovima, konzistentnim oblicima i preloadanim slikama kako biste spriječili flash i nepodudarno kretanje.
  • Odaberite Lottie za uglavnom linearnu dekorativnu animaciju, a Rive za interaktivne animacije vođene state machine-om; animacijske assete tretirajte kao verzionirani kod s ograničenjima veličine.
  • Testirajte animacije determinističnim widget testovima i golden testovima ključnih frameova te profilirajte u profile modu kako biste rano uhvatili regresije frame budžeta.

# Zaključak#

Produkcijski sustav Flutter animacija je alatni set, ne jedna tehnika. Brzo isporučujte s implicitnim animacijama, skalirajte kontrolu s AnimationController, koristite Hero prijelaze da navigacija djeluje kontinuirano, a Lottie ili Rive uvodite samo kada odgovaraju potrebama proizvoda.

Ako želite pomoć u auditu performansi animacija, postavljanju ponovljivog workflowa profiliranja ili integraciji Rive ili Lottie u održivu Flutter arhitekturu, Samioda može pomoći. Kontaktirajte nas i pregledat ćemo vaše najkritičnije tokove te dati provediv plan kako bi kretanje ostalo glatko na 60fps i više.

FAQ

Share
A
Adrijan OmićevićOsnivač i senior developer

Osnivač i senior developer u Samiodi. 8+ godina iskustva u izradi React, Next.js, Flutter i n8n rješenja za klijente diljem Europe.

Trebate pomoć s projektom?

Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.

Povezani članci

·14 min čitanja

Optimizacija performansi u Flutteru: kako održavamo aplikacije na 60 fps (profiliranje + popravci)

Ponovljiv workflow za optimizaciju performansi u Flutteru uz DevTools: dijagnosticiranje trzanja (jank), zatim ciljane dorade — smanjenje rebuildova, poboljšanja renderiranja, image pipeline i isolates — uz budžete i kontrolne liste.

FlutterRazvoj mobilnih aplikacijaPerformanseProfiliranjeDevToolsOptimizacija
Adrijan OmićevićPročitaj članak
·15 min čitanja

Flutter + Supabase u produkciji: Auth, Realtime, RLS i offline-pristup podacima (Vodič za 2026.)

Vodič spreman za produkciju za Flutter + Supabase auth, realtime i offline sync: sigurni auth flowovi, obrasci za Row Level Security, realtime pretplate te offline-first UX uz praktičan kod i česte zamke.

FlutterSupabaseAutentifikacijaRealtimeOffline-firstRLSRazvoj mobilnih aplikacijaPostgreSQL
Adrijan OmićevićPročitaj članak
·13 min čitanja

Flutter vs izvorni iOS/Android u 2026.: kompromisi između troška, performansi i vremena do izlaska na tržište

Praktična, brojkama potkrijepljena usporedba Fluttera i izvornog iOS-a i Androida za 2026. — uključuje model troška, realnost performansi, utjecaj održavanja i okvir za odluku za MVP-ove, UI visokih performansi, zahtjevne platform API-je i regulirane aplikacije.

FlutteriOSAndroidRazvoj mobilnih aplikacijaUsporedbaVrijeme do izlaska na tržišteTrošak aplikacije
Adrijan OmićevićPročitaj članak