# Š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 vidite | Zašto se događa | Ublažavanje |
|---|---|---|---|
| Previše rebuildova | Padovi frameova tijekom promjena stanja | Prevelik widget subtree se rebuilda po ticku | Držite animirani subtree malim, koristite AnimatedBuilder, ValueListenableBuilder |
| Previše repaintova | Jank i kad su rebuildovi mali | Velika područja se repaintaju zbog setState ili neograničenog crtanja | Koristite RepaintBoundary, izbjegavajte animiranje velikih Clip područja |
| Skupi shaderi | Jednokratni jank na gradijentima, blurovima, maskama | GPU kompilira shadere tijekom prvih frameova | Zagrijte shadere, izbjegnite teške efekte na prvom zaslonu |
| Dekodiranje i upload slika | Jank pri prikazu novih slika | Decode na CPU-u, upload na GPU | Precache slika, ispravne dimenzije, koristite cached_network_image |
| Layout thrash | Jank pri animiranju veličine | Layout se ponavlja ili su constrainti skupi | Preferirajte 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#
| Widget | Najbolje za | Česta zamka | Sigurnija alternativa |
|---|---|---|---|
AnimatedContainer | Promjene veličine, paddinga, boje, bordera | Animiranje layouta složenih subtreeova uzrokuje layout posao | Gdje je moguće animirajte transformaciju s AnimatedScale ili Transform.scale |
AnimatedOpacity | Fade in/out | I dalje builda child svaki frame ako se loše kombinira | Omotajte statični child u RepaintBoundary |
AnimatedSwitcher | Zamjena widgeta s prijelazom | Nema keyeva pa prijelazi budu pogrešni | Dajte stabilan ValueKey po vizualnom stanju |
TweenAnimationBuilder | Jednokratni custom tween | Rebuild teškog child subtreea | Koristite child parametar da izbjegnete rebuild |
Primjer: proširenje kartice s AnimatedContainer i AnimatedSwitcher#
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
AnimatedSwitcheruvijek 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.
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 animacije | Interval | Curve | Svojstvo |
|---|---|---|---|
| Fade in | 0.00 do 0.35 | easeOut | opacity |
| Pomak gore | 0.10 do 0.60 | easeOutCubic | offset |
| “Smirivanje” skale | 0.40 do 1.00 | easeOutBack | scale |
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#
- 1Koristite stabilne tagove koji identificiraju sadržaj, ne instancu widgeta. Product id je dobar tag.
- 2Pobrinite se da Hero na izvoru i odredištu imaju slične oblike i omjere stranica.
- 3Ako je moguće, preloadajte odredišnu sliku ili koristite isti
ImageProviderna oba mjesta. - 4Omotajte Hero child u
Materialpri prijelazu između material konteksta kako biste izbjegli “ink” ili artefakte elevacije.
Primjer: Hero s konzistentnim oblikom i materialom#
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#
| Zahtjev | Native Flutter | Lottie | Rive |
|---|---|---|---|
| Jednostavne animacije UI svojstava | Najbolje | Pretjerano | Pretjerano |
| Složena, interaktivna animacijska stanja | Teško | Ograničeno | Najbolje |
| Workflow predaje dizajnera | Srednje | Odlično kroz AE export | Odlično kroz Rive editor |
| Runtime kontrola preko parametara | Dobro | Ograničeno | Izvrsno sa state machine-ovima |
| Predvidljivost veličine datoteke | Dobra | Varira po složenosti JSON-a | U pravilu dobra, ovisi o assetima |
| Performanse na slabijim uređajima | Dobre uz optimizaciju | Dobre za jednostavne animacije, mogu biti teške | Uglavnom 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,AnimatedSlideOpacityiliFadeTransition
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.
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:
- 1Identificirajte 3 do 5 “animation-heavy” ekrana.
- 2Snimite DevTools performance traceove za fiksni skript interakcija.
- 3Pratite metrike poput 90. percentila frame timea i broja janky frameova kroz 10 sekundi.
- 4Usporedite 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
AnimationControllerkada trebate sekvenciranje, rukovanje prekidima ili više sinkroniziranih tweenova. - Držite rebuildove i repaintove malima koristeći
AnimatedBuildersa stabilnimchildwidgetima i izoliranjem kontinuiranih animacija sRepaintBoundary. - 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
Osnivač i senior developer u Samiodi. 8+ godina iskustva u izradi React, Next.js, Flutter i n8n rješenja za klijente diljem Europe.
Više iz kategorije Mobilni razvoj
Sve →Flutter + Supabase vs Firebase u 2026: Auth, Realtime, Offline, Cijene i Lock-In
Praktična usporedba za 2026. Fluttera sa Supabaseom i Firebaseom kroz auth, push, realtime, offline/local-first, storage, funkcije, cijene i vendor lock-in — uz preporuke po tipu aplikacije i skali.
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.
Skaliranje Fluttera modularizacijom: Postavljanje monorepa s Melosom, dijeljenim paketima i čistim granicama
Praktični vodič za modularizaciju Flutter monorepa s Melosom: kada razdvajati u pakete, kako strukturirati dijeljeni kod, kako provoditi granice te kako učinkovito pokretati CI i testove kroz rastuću bazu koda.
Trebate pomoć s projektom?
Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.
Povezani članci
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.
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.
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.