RenderObject seviyesinde repaint ve heap churn azaltarak flutter mobil uygulama geliştirme performansını yükseltin; somut kod, DevTools profili ve state management köprü örnekleriyle uygulamalı rehber.
Flutter RenderObject ile repaint azaltma ve state management stratejileri
Neden RenderObject tabanlı optimizasyon - kısa teknik gerekçe
RenderObject katmanına inmek, widget rebuild-okunu (Dart heap ve build-phase CPU) azaltır çünkü UI ağaçtaki küçük değişiklikleri sadece paint/semantics/baseline seviyesine indirgeme imkanı verir. Teknik gerekçe: widget.build her çağrıldığında yeni Widget/Element/RenderObject ataması veya alan güncellemesi yapılır; RenderObject seviyesinde markNeedsPaint/markNeedsLayout çağrılarıyla yalnızca paint veya layout aşaması tetiklenir. Ölçüm yöntemi: önce "flutter run --profile -d
Uygulamalı örnek: Picture cache kullanan RenderBox (kod)
Aşağıdaki örnek, sıkça yeniden çizilen ancak nadiren değişen vektörel grafikleri ui.Picture ile kaydedip yeniden kullanır; bu sayede Skia yeniden-record maliyeti ve Dart tarafında gereksiz alokasyonlar azalır. Kullanım: widget ağacında bu RenderBox'i sarın veya LeafRenderObjectWidget olarak kullanın. Profil adımı: önce DevTools Performance ile 60 frame kaydedin, sonra bu değişikliği uygulayıp tekrar kaydedin; export edilen JSON'da "Rasterizer" ve "Skia" sürelerindeki düşüşü ölçün.
import 'dart:ui' as ui;
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
class CachedShape extends LeafRenderObjectWidget {
final Color color;
CachedShape({required this.color});
@override
_RenderCachedShape createRenderObject(BuildContext context) => _RenderCachedShape(color);
@override
void updateRenderObject(BuildContext context, _RenderCachedShape renderObject) {
renderObject.color = color;
}
}
class _RenderCachedShape extends RenderBox {
Color _color;
ui.Picture? _picture;
_RenderCachedShape(this._color);
set color(Color c) {
if (c == _color) return;
_color = c;
_picture?.dispose();
_picture = null; // force re-record only when color actually changed
markNeedsPaint();
}
@override
void performLayout() {
size = constraints.constrain(Size(200, 200));
}
void _ensurePicture() {
if (_picture != null) return;
final recorder = ui.PictureRecorder();
final canvas = Canvas(recorder);
final paint = Paint()..color = _color;
// expensive vector drawing once
canvas.drawRRect(RRect.fromRectXY(Offset.zero & size, 12, 12), paint);
// add more complex paths, shadows, gradients here
_picture = recorder.endRecording();
}
@override
void paint(PaintingContext context, Offset offset) {
_ensurePicture();
context.canvas.save();
context.canvas.translate(offset.dx, offset.dy);
context.canvas.drawPicture(_picture!);
context.canvas.restore();
}
@override
void dispose() {
_picture?.dispose();
super.dispose();
}
}
Teknik not: ui.Picture komutları native Skia tarafında tutulur; yeniden record yerine drawPicture çağırmak GPU tarafında daha ucuz olabilir ve Dart heap'te geçici Path/Canvas nesneleri oluşturmaktan kaçınır. Profil önerisi: DevTools Memory -> Take Heap Snapshot -> filter by Class (ui.Picture, Path) ile alokasyon farkını görün.State management ile repaint hedefleme: ValueListenable, Selector ve RenderObject köprüsü
State management katmanını doğrudan RenderObject ile bağlamak, widget rebuild'lerini minimize eder. Örnek yaklaşım: global state provider (Provider veya Riverpod) yerine, sık değişen küçük alanlar için ValueNotifier kullanın ve RenderObject içinde listener ekleyin; böylece sadece paint tetiklenir, build aşaması atlanır. Aşağıda ValueListenable'ı RenderObject'e bağlayan güvenli bir örnek (attach/detach yönetimi) var. Profil yöntemi: DevTools -> Performance -> Record, ayrıca DevTools -> Inspect -> Widget rebuild count özelliği ile rebuild sayısını karşılaştırın.
class NotifierRenderWidget extends LeafRenderObjectWidget {
final ValueListenable colorListenable;
NotifierRenderWidget({required this.colorListenable});
@override
_NotifierRenderObject createRenderObject(BuildContext context) => _NotifierRenderObject(colorListenable);
@override
void updateRenderObject(BuildContext context, _NotifierRenderObject renderObject) {
renderObject.colorListenable = colorListenable;
}
}
class _NotifierRenderObject extends RenderBox {
ValueListenable? _listenable;
_NotifierRenderObject(this._listenable);
void _onChange() {
// sadece paint gerekiyorsa
markNeedsPaint();
}
set colorListenable(ValueListenable? n) {
if (identical(n, _listenable)) return;
_detach();
_listenable = n;
_attach();
markNeedsPaint();
}
void _attach() { _listenable?.addListener(_onChange); }
void _detach() { _listenable?.removeListener(_onChange); }
@override
void attach(PipelineOwner owner) { super.attach(owner); _attach(); }
@override
void detach() { _detach(); super.detach(); }
@override
void performLayout() { size = constraints.constrain(const Size(100, 100)); }
@override
void paint(PaintingContext context, Offset offset) {
final color = _listenable?.value ?? const Color(0xFF000000);
context.canvas.drawRect(offset & size, Paint()..color = color);
}
}
Best practice: Provider paketinde Selector kullanarak widget rebuild'leri azaltabilirsiniz; ama daha ince kontrol istiyorsanız yukarıdaki gibi doğrudan RenderObject'e listen yapın. Neden: widget rebuild'leri Element and Widget allocation'lara yol açar, bunlar Dart heap'te kısa ömürlü nesne üretir ve GC baskısını arttırır - RenderObject listener ile sadece paint çalışır.Heap churn azaltma: ByteBuffer pool, const ve allocation tracing
Heap churn'u azaltmak için: 1) kısa ömürlü list/string oluşturmayı azaltın (const, List.filled, reuse), 2) büyük byte buffer'ları pool'layın (örneğin resim işleme pipeline'ında), 3) dart:developer Timeline event'leri ile kritik yolları işaretleyin. Profil aracı/akış: flutter run --profile -d
Ek tavsiye: büyük resim decode/resize işlerini isolate'a taşımak için compute veya package:isolate kullanın ve Dart DevTools -> Memory snapshot ile izleyin. Not: dart:ui tipleri çoğunlukla native bellekte yer tutar; ui.Picture reuse etmek Dart heap'te Path/Canvas geçicilerini azaltır ancak yine native bellekte kaynak tutar - bu yüzden picture.dispose() yönetimini unutmayın. Allocation tracing için ayrıca "DevTools -> Memory -> Allocation profile -> Record (short)" kullanın ve en çok hangi fonksiyonların alokasyon yaptığını görün.class BytePool {
final int bufferSize;
final List
Profil stratejisi: ölçümler, metrikler ve karşılaştırma adımları
Somut profil planı ve metrikler: 1) Hazırlık: uygulamayı "flutter run --profile -d
İlgili Eğitim
Sık Sorulan Sorular
Flutter state management kullanırken rebuild'leri nasıl minimize ederim?
Sık değişen küçük state'ler için Provider/Riverpod üstünde Selector veya ValueListenable/ValueNotifier kullanın; daha da ince kontrol için doğrudan RenderObject'e listener ekleyin (attach/detach yönetimi). Ölçüm: DevTools -> Performance ile widget rebuild count ve build-time p95 karşılaştırması yapın.
Flutter mobil uygulama geliştirme sırasında heap churn'ı nasıl tespit ederim?
flutter run --profile -d
Dart programlama eğitimi alan bir ekip için hangi pratikleri önerirsiniz?
Ekiplere öneri: 1) performans odaklı kod incelemelerinde widget rebuild ve heap allocation inceleyin, 2) DevTools kullanımını öğretin (Performance, Memory, CPU Profiler), 3) izolasyon için compute kullanımı ve buffer pooling stratejilerini egzersiz haline getirin. Somut başlangıç görevi: uygulamada 60 frame profil alıp p95 build/raster süresini azaltacak bir RenderObject cache uygulayın ve farkı raporlayı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.



