Flutter Performans Optimizasyonu: Kapsamlı Rehber

14 dakika okuma9 Şubat 2026Güncellendi: 9 Mar 2026
Flutter performanceFlutter optimizasyonFlutter hızlandırmaFlutter widget rebuildFlutter memoryFlutter profilingFlutter DevToolsFlutter best practicesFlutter lag fix

# Flutter Performans Optimizasyonu: Kapsamli Rehber

Performans calismasi her zaman veriye dayali olmalidir. Darbogazlarin nerede oldugunu tahmin etmek, bos yere zaman harcamaktir. Bu rehberde, takilmali bir Flutter uygulamasini akici bir 60fps (hatta 120fps) deneyime donusturecek teknikleri, araclari ve yaklasim degisikliklerini ele aliyorum.

Performans Neden Onemlidir

Kullanicilar fark eder. Arastirmalar, arayuz yanitindaki 100ms'lik bir gecikmenin hissedilir yavaslik yarattigi, 300ms'yi asan gecikmelerin ise uygulamanin bozuk oldugu izlenimini verdigi gosteriyor. Flutter'da tek bir dusuk kare bile render hattinin 16ms butcesini (60Hz'de) astigi anlamina gelir. Bunu kaydirma, sayfa gecisleri ve animasyonlarla carptiginizda, ozellikler ne kadar iyi olursa olsun uygulamaniz ucuz hissettirir.

En Kritik 10 Performans Katili (ve Cozumleri)

1. Tum Widget Agacini Yeniden Olusturmak

Uretim kod tabanlarinda en sik karsilastigim sorun budur. Buyuk bir widget agacinin tepesinde yapilan tek bir `setState` cagrisi, tum alt dallaarin yeniden olusturulmasini tetikler.

Yanlis yaklasim:

dart
class MyHomePage extends StatefulWidget {
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = class="code-number">0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          class=class="code-string">"code-comment">// Bu pahali widget, _counter her degistiginde yeniden olusturulur
          ExpensiveHeader(),
          ExpensiveProductList(),
          Text(class="code-string">'Sayac: $_counter'),
          ElevatedButton(
            onPressed: () => setState(() => _counter++),
            child: Text(class="code-string">'Artir'),
          ),
        ],
      ),
    );
  }
}

Cozum: Degisen kismi ayri bir widget'a cikarin.

dart
class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          const ExpensiveHeader(),
          const ExpensiveProductList(),
          class=class="code-string">"code-comment">// Sadece bu kucuk widget yeniden olusturulur
          CounterDisplay(),
        ],
      ),
    );
  }
}

class CounterDisplay extends StatefulWidget {
  const CounterDisplay({super.key});

  @override
  State<CounterDisplay> createState() => _CounterDisplayState();
}

class _CounterDisplayState extends State<CounterDisplay> {
  int _counter = class="code-number">0;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(class="code-string">'Sayac: $_counter'),
        ElevatedButton(
          onPressed: () => setState(() => _counter++),
          child: Text(class="code-string">'Artir'),
        ),
      ],
    );
  }
}

2. `const` Constructor Kullanmamak

`const` olmayan her widget, her build'de yeni bir ornek olarak olusturulur. Framework bunu onceki agacla karsilastirmak zorundadir. `const` ile Flutter, widget'in degismedigini bilir ve tamamen atlar.

dart
class=class="code-string">"code-comment">// Yanlis - her buildclass="code-string">'de yeni ornek
Container(
  padding: EdgeInsets.all(class="code-number">16),
  child: Text('Merhabaclass="code-string">'),
)

class=class="code-string">"code-comment">// Dogru - derleme zamani sabiti, rebuild sirasinda atlanir
const Padding(
  padding: EdgeInsets.all(class="code-number">16),
  child: Text('Merhaba'),
)

3. Tum Liste Elemanlarini Aninda Olusturmak

Yuzlerce eleman iceren `ListView(children: [...])` kullanmak, ekran disinda kalanlar dahil hepsinin ayni anda olusturulup yerlestirilmesi demektir.

dart
class=class="code-string">"code-comment">// Yanlis - class="code-number">10.000 elemanin hepsini aninda olusturur
ListView(
  children: List.generate(class="code-number">10000, (i) => ProductCard(product: products[i])),
)

class=class="code-string">"code-comment">// Dogru - sadece gorunen elemanlari ve kucuk bir tamponu olusturur
ListView.builder(
  itemCount: products.length,
  itemBuilder: (context, i) => ProductCard(product: products[i]),
)

4. Optimize Edilmemis Gorseller

4000x3000 piksellik bir gorseli 200x150'lik bir widget'a yuklemek muazzam miktarda bellek ve kod cozme suresi israf eder.

dart
class=class="code-string">"code-comment">// Yanlis - tam cozunurluk bellege yuklenir
Image.network(class="code-string">'https:class=class="code-string">"code-comment">//example.com/buyuk-foto.jpg')

