Bu makale, Flutter mobil uygulama geliştirme bağlamında Element/Widget reuse ile element churn azaltma, ölçme ve optimize etme adımlarını gösterir. Flutter state management ve performans profil yöntemleriyle somut örnekler içerir; flutter eğitimi ve dart programlama eğitimi içerikleriyle uyumlu pratik ipuçları sunar.
Flutter Element ve Widget Reuse: Element Churn Azaltma ile Profil Rehberi
Element churn nedir, nasıl ölçülür? (profil aracı ve metrikler)
Element churn - Element ağacındaki gereksiz oluşturma/işten çıkarma döngüleri - uygulamada CPU, GC baskısı ve jank oluşturur. Ölçmek için somut adımlar: uygulamayı profile modda çalıştırın, Flutter DevTools > Performance > Timeline ile 30 saniyelik senaryoyu kaydedin ve 'Widget build' / 'Rebuild' olaylarını sayın. Komut örneği:
flutter run --profile --trace-skia Daha kesin ölçüm için DevTools'tan 'Export trace' alın ve JSON içindeki "TimelineEvents" içinde 'Widget build' olaylarını sayın. Bu iş akışının neden işe yaradığını açıklamak gerekirse: profile mod, JIT optimizasyonlarından arındırılmış, gerçek zamanlı raster/CPU iş yükünü gösterir; timeline olayları ise hangi Frame'de kaç build/paint/repaint gerçekleştiğini doğrudan verir.Hızlı yerel sayım için uygulamaya aşağıdaki instrumentation'u ekleyin; bu örnek her build çağrısında sayaç arttırır ve debugPrint ile loglar. Somut kod:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class BuildCounter extends StatefulWidget {
final Widget child;
BuildCounter({required this.child});
@override
_BuildCounterState createState() => _BuildCounterState();
}
class _BuildCounterState extends State {
int builds = 0;
@override
Widget build(BuildContext context) {
builds++; // her build sayılıyor
if (kDebugMode && builds % 50 == 0) debugPrint('BuildCounter: $builds');
return widget.child;
}
}
Bu sayım, DevTools'taki timeline ile çapraz doğrulama yapmanıza olanak verir: timeline'da görülen 'Widget build' sayısı ile burada kaydedilen builds sayısını karşılaştırın.Liste ve koleksiyonlarda Element reuse: Key, sliver ve builder stratejileri
Liste yapıları, elemanların yeniden kullanılmasında en kritik alanlardır. Yanlış key kullanımı veya key eksikliği, liste elemanlarının yeniden oluşturulmasına (recreate) ve element churn'e yol açar. Somut örnek: ListView.builder ile tarihsel veriler sunan bir liste. Aşağıdaki kodda uygun Key kullanımını ve cacheExtent ayarını gösteriyorum:
ListView.builder(
key: PageStorageKey('infinite_list'), // route değişiminde state korur
cacheExtent: 500.0, // eğer görsel olarak büyük öğeler varsa ön yükleme
itemCount: items.length,
itemBuilder: (context, i) {
final item = items[i];
return ListTile(
key: ValueKey(item.id), // stable identity: Element reuse sağlar
title: Text(item.title),
);
},
) Neden ValueKey? Bir öğenin runtime kimliği varsa (id), ValueKey elementin yeniden kullanımı için en hızlı ve en düşük bellek maliyetli çözümdür; GlobalKey sadece widget'ın State'ini korur ama bellek ve rekonstrüksiyon maliyeti yüksektir.Sliver'lar için SliverChildBuilderDelegate kullanırken dikkat edilecek parametreler: addAutomaticKeepAlives ve addRepaintBoundaries. Eğer alt öğeler kendi repaint boundary'lerine ihtiyaç duymuyorsa addRepaintBoundaries: false ile gereksiz compositing katmanlarını azaltabilirsiniz. Örnek:
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => MyItemWidget(key: ValueKey(items[index].id)),
childCount: items.length,
addRepaintBoundaries: false,
),
) Bu değişikliğin etkisini ölçmek için baseline trace alın, sonra addRepaintBoundaries: false yapıp tekrar ölçün; DevTools'ta raster thread CPU zamanında ve 'Layer' sayısında azalma göreceksiniz.Repaint, compositing ve layer stratejileri: RepaintBoundary, raster cache ve GPU upload
RepaintBoundary kullanımı, büyük statik kısımları ayrı katmana çıkararak tekrar rasterizasyon maliyetini düşürür. Ancak aşırı kullanım compositing katmanı fazlalığına ve daha fazla GPU upload'a sebep olabilir. Profil adımları: DevTools > Performance > 'Raster' ve 'GPU' timeline'ını izleyin; ayrıca 'Profile' modda bir senaryo kaydedip exported trace'teki 'Raster' sürelerini karşılaştırın. Kullanım örneği:
RepaintBoundary(
key: ValueKey('chart'),
child: CustomPaint(painter: LargeChartPainter(data)),
) Neden bu işe yarar? CustomPaint pahalıysa, RepaintBoundary sadece veri değiştiğinde o alt ağacı rasterize eder; bu da sürekli animasyon yapan bir üst widget'ın repaint maliyetini azaltır.Edge case: RepaintBoundary içindeki child boyutu değişiyorsa (ör. responsive resimler), layer yeniden oluşturulur ve maliyet artar. Bu durumda layer cache yerine cacheExtent veya offscreen resampling (image resize) kullanın. Örnek olarak Image widget için paket 'cached_network_image' yerine 'flutter_image_compress' ile sunucu tarafında uygun boyuta gönderim veya client-side decode resize yapmak (decodeImageFromList) daha etkili olabilir. Ölçüm olarak: aynı senaryoyu önce RepaintBoundary ile sonra decode-resize ile çalıştırın ve DevTools'ta 'Texture upload' ve 'Raster' sürelerini karşılaştırın.
State management ile granüler rebuild: Provider/Selector ve ValueNotifier örneği
Flutter state management seçimleri doğrudan rebuild sınırınızı belirler. setState ile geniş kapsamlı rebuild'ler yapmak yerine ValueNotifier, Provider + Selector ya da Riverpod'un select API'si ile ince taneli (granüler) rebuild gerçekleştirin. Aşağıdaki örnek Provider + Selector kullanarak sadece title alanı değiştiğinde ilgili öğenin yeniden çizilmesini sağlıyor:
class Item { final int id; String title; Item(this.id, this.title); }
class ItemsModel extends ChangeNotifier {
final List- _items;
ItemsModel(this._items);
void updateTitle(int id, String newTitle) {
final i = _items.indexWhere((e) => e.id == id);
if (i == -1) return;
_items[i].title = newTitle;
notifyListeners();
}
}
// tüketim
Selector
(
selector: (_, model) => model.items[index].title,
builder: (_, title, __) => Text(title),
)
Profiling yöntemi: önce aynı senaryo için setState ile implementasyonu çalıştırın, timeline'ı kaydedin; sonra Selector kullanarak tekrar kaydedin. DevTools'ta 'Widget build' sayısında ve main thread CPU süresinde düşüş gözleyeceksiniz.Pratik pitfall: Selector ya da select kullanırken selector fonksiyonunun referans stabil olmasına dikkat edin; eğer selector yeni bir obje döndürüyor (ör. List.from(...)) her rebuild'de eşitlik bozulur ve Selector işe yaramaz. Bunun yerine immutable/deterministic dönüşler veya primitive değerlere map etmek en sağlam yaklaşımdır. Ayrıca cross platform mobil uygulama geliştirme hedefliyorsanız, State management çözümünün platforma özel lifecycle farklarını (iOS background/Android lifecycle) doğru yönettiğinden emin olun.
Adım adım optimizasyon checklist ve ölçülebilir karşılaştırma
1) Baseline: profil modda 30s senaryo kaydedin:
flutter run --profile
# DevTools: Performance > Record 30s senaryo > Export trace
2) Instrumentation: BuildCounter ile her widget tipi için build sayısını alın (ilk bölümdeki örnek). 3) Değişiklik: Key/Selector/RepaintBoundary/Sliver ayarlarını uygulayın. 4) A/B testi: aynı senaryoyu tekrar kaydedip exported trace'i karşılaştırın. Karşılaştırma için aranan metrikler: toplam 'Widget build' sayısı, main thread CPU ms, raster thread ms, GC tetikleme sayısı. Neden bu metrikler? Widget build sayısı rebuild yükünü, main thread CPU uygulama mantığını ve layout/painter maliyetini, raster thread GPU upload/raster zamanını, GC ise heap churn'i doğrudan gösterir.Örnek bir karşılaştırma tablosu notu (ölçülen değerler uygulamaya göre değişir):
- Baseline: Widget build = 1200, main CPU = 1800ms, raster = 900ms, GC = 6
- Opt: Widget build = 320, main CPU = 650ms, raster = 420ms, GC = 2
İlgili Eğitim
Sık Sorulan Sorular
Flutter'da element churn nasıl tespit edilir ve hangi araçlarla ölçülür (flutter mobile uygulama geliştirme için)?
Element churn'u tespit etmek için en pratik yol profile modda senaryo kaydı yapmaktır: terminalde "flutter run --profile --trace-skia" çalıştırın, sonra Flutter DevTools > Performance > Record ile timeline kaydını alın. Export edilen trace JSON içindeki 'Widget build' ve 'Frame' olaylarını sayarak churn'u nicelendirirsiniz. Ayrıca uygulamaya build sayaçları (debugPrint) ekleyerek DevTools'taki sayılarla çapraz doğrulama yapın.
Flutter state management seçimi element reuse ve rebuild'leri nasıl etkiler (flutter state management pratik tavsiye)?
State management tercihleri, hangi widget'ların notifier tetiklediğinde yeniden inşa edileceğini belirler. setState ile geniş kapsamlı değişiklikler tüm subtree'yi yeniden inşa ederken, Provider + Selector veya ValueNotifier ile yalnızca ilgili widget'lar yeniden inşa edilir. Örnek: Provider + Selector kullanıp selector fonksiyonunu primitive değer döndürecek şekilde tasarlarsanız rebuild sayısını dramatik şekilde düşürebilirsiniz. Profil ile önce/sonra ölçümü zorunludur.
Cross platform mobil uygulama geliştirme bağlamında GlobalKey kullanırken hangi performans riskleri vardır (flutter kursu katılımcıları için)?
GlobalKey State objesini korur ama aynı zamanda elementin lifecycle ve lookup maliyetini artırır; çok sayıda GlobalKey bellek ve CPU maliyeti doğurur. GlobalKey kullanırken sahiplik (widget tree içindeki benzersizliği) ve gereklilik sorgulanmalı; çoğu durumda ValueKey/UniqueKey veya state lifting ile GlobalKey'a ihtiyaç kalmaz. Deneysel doğrulama için bir senaryo oluşturup DevTools'ta heap kullanım ve GC sayısını karşılaştırın.
AI / LLM Discovery
Bu makale Opendart Akademi Flutter eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.



