Mobilni razvoj
FlutterPerformanse mobilnih aplikacijaAndroidiOSProfiliranjeDX

Optimizacija Flutter cold starta: brže pokretanje, bolji splash, manje jank frameova

AO
Adrijan Omićević
·13 min čitanja

# Š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čiPreporučeni cilj
Vrijeme do prvog Flutter frameaVrijeme od starta procesa do prvog renderiranog Flutter framea400 do 800 ms na modernim uređajima, ispod 1500 ms na low-end
Vrijeme do interaktivnostiVrijeme dok prvi smisleni ekran nije upotrebljivIspod 1500 ms za tipične flowove
Startup jank frameoviIspušteni ili odgođeni frameovi u prve 1 do 2 sekundeManje od 3
Main thread long taskoviBlokirajući taskovi na Android main threadu ili iOS main run loopuNema taskova duljih od 100 ms tijekom startupa
Vrijeme routanja deep linkaVrijeme od pokretanja do ispravnog odredišnog ekranaIspod 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.

Bash
adb shell am force-stop com.example.app
adb shell am start -W -n com.example.app/.MainActivity

Vidjet ć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
Bash
flutter run --profile --trace-startup --verbose

Startup 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.

Dart
// 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.

Dart
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 await lance u main koji 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 compute ili dedicated isolate

Primjer: prebacite JSON parsiranje s UI isolatea.

Dart
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.

Dart
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 onAttachedToEngine ili 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

  1. 1
    Napravite inventuru plugina i kategorizirajte ih kao kritične za prvi ekran naspram onih koje možete odgoditi.
  2. 2
    Uklonite neiskorištene plugine i neiskorištene tranzitivne dependencyje.
  3. 3
    Nadogradite plugine. Mnogi problemi s performansama se s vremenom poprave.
  4. 4
    Odgodite inicijalizaciju tako da odgodite korištenje plugina ili koristite lazy singletonove u Dart-u.
Kategorija pluginaPotrebno prije prvog frameaStrategija odgode
Crash reportingNije potrebno za prvi frameInicijalizirajte nakon prvog framea, zatim dodajte user context
AnalyticsNeLokalno queueajte evente, pokrenite SDK nakon prve interakcije
Remote configRijetkoKoristite zadnje poznate vrijednosti, osvježite async
Deep linkingBrzo uhvatite intentRutirajte na placeholder, riješite nakon prvog framea
PaymentsNeInicijalizirajte 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:

  1. 1
    Odmah prikažite nativni splash.
  2. 2
    Odredite minimalni routing kontekst bez blokiranja.
  3. 3
    Renderirajte prvi Flutter frame s laganim placeholderom.
  4. 4
    Nakon prvog framea riješite deep link, auth, remote config i podatke.
  5. 5
    Navigirajte 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.

FazaVremenski budžetŠto se izvršavaPrikazani UI
Native splashOS-managedKreiranje app procesaNativni splash screen
Pre-runAppmanje od 150 msHvatanje initial intenta, postavljanje minimalnog DI-jaFlutter placeholder
Prvi Flutter frameASAPRender app shell-aSkeleton ili loading ruta
Post-frame init0 do 2000 msAuth provjera, refresh configa, warming cachevaPostupna hidratacija
Finalizacija ruteispod 2000 msNavigacija na deep link ili homeFinalni 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:

Dart
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 initState kroz više widgeta
  • ponavljane setState petlje 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 -W brojeve
  • 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.

Dart
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 Application i MainActivity budu lean
  • izbjegavajte teški sinkroni posao u onCreate

Na iOS-u:

  • neka didFinishLaunchingWithOptions bude 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#

SimptomVjerojatan uzrokPopravak koji možete primijeniti ovaj tjedan
Prvi frame je brz, ali aplikacija djeluje “zaglavljeno”Blokiranje deep link resolvanja ili auth provjeraPrvo renderirajte shell, zatim resolve i navigacija
Sporo samo pri prvom pokretanju nakon instalacijeKompilacija shadera, ekstrakcija asseta, migracijeSmanjite kompleksnost prvog ekrana, odgodite migracije, gdje je moguće zagrijte shadere
Android pokazuje duge sliceove na main threaduSinkroni I/O ili teški SDK initUklonite sync pozive, prebacite u background, odgodite SDK init
iOS launch time spikeovi u InstrumentsPosao u AppDelegateuPremjestite posao iz launch-a, lazy init servisa
Jank tijekom prijelaza sa splasha na homeDecode slika i težak prvi paintKoristite 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

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

·13 min čitanja

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.

FlutterRazvoj mobilnih aplikacijaAndroidiOSZadaci u pozadiniRaspoređivanjeSinkronizacija
Adrijan OmićevićPročitaj članak →
·15 min čitanja

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.

FlutterRazvoj mobilnih aplikacijaKupnje unutar aplikacijePretplateRevenueCatiOSAndroid
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 →