class=class="code-string">"code-comment">// Dogru - sadece ihtiyaciniz kadari cozumlenir
Image.network(
  class="code-string">'https:class=class="code-string">"code-comment">//example.com/buyuk-foto.jpg',
  cacheWidth: class="code-number">400,  class=class="code-string">"code-comment">// Retina icin goruntuleme genisliginin class="code-number">2 kati
  cacheHeight: class="code-number">300,
)

5. UI Isolate Uzerinde Agir Islemler

Buyuk bir JSON yanitini parse etmek, gorsel islemek veya kriptografi islemleri calistirmak ana isolate'i bloke ederek render hattini durdurur.

dart
class=class="code-string">"code-comment">// Yanlis - arayuzu bloke eder
final data = jsonDecode(devJsonString);

class=class="code-string">"code-comment">// Dogru - ayri bir isolate'e yikle
final data = await compute(jsonDecode, devJsonString);

Daha karmasik isler icin `Isolate.spawn` veya `IsolatePool` kalibini kullanin.

6. Opacity ve ColorFiltered'in Asiri Kullanimi

`Opacity` widget'i ayri bir compositing katmani olusturur. Ic ice veya buyuk alt agaclarda kullanildiginda GPU performansini olturur.

dart
class=class="code-string">"code-comment">// Yanlis - tum alt agac icin offscreen buffer olusturur
Opacity(
  opacity: class="code-number">0.5,
  child: BuyukKarmasikWidget(),
)

class=class="code-string">"code-comment">// Daha iyi - sadece metin/ikon saydamligina ihtiyaciniz varsa
Text(class="code-string">'Merhaba', style: TextStyle(color: Colors.black.withOpacity(class="code-number">0.5)))

7. RepaintBoundary Kullanmamak

Repaint sinirlari olmadan, kucuk bir animasyon her karede tum ekranin yeniden boyanmasina neden olabilir.

dart
class=class="code-string">"code-comment">// Sik degisen widget'lari sarin
RepaintBoundary(
  child: AnimatedProgressIndicator(),
)

8. Pahali `build()` Metodlari

`build()` icinde filtreleme, siralama veya hesaplama yapmak, bunlarin her rebuild'de calisacagi anlamina gelir.

dart
class=class="code-string">"code-comment">// Yanlis - her build'de siralar
@override
Widget build(BuildContext context) {
  final sorted = List.of(items)..sort((a, b) => a.date.compareTo(b.date));
  return ListView.builder(
    itemCount: sorted.length,
    itemBuilder: (ctx, i) => ItemTile(sorted[i]),
  );
}

class=class="code-string">"code-comment">// Dogru - veri degistiginde sirala, sonucu onbellekle
late List<Item> _sortedItems;

void _updateItems(List<Item> items) {
  _sortedItems = List.of(items)..sort((a, b) => a.date.compareTo(b.date));
  setState(() {});
}

9. Shader Derleme Takilmalarini Goz Ardi Etmek

Flutter yeni bir shader ile ilk kez karsilastiginda, onu aninda derler ve gorunur bir takilma yaratir. Bu ozellikle ilk acilista belirgindir.

Cozum: Isinma sureci sirasinda `flutter run --profile --cache-sksl --purge-persistent-cache` kullanin, ardindan yakalanan shader'lari `--bundle-sksl-path` ile paketleyin.

10. Kisitlanmamis Intrinsic Hesaplamalari

`IntrinsicHeight` ve `IntrinsicWidth` en kotu durumda O(n^2) karmasikligindadir, cunku spekulatif bir yerlestirme gecisi yaparlar. Listelerde kullanmaktan kacinin.

dart
class=class="code-string">"code-comment">// ListView icinde yanlis
IntrinsicHeight(
  child: Row(
    crossAxisAlignment: CrossAxisAlignment.stretch,
    children: [LeftPanel(), RightPanel()],
  ),
)

class=class="code-string">"code-comment">// Daha iyi - sabit yukseklik veya LayoutBuilder kullanin
SizedBox(
  height: class="code-number">120,
  child: Row(
    children: [LeftPanel(), RightPanel()],
  ),
)

DevTools ile Profilleme Rehberi

Flutter DevTools en iyi dostunuzdur. Her performans sorusturmasinda izledigim pratik is akisini paylasiyorum.

Adim 1: Profile Modunda Calistirin

bash
flutter run --profile

Asla debug modunda profilleme yapmayin. Debug modu tum optimizasyonlari devre disi birakir ve yaniltici rakamlar verir. Profile modu, tipiik release gibi AOT derleme kullanir, ancak observatory ve DevTools baglantisini acik tutar.

Adim 2: DevTools'u Acin

