# Što ćete naučiti#
Ovaj vodič se fokusira na optimizaciju Flutter cold starta: kako ispravno mjeriti cold start, postaviti mjerljive ciljeve i ukloniti najčešća uska grla pri pokretanju u stvarnim aplikacijama. Naučit ćete pristup sekvenciranja za splash screenove i deep linkove koji izbjegava blokiranje prvog framea, a i dalje rutira ispravno.
Ako ste već optimizirali skrolanje i animacije unutar aplikacije, ovo je dio koji nedostaje. Performanse pri pokretanju su konverzijska metrika: sporiji cold start korelira s većim bounce rateom, posebno na Android low-end uređajima i kada korisnik dolazi preko deep linka.
Za širi rad na runtime performansama Fluttera, pročitajte Flutter performance optimization for 60 FPS.
# Definirajte cold start i postavite mjerljive ciljeve#
Cold start znači da OS stvara proces iz nule. Uključuje nativnu inicijalizaciju, bootstrap Flutter enginea, start Dart isolatea, prvi build widgeta i prvi rasterizirani frame.
Vaše metrike trebaju biti specifične i testabilne.
| Metrika | Što znači | Preporučeni cilj |
|---|---|---|
| Vrijeme do prvog Flutter framea | Vrijeme od starta procesa do prvog renderiranog Flutter framea | 400 do 800 ms na modernim uređajima, ispod 1500 ms na low-end |
| Vrijeme do interaktivnosti | Vrijeme dok prvi smisleni ekran nije upotrebljiv | Ispod 1500 ms za tipične flowove |
| Startup jank frameovi | Ispušteni ili odgođeni frameovi u prve 1 do 2 sekunde | Manje od 3 |
| Main thread long taskovi | Blokirajući taskovi na Android main threadu ili iOS main run loopu | Nema taskova duljih od 100 ms tijekom startupa |
| Vrijeme routanja deep linka | Vrijeme od pokretanja do ispravnog odredišnog ekrana | Ispod 2000 ms uz dopušten placeholder |
🎯 Ključna poruka: Tretirajte pokretanje kao budžet. Cilj je brz prvi frame, a zatim postupno “hidriranje” aplikacije.
# Kako profilirati cold start (Android i iOS)#
Ne možete optimizirati ono što ne mjerite. Profilirajte u tri sloja: OS startup timeline, Flutter frame timing i app-level spanove.
Android: mjerite s Perfetto, logcat i Flutter alatima#
1) Prepoznajte sporu startup putanju s Perfetto
Perfetto ističe blokiranje main threada, inicijalizaciju plugina i skupe operacije tijekom Activity startupa. Snimite trace tijekom cold starta i tražite duge “sliceove” oko pokretanja aplikacije.
2) Koristite adb za hvatanje vremena pokretanja
Ovo je brza baseline mjera za svaki build i tier uređaja.
adb shell am force-stop com.example.app
adb shell am start -W -n com.example.app/.MainActivityVidjet ćete timingove poput ThisTime i TotalTime. Tretirajte ih kao grube indikatore, ne kao jedinu istinu, jer se ne mapiraju savršeno na prvi Flutter frame.
3) Snimite Flutter frame timing
Pokrenite u profile modu i koristite DevTools za pregled build i raster vremena oko starta. Tražite:
- spikeove build vremena uzrokovane teškim widget stablima i sinkronom inicijalizacijom
- spikeove raster vremena uzrokovane decodeom slika, kompilacijom shadera i velikim prvim paintom
flutter run --profile --trace-startup --verboseStartup trace može pokazati koji se Dart kod izvršava prije prvog framea.
iOS: Instruments i signpostovi#
Na iOS-u koristite Instruments uz:
- Time Profiler za CPU hotspotove tijekom pokretanja
- Main Thread Checker za slučajno blokiranje main threada
- os_signpost spanove ako dodate app-level markere
Često ćete naći težak posao u application:didFinishLaunchingWithOptions: ili ranom kodu plugina, plus sinkrona čitanja s diska u prvim sekundama.
Dodajte app-level startup spanove za korisnu atribuciju#
Trace koji kaže “startup je spor” nije akcijski. Trebate atribuciju za korake poput učitavanja konfiguracije, dohvaćanja tokena, rutanja i renderiranja prvog ekrana.
Jednostavan obrazac je startup štoperica i strukturirani logovi.
// Keep this minimal and remove in release if not needed.
final appStart = Stopwatch()..start();
void mark(String name) {
// Avoid string-heavy logs in release; route to your telemetry in production.
// Example output: [startup] 123ms init:load_config
// ignore: avoid_print
print('[startup] ${appStart.elapsedMilliseconds}ms $name');
}Za observability u produkciji, povežite startup spanove i crash kontekst preko Flutter app observability with Crashlytics, Sentry, logging, metrics, and tracing.
ℹ️ Napomena: Cold start uvijek testirajte tako da force-stopate aplikaciju. “First run after hot restart” nije cold start i dovest će vas u zabludu.
# Popravite najveće “ubojice” cold starta#
Većina Flutter cold start regresija dolazi od toga da se previše radi prije runApp, da se UI thread blokira sinkronim I/O-om, da se velike slike dekodiraju na kritičnoj putanji ili da plaćate plugin startup trošak koji vam ne treba.
1) Teška inicijalizacija prije runApp#
Česti anti-patterni:
- čekanje remote configa prije prikaza UI-a
- inicijaliziranje analyticsa, feature flagova, A B testiranja, push notifikacija, plaćanja i baze prije prvog framea
- eager gradnja dependency grafova i service locatora
Pravilo: pozovite runApp što ranije. Zatim obavite nekritični init nakon prvog framea.
import 'package:flutter/widgets.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Minimal critical init only.
final initialRoute = await _captureInitialRouteFast();
runApp(App(initialRoute: initialRoute));
// Defer heavy work.
WidgetsBinding.instance.addPostFrameCallback((_) async {
await _initNonCriticalServices();
});
}
Future<String?> _captureInitialRouteFast() async {
// Keep it fast: read only what is needed to decide the initial route.
return null;
}
Future<void> _initNonCriticalServices() async {
// Analytics, remote config, experiments, crash reporting enrichment, etc.
}Ako zbog ispravnosti morate blokirati, definirajte strogi budžet. Primjerice, možete dopustiti do 100 do 150 ms prije runApp na modernim uređajima, a i dalje morate testirati low-end.
⚠️ Upozorenje: Izbjegavajte
awaitlance umainkoji uključuju networking. Jedan spor TLS handshake može pretvoriti start od 700 ms u start od 5 sekundi.
2) Sinkroni I/O pri pokretanju#
Startup treba izbjegavati sinkrono čitanje i pisanje na disk, posebno na Androidu gdje main thread može biti blokiran. Tipični izvori:
- višekratno čitanje preferencesa ili secure storagea
- učitavanje velikih JSON datoteka iz asseta i sinkrono parsiranje
- migracije lokalnih baza pri prvom pokretanju
Obrasci za popravak
- grupirajte čitanja u jedan poziv nakon prvog framea
- cacheirajte kritične male vrijednosti jednom u memoriji
- prebacite parsiranje i migracije s main isolatea koristeći
computeili dedicated isolate
Primjer: prebacite JSON parsiranje s UI isolatea.
import 'dart:convert';
import 'package:flutter/foundation.dart';
Future<Map<String, dynamic>> loadAndParse(String rawJson) {
return compute(_parseJson, rawJson);
}
Map<String, dynamic> _parseJson(String rawJson) {
return jsonDecode(rawJson) as Map<String, dynamic>;
}Ako se oslanjate na secure storage za tokene, preferirajte:
- čitanje jednom, cache u memoriji
- odmah renderiranje neutralnog prvog ekrana
- zatim prijelaz kad je auth stanje poznato
3) Decode slika i trošak prvog painta#
Velike slike mogu dominirati i CPU-om i GPU-om pri pokretanju:
- decode velikih PNG ili JPEG na kritičnoj putanji
- renderiranje velike hero slike u prvom frameu
- korištenje punih rezolucija asseta za male UI elemente
Što napraviti
- neka prvi frame bude vizualno jednostavan
- učitavajte “teške” slike nakon prvog framea
- koristite assete odgovarajuće veličine i kompresiju
- pre-cacheajte samo ono što stvarno trebate za sljedeći ekran
Primjer: odgodite precache do nakon prvog framea.
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
precacheImage(const AssetImage('assets/hero.webp'), context);
});
}
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}Također razmotrite WebP gdje ima smisla. Za fotografije, WebP obično značajno smanjuje veličinu u odnosu na PNG, čime smanjuje I/O i decode vrijeme.
4) Plugin startup overhead i posao na main threadu#
Plugini mogu utjecati na cold start na dva načina:
- overhead registracije pri pokretanju
- obavljanje skupog posla tijekom
onAttachedToEngineili ranih lifecycle callbackova
Tipični krivci u stvarnim aplikacijama:
- analytics SDK-ovi koji rade čitanja s diska pri initu
- attribution SDK-ovi
- neki push i marketing SDK-ovi
- database plugini koji eager rade migracije
Plan djelovanja
- 1Napravite inventuru plugina i kategorizirajte ih kao kritične za prvi ekran naspram onih koje možete odgoditi.
- 2Uklonite neiskorištene plugine i neiskorištene tranzitivne dependencyje.
- 3Nadogradite plugine. Mnogi problemi s performansama se s vremenom poprave.
- 4Odgodite inicijalizaciju tako da odgodite korištenje plugina ili koristite lazy singletonove u Dart-u.
| Kategorija plugina | Potrebno prije prvog framea | Strategija odgode |
|---|---|---|
| Crash reporting | Nije potrebno za prvi frame | Inicijalizirajte nakon prvog framea, zatim dodajte user context |
| Analytics | Ne | Lokalno queueajte evente, pokrenite SDK nakon prve interakcije |
| Remote config | Rijetko | Koristite zadnje poznate vrijednosti, osvježite async |
| Deep linking | Brzo uhvatite intent | Rutirajte na placeholder, riješite nakon prvog framea |
| Payments | Ne | Inicijalizirajte kad korisnik uđe u checkout |
Kad trebate deep linkove, držite sekvencu inicijalizacije čvrstom i predvidljivom. Za detalje implementacije deep linkova, pogledajte Flutter deep linking: Universal Links, App Links, and routing.
# Preporučeno sekvenciranje splasha i inicijalizacije deep linkova#
Brz launch je uglavnom pitanje toga da radite manje prije prvog framea. Ispravan launch je pitanje toga da radite pravi minimum prije prvog framea.
Cilj sekvenciranja:
- 1Odmah prikažite nativni splash.
- 2Odredite minimalni routing kontekst bez blokiranja.
- 3Renderirajte prvi Flutter frame s laganim placeholderom.
- 4Nakon prvog framea riješite deep link, auth, remote config i podatke.
- 5Navigirajte ili ažurirajte state kada je spremno.
Pragmatičan startup state machine#
Koristite jedan startup coordinator koji emitira mali set stanja. Neka prvi ekran bude jeftin: bez teških slika, bez ogromnih lista, bez skupih layoutova.
| Faza | Vremenski budžet | Što se izvršava | Prikazani UI |
|---|---|---|---|
| Native splash | OS-managed | Kreiranje app procesa | Nativni splash screen |
| Pre-runApp | manje od 150 ms | Hvatanje initial intenta, postavljanje minimalnog DI-ja | Flutter placeholder |
| Prvi Flutter frame | ASAP | Render app shell-a | Skeleton ili loading ruta |
| Post-frame init | 0 do 2000 ms | Auth provjera, refresh configa, warming cacheva | Postupna hidratacija |
| Finalizacija rute | ispod 2000 ms | Navigacija na deep link ili home | Finalni ekran |
💡 Savjet: Neka prva Flutter ruta bude “AppShell” koji se može renderirati odmah i reagirati na promjene stanja, umjesto da sve odlučujete u
main.
Deep linkovi bez blokiranja prvog framea#
Na Androidu i iOS-u često dobijete deep link podatke rano, ali trebate izbjegavati čekanje na network ili database posao prije prvog painta.
Praktičan pristup:
- brzo uhvatite deep link URI
- spremite ga u memoriju kao
pendingLink - odmah renderirajte AppShell
- nakon prvog framea riješite auth i dohvatite potrebni entitet
- navigirajte na cilj kad je spremno, a u međuvremenu prikažite lagani ekran “učitavanje odredišta”
Primjer minimalnog capturea u Dart-side logici:
class StartupModel {
Uri? pendingLink;
bool authKnown = false;
bool isAuthenticated = false;
}
final startup = StartupModel();Zatim, u post-frame initu:
- ako deep link postoji i traži auth, prikažite mali interstitial dok se auth ne riješi
- ako deep link traži dohvat podataka, fetchajte nakon prvog framea i navigirajte kad završi
Time izbjegavate najgori UX obrazac: prazan ekran dok aplikacija radi “startup work”.
# Smanjite first-frame jank: frame budžet i praktični popravci#
Na 60 Hz imate oko 16,7 ms po frameu. Tijekom startupa možete tolerirati mali broj sporih frameova, ali trebate ih ograničiti.
Česti uzroci startup janka:
- skup prvi build zbog ogromnih widget stabala
- sinkroni decode ili layout velikih slika
- teški posao u
initStatekroz više widgeta - ponavljane
setStatepetlje tijekom inicijalizacije
Konkretni popravci koji se brzo isplate:
- smanjite početnu dubinu widgeta i izbjegavajte velike offscreen strukture
- ne učitavajte fontove, slike i remote config u prvom buildu
- koristite const widgete gdje je moguće na prvoj ruti
- odgodite neesencijalne animacije dok se početno stanje ne stabilizira
Ako je prvi ekran dashboard, izbjegnite graditi cijeli dashboard u prvom frameu. Prikažite skeleton i postupno renderirajte sekcije kako podaci postaju dostupni.
Za dublje runtime taktike, pogledajte Flutter performance optimization for 60 FPS.
# Ponovljiv checklist za optimizaciju cold starta#
Koristite ovaj checklist po releaseu kako biste spriječili regresije.
Korak 1: Baseline na dva “tiera” uređaja#
Odaberite:
- jedan moderan uređaj
- jedan low-end ili stariji uređaj
Zabilježite:
adb am start -Wbrojeve- vrijeme do prvog Flutter framea u DevTools
- broj startup jank frameova u DevTools
Korak 2: Audit main i pre-runApp posla#
Provjerite:
- nema networkinga prije
runApp - nema velikog parsiranja ili migracija prije
runApp - nema višestrukih awaitova koji serijaliziraju neovisne taskove
Ako imate više neovisnih taskova koji moraju rano odraditi, pokrenite ih konkurentno i nametnite timeout, ali neka bude minimalno.
Future<void> initWithBudget() async {
final tasks = <Future<void>>[
_initMinimalConfig(),
_initMinimalLogging(),
];
await Future.any([
Future.wait(tasks),
Future<void>.delayed(const Duration(milliseconds: 150)),
]);
}Korak 3: Premjestite teške taskove nakon prvog framea#
Tipični kandidati:
- refresh remote configa
- pokretanje analyticsa
- inicijalizacija attribution SDK-a
- image pre-cache
- database warmup
Korak 4: Provjerite plugine i nativni startup kod#
Na Androidu:
- neka
ApplicationiMainActivitybudu lean - izbjegavajte teški sinkroni posao u
onCreate
Na iOS-u:
- neka
didFinishLaunchingWithOptionsbude lean - izbjegavajte čitanja s diska i veliku inicijalizaciju u AppDelegateu
Korak 5: Dodajte observability za otkrivanje regresija#
Pratite startup metrike kroz vrijeme u produkciji. Ako vam se p50 i p95 startup vremena pogoršaju nakon dodavanja novog SDK-a, želite brz signal.
Implementacija stvarne startup telemetrije je dio Flutter app observability.
# Česti cold start problemi i njihovi popravci#
| Simptom | Vjerojatan uzrok | Popravak koji možete primijeniti ovaj tjedan |
|---|---|---|
| Prvi frame je brz, ali aplikacija djeluje “zaglavljeno” | Blokiranje deep link resolvanja ili auth provjera | Prvo renderirajte shell, zatim resolve i navigacija |
| Sporo samo pri prvom pokretanju nakon instalacije | Kompilacija shadera, ekstrakcija asseta, migracije | Smanjite kompleksnost prvog ekrana, odgodite migracije, gdje je moguće zagrijte shadere |
| Android pokazuje duge sliceove na main threadu | Sinkroni I/O ili teški SDK init | Uklonite sync pozive, prebacite u background, odgodite SDK init |
| iOS launch time spikeovi u Instruments | Posao u AppDelegateu | Premjestite posao iz launch-a, lazy init servisa |
| Jank tijekom prijelaza sa splasha na home | Decode slika i težak prvi paint | Koristite manje assete, odgodite hero slike, skeleton UI |
# Ključne poruke#
- Optimizirajte prvi Flutter frame tako da rano pozovete
runApp, a nekritični posao odgodite do nakon prvog framea. - Eliminirajte sinkroni I/O i teško parsiranje pri startupu grupiranjem čitanja i prebacivanjem CPU posla u isolateove.
- Neka prvi frame bude vizualno jednostavan kako biste izbjegli spikeove decodea slika i rastera, a zatim postupno “hidrirajte” sadržaj.
- Auditirajte i minimizirajte plugin startup trošak uklanjanjem neiskorištenih SDK-ova, nadogradnjom plugina i odgađanjem inicijalizacije.
- Koristite jasnu sekvencu splasha i deep linkova: brzo uhvatite link, renderirajte shell, a routing i podatke riješite post-frame unutar mjerljivih budžeta.
# Zaključak#
Optimizacija Flutter cold starta je uglavnom pitanje discipliniranog sekvenciranja: renderirajte brzi app shell, a zatim postupno inicijalizirajte sve ostalo bez blokiranja UI-a. Ako u svakom releaseu mjerite vrijeme do prvog framea, startup jank frameove i vrijeme routanja deep linka, regresije postaju očite i popravljive.
Ako želite praktičan audit startup timelinea vaše aplikacije, plugin stacka i deep link routing flowa, Samioda može pomoći postaviti ciljeve, profilirati stvarne uređaje i isporučiti primjetno brže pokretanje. Javite se preko naše stranice i uključite vaše trenutne cold start brojke i ciljne uređaje.
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 →Vodič za pristupačnost u Flutteru: Semantics, redoslijed fokusa i dinamički tekst kako treba
Praktičan vodič za pristupačnost u Flutteru za 2026.: Semantics oznake, ispravan redoslijed fokusa, navigacija tipkovnicom, kontrast i skaliranje teksta uz konkretne korake testiranja za TalkBack i VoiceOver.
Flutter analitika i atribucija za startupove: Firebase vs Amplitude vs AppsFlyer (što pratiti i zašto)
Usporedba Firebase Analyticsa, Amplitudea i AppsFlyera iz perspektive startupa za Flutter analitiku i atribuciju — trud oko postavljanja, trošak, GDPR/privatnost kompromisi, modeliranje događaja te MVP-spremna taksonomija praćenja s planom uvođenja.
Flutter + Supabase Upload Datoteka: Sigurna pohrana, potpisani URL-ovi, promjena veličine slika i kontrola pristupa
Praktičan vodič za 2026. za Flutter Supabase tokove uploada datoteka: upload iz kamere i galerije, pozadinski retry, sigurne Storage politike, potpisani URL-ovi za preuzimanje, promjena veličine slika i error handling na razini produkcije.
Trebate pomoć s projektom?
Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.
Povezani članci
Flutter zadaci u pozadini: raspoređivanje, pouzdanost i ograničenja platformi (iOS + Android) u 2026.
Praktičan vodič za raspoređivanje Flutter zadataka u pozadini: ograničenja platformi na iOS-u i Androidu, kompromisi pouzdanosti te kada koristiti workmanager, background_fetch ili izvorni kod za sinkronizaciju, obavijesti i raspoređivanje koje čuva bateriju.
Flutter kupnje unutar aplikacije i pretplate: Apple i Google postavljanje, testiranje i RevenueCat
Praktični vodič za 2026. za Flutter kupnje unutar aplikacije: postavljanje proizvoda u App Store Connect i Play Console, pretplate, entitlements, validacija računa, paywallovi, sandbox testiranje i otklanjanje stvarnih produkcijskih problema.
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.