Terminalinizde `v` tusuna basarak DevTools'u tarayicida acin veya IDE'nizin Flutter panelinden baslayin.

Adim 3: Performans Kaplama Katmani

Once performans kaplama katmanini etkinlestirin. Iki grafik gosterir:

  • **UI thread** (ust): Widget olusturma ve Dart kodu calistirma suresi
  • **Raster thread** (alt): GPU'ya birlestirme ve boyama suresi
  • UI cubugu kirmiziya donerse, Dart kodunuz cok yavas demektir. Raster cubugu kirmiziya donerse, cok fazla katmaniniz veya pahali boyama islemleriniz vardir.

    Adim 4: Zaman Cizelgesi Olaylari

    Performans sekmesine gecin. Sorunlu bir etkilesimi kaydedin (kaydirma, sayfa gecisi, animasyon). Sonra alev grafikini inceleyin:

  • Uzun `build` asamalari arayin. Pahali widget agaclarina isaret ederler.
  • Uzun `layout` asamalari arayin. Karmasik yerlestirme kisitlamalarina veya intrinsic hesaplamalara isaret ederler.
  • `paint` sivrilmeleri arayin. Eksik repaint sinirlarini gosterirler.
  • Adim 5: Widget Rebuild Sayaclari

    DevTools'ta "Track widget rebuilds" secenegini etkinlestirin. Bu, her widget'in uzerine etkilesiminiz sirasinda kac kez yeniden olusturuldugunu gosteren bir sayac bindirmesi yapar. Statik bir gorunumde birden fazla kez yeniden olusan herhangi bir widget supheli demektir.

    Adim 6: Tekrarlayin

    En buyuk sorunu duzeltin, yeniden profil cikarin, iyilesmeyi dogrulayin ve tekrarlayin. Her seyi ayni anda duzeltme durtusune karsi koyun. Hedefli, olculmus degisiklikler cok daha etkilidir.

    Bellek Sizintisi Tespiti ve Onlenmesi

    Flutter'da bellek sizintilari inceliklidir. Cop toplayici cogu seyi halleder, ancak belirli kalipler nesnelerin toplanmasini engeller.

    Yaygin Sizinti Kaynaklari

    Unutulan dinleyiciler ve abonelikler:

    dart
    class _MyWidgetState extends State<MyWidget> {
      late StreamSubscription _subscription;
    
      @override
      void initState() {
        super.initState();
        _subscription = someStream.listen((data) {
          setState(() { class=class="code-string">"code-comment">/* guncelle */ });
        });
      }
    
      class=class="code-string">"code-comment">// Bunu unutursaniz, abonelik bu State nesnesine
      class=class="code-string">"code-comment">// sonsuza kadar referans tutar
      @override
      void dispose() {
        _subscription.cancel();
        super.dispose();
      }
    }

    Dispose edilmeyen animasyon controller'lari:

    dart
    class _AnimatedWidgetState extends State<AnimatedWidget>
        with SingleTickerProviderStateMixin {
      late AnimationController _controller;
    
      @override
      void initState() {
        super.initState();
        _controller = AnimationController(
          vsync: this,
          duration: const Duration(milliseconds: class="code-number">300),
        );
      }
    
      @override
      void dispose() {
        _controller.dispose(); class=class="code-string">"code-comment">// Kritik!
        super.dispose();
      }
    }

    BuildContext yakalayan closure'lar:

    dart
    class=class="code-string">"code-comment">// Tehlikeli - closure context'i yakalar, context ise Element agacini tutar
    void _onTap(BuildContext context) {
      Future.delayed(Duration(seconds: class="code-number">5), () {
        class=class="code-string">"code-comment">// class="code-number">5 saniye icinde widget dispose edilirse, bu context gecersiz olur
        Navigator.of(context).push(...);
      });
    }
    
    class=class="code-string">"code-comment">// Daha guvenli - mounted kontrolu yapin
    void _onTap(BuildContext context) {
      Future.delayed(Duration(seconds: class="code-number">5), () {
        if (!mounted) return;
        Navigator.of(context).push(...);
      });
    }

    Bellek Profiler Kullanimi

  • DevTools'u acin ve **Memory** sekmesine gidin.
  • Etkilesimden once bir **snapshot** alin.
  • Islemi gerceklestirin (ornegin bir ekrana gidin ve geri donun, 5 kez).
  • Baska bir snapshot alin.
  • Buyuyen nesneleri gormek icin **diff** gorunumunu kullanin. Bir ekrana gidip geri donmek her seferinde yeni nesneler olusturuyor ve eskileri serbest birakmiyorsa, sizinti var demektir.
  • Ozellikle State nesnelerinin, Stream controller'larinin ve animasyon controller'larinin sayilarindaki buyumeyi arayin.

    Gercek Dunya Performans Kazanimlari

    Optimize ettigim bir uretim e-ticaret uygulamasinda, urun listeleme ekrani hizli kaydirma sirasinda 20fps'ye dusuyordu. Kok nedenler sunlardi:

  • Her `ProductCard`, ince bir solma efekti icin bir `Opacity` sarmalayiciya sahipti ve ekranda 50'den fazla compositing katmani olusturuyordu.
  • Urun gorselleri, 180 piksel genisligindeki kucuk resimler icin tam cozunurlukle (2000 piksel genislik) yukleniyordu.
  • Scaffold seviyesindeki bir `BlocBuilder`, her sepet guncellemesinde tum sayfayi yeniden olusturuyordu.
  • Hedefli duzeltmelerden sonra (Opacity'yi onceden hesaplanmis renklerle degistirmek, gorsellere `cacheWidth` eklemek ve `BlocBuilder`'i yalnizca sepet ikonu badge'ini saracak sekilde tasimak) kare sureleri 45ms'den 8ms'ye dustu ve bellek kullanimi 120MB azaldi. Tum surec, odakli profilleme ve adim adim degisikliklerle iki gun surdu.

    Ileri Teknikler

    Karmasik Kaydirma Icin Sliver'lar

    Heterojen kaydiriliabilir icerikleriniz oldugunda (basliklar, izgaralar, listeler), ic ice kaydiriliabilir widget'lar yerine `CustomScrollView` ile sliver'lari kullanin.

    dart
    CustomScrollView(
      slivers: [
        SliverAppBar(floating: true, title: Text(class="code-string">'Urunler')),
        SliverGrid(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: class="code-number">2,
          ),
          delegate: SliverChildBuilderDelegate(
            (context, i) => ProductCard(products[i]),
            childCount: products.length,
          ),
        ),
      ],
    )

    setState Yerine ValueListenableBuilder

    Ince taneli guncellemeler icin, `ValueListenableBuilder` yalnizca degere bagli olan alt agaci yeniden olusturur.

    dart
    final _counter = ValueNotifier<int>(class="code-number">0);
    
    class=class="code-string">"code-comment">// Sadece bu builderclass="code-string">'in alt agaci yeniden olusturulur
    ValueListenableBuilder<int>(
      valueListenable: _counter,
      builder: (context, value, child) {
        return Text('Sayac: $value');
      },
    )

    saveLayer Tetikleyicilerinden Kacinin

    Belirli widget'lar dolayli olarak `saveLayer` cagrisinda bulunur ve ekran disi tampon tahsis eder. En buyuk sorumlular: `Opacity`, `ShaderMask`, `ColorFiltered`, `ClipRRect` (`clipBehavior: Clip.antiAliasWithSaveLayer` ile). Bunlari bulmak icin DevTools'taki "highlight saveLayer" gecisini kullanin.

    Performans Kontrol Listesi

    Her surumden once bu kontrol listesini gecin:

  • [ ] Profile modunda (debug degil) kaydirma performansi profillendi
  • [ ] Normal kullanim sirasinda zaman cizelgesinde 16ms'yi asan kare yok
  • [ ] Tum listeler `.builder` veya sliver esdeqerlerini kullaniyor
  • [ ] Gorseller `cacheWidth`/`cacheHeight` belirtiyor
  • [ ] Mumkun olan her yerde `const` constructor kullaniliyor
  • [ ] `build()` metodlarinda agir hesaplama yok
  • [ ] Tum animasyon controller'lari `dispose()` icinde temizleniyor
  • [ ] Tum stream abonelikleri `dispose()` icinde iptal ediliyor
  • [ ] Animasyonlu/sik degisen widget'larin etrafina `RepaintBoundary` eklendi
  • [ ] Buyuk alt agaclari saran gereksiz `Opacity` widget'lari yok
  • [ ] Bellek snapshot'lari tekrarlanan gezinme sonrasi buyume gostermiyor
  • [ ] Kritik kullanici yollari icin shader isinmasi yapilandirildi
  • [ ] Uygulama boyutu `flutter build --analyze-size` ile analiz edildi
  • [ ] Release build, en dusuk seviye hedef cihazda test edildi
  • Sonuc

    Performans optimizasyonu tek seferlik bir is degildir. Bir disiplindir. Once olc, en buyuk darbogazy duzelt, iyilesmeyi dogrula ve tekrarla. Araclar mevcut. Kalipler iyi belgelenmis. Akici bir uygulamayi takilanlardan ayiran sey, performansi bir ozellik olarak onceliklendirme kararidir, sonradan eklenen bir dusunce degil.

    Flutter uygulamaniz icin uygulamali bir performans denetimi ister misiniz? Darbogazlari birlikte tespit edip cozelim.

    İlgili Makaleler

    Flutter Projeniz mi Var?

    iOS, Android ve web için yüksek performanslı Flutter uygulamaları geliştiriyorum.

    İletişime Geç