• 24.09.2026 21:31:11
  • Admin Admin

Flutter mobil uygulama geliştirmede raster cache, RepaintBoundary ve daraltılmış rebuild alanlarıyla raster işini ölçerek azaltın. DevTools üzerinden UI ve Raster thread bulgularını karşılaştırılabilir hale getirin.

Flutter Mobil Uygulama Geliştirmede Raster Cache ve Repaint Sınırları

Flutter mobil uygulama geliştirmede raster cache nasıl karar verir?

Raster cache, bir widget'ı otomatik olarak 'hızlandıran' kalıcı bir özellik değildir. Engine, karmaşık bir layer alt ağacının art arda karelerde değişmeden kaldığını ve örneğin transform ile yeniden kullanıldığını gözlemlediğinde bu alt ağacı bir texture'a rasterize etmeyi deneyebilir. Sonraki karelerde çok sayıdaki draw call yerine tek bir texture çizimi yapılır. Bu nedenle zoom, pan veya animasyon sırasında sabit kalan karmaşık vektör haritalarda anlamlıdır; her karede renk, path veya child listesi değişen dashboard kartında cache her karede geçersiz kalacağından ek raster maliyeti ve GPU bellek baskısı üretir.

İlk teşhisi debug modda değil, gerçek engine davranışına daha yakın olan profile modda yapın. flutter run --profile ile uygulamayı açın, DevTools Performance ekranında 20-30 saniyelik aynı pinch-zoom senaryosunu kaydedin ve Frame chart'taki UI ile Raster sürelerini ayrı inceleyin. 60 Hz cihazda 16.67 ms, 120 Hz cihazda 8.33 ms kare bütçesidir. Raster çubukları bütçeyi aşıyor, UI çubukları düşük kalıyorsa önce build optimizasyonu değil compositing, clipping, opacity ve çizim sayısı araştırılmalıdır.

Bir flutter eğitimi veya flutter kursu içinde sık görülen hata, her pahalı widget'a gelişigüzel RepaintBoundary eklemektir. RepaintBoundary bir yeniden çizim sınırı ve ayrı compositing layer oluşturur; cache tahsisini garanti etmez. Çok sayıda küçük boundary layer ağacını, texture tahsislerini ve compositing işini büyütür. DevTools'ta Render Tree ve Performance timeline ile sadece uzun süre sabit kalan, pahalı çizilen alt ağaçlarda sınır koyduğunuzu doğrulayın.

RepaintBoundary yerleşimi: statik sahneyi dinamik durumdan ayırın

Aşağıdaki örnekte harita path'leri ve gradient sabittir, yalnızca seçili nokta değişir. Boundary, sabit CustomPaint alt ağacını sarar; marker ise boundary dışında kalır. Marker'ı aynı boundary içine koymak, her konum güncellemesinde cache'i geçersizleştirir ve asıl amacı bozar.

class MapPreview extends StatelessWidget {
  const MapPreview({super.key, required this.selected});

  final ValueListenable<Offset?> selected;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        const RepaintBoundary(
          child: CustomPaint(
            size: Size.expand,
            painter: StaticMapPainter(),
            isComplex: true,
            willChange: false,
          ),
        ),
        ValueListenableBuilder<Offset?>(
          valueListenable: selected,
          builder: (context, point, child) {
            if (point == null) return const SizedBox.shrink();
            return Positioned(
              left: point.dx - 8,
              top: point.dy - 8,
              child: const _Marker(),
            );
          },
        ),
      ],
    );
  }
}

class StaticMapPainter extends CustomPainter {
  const StaticMapPainter();

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..color = const Color(0xff172033);
    canvas.drawRect(Offset.zero & size, paint);
    // Gerçekte önceden parse edilmiş Path listeleri burada çizilir.
  }

  @override
  bool shouldRepaint(covariant StaticMapPainter oldDelegate) => false;
}

isComplex: true ve willChange: false, CustomPaint'in layer'ı için engine'e ipucu verir; zorunlu cache komutu değildir. Kritik incelik şudur: shouldRepaint false dönerken painter'ın dışarıdan okuduğu mutable bir listeyi değiştirirseniz framework repaint istemez ve ekranda eski veri kalabilir. Painter girdilerini immutable yapın veya değişim olduğunda yeni delegate üretip alan bazlı karşılaştırma yapın. Bu ayrım, dart programlama eğitimi sırasında anlatılan immutable state ilkesinin doğrudan render doğruluğuna yansıdığı noktalardan biridir.

Flutter state management ile cache geçersizleştirmesini sınırlama

flutter state management katmanında tek bir ekran state'iyle tüm sahneyi dinlemek, boundary doğru konulsa bile parent widget'ın build maliyetini büyütür ve yanlışlıkla painter girdilerini yeniden üretir. Bloc, Riverpod veya Provider seçiminden bağımsız prensip aynıdır: render alt ağacının ihtiyaç duyduğu en küçük selector'u dinleyin. Aşağıdaki Riverpod örneğinde rota verisi ile seçili marker ayrı provider'lardır; marker değiştiğinde RoadLayer yeniden build edilmez.

final roadDataProvider = Provider<List<Road>>((ref) {
  return ref.watch(mapRepositoryProvider).roads;
});

final selectedMarkerProvider = StateProvider<Offset?>((ref) => null);

class MapScreen extends ConsumerWidget {
  const MapScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final roads = ref.watch(roadDataProvider);
    final marker = ref.watch(selectedMarkerProvider);

    return Stack(
      children: [
        RepaintBoundary(child: RoadLayer(roads: roads)),
        MarkerLayer(position: marker),
      ],
    );
  }
}

Bu kodda roadDataProvider aslında repository içindeki mutable listeyi döndürüyorsa provider yeniden hesaplanmasa bile çizilen Path'ler değişebilir. Listeleri immutable snapshot olarak yayınlayın ve veri güncellemesinde yeni referans üretin. Riverpod kullanılıyorsa marker katmanını ayrıca bir ConsumerWidget yapmak, parent'ın marker değişiminde build edilmesini de engeller. DevTools'taki Widget rebuild stats veya Inspector'daki rebuild vurgusu ile önce tek marker hareketinde hangi widget'ların rebuild olduğunu kaydedin, ardından selector ayrımından sonra aynı hareketi tekrar çalıştırın.

DevTools ile önce-sonra raster ölçümü ve kare yüzdeliği

Ölçümü 'akıcı hissettiriyor' düzeyinde bırakmayın. Aynı fiziksel cihazda, aynı profile build'de, aynı 30 saniyelik pan ve zoom kaydını en az beş kez alın. DevTools Performance kaydında Raster thread için medyan ve p95 kare süresini, bütçeyi aşan kare sayısını ve en pahalı event'i not edin. Örneğin değişiklik öncesinde p95 Raster 13.4 ms ve 120 Hz bütçesini aşan 48 kare, boundary ve state ayrımından sonra p95 6.7 ms ve 5 aşım görülebilir. Bu sonuç yalnızca aynı veri seti, ekran çözünürlüğü ve gesture tekrarında anlamlıdır.

Uygulama içinden de ham FrameTiming örnekleri toplayarak regresyon testi için sayısal veri üretebilirsiniz. Bu listener'ı debug log yerine profile test oturumunda CSV veya telemetry altyapınıza gönderin; raster süreleri mikrosaniye cinsindendir.

import 'dart:ui';
import 'package:flutter/scheduler.dart';

class FrameMetrics {
  final List<int> rasterMicros = [];

  void start() {
    SchedulerBinding.instance.addTimingsCallback(_onTimings);
  }

  void stop() {
    SchedulerBinding.instance.removeTimingsCallback(_onTimings);
  }

  void _onTimings(List<FrameTiming> timings) {
    for (final timing in timings) {
      rasterMicros.add(timing.rasterDuration.inMicroseconds);
    }
  }

  double percentile95Ms() {
    final sorted = [...rasterMicros]..sort();
    final index = ((sorted.length - 1) * 0.95).round();
    return sorted[index] / 1000.0;
  }
}

Önemli edge case: ilk birkaç gesture turunu ölçüme katmayın. Shader derleme, image decode, font yükleme ve cache ısınması ilk etkileşimde farklı bir maliyet oluşturabilir. Soğuk başlangıç ve ısınmış durum için iki ayrı senaryo tutun. Ayrıca Flutter sürümü, cihaz GPU sürücüsü ve ekran yenileme hızı değiştiğinde sayıları doğrudan kıyaslamayın; değişiklik başına aynı cihazdaki önce-sonra farkı karar vermek için daha güvenilir bir sinyaldir.

Cross platform mobil uygulama geliştirmede cache sınırlarının tuzakları

cross platform mobil uygulama geliştirme hedefinde Android ve iOS'ta aynı widget ağacı çalışsa da GPU bellek sınırı, texture çözünürlüğü ve driver davranışı cihazlar arasında farklıdır. Tam ekran 1440 x 3200 RGBA texture yaklaşık 1440 * 3200 * 4 = 18.4 MB ham bellek tüketir; birden fazla büyük cached layer, düşük bellekli cihazlarda texture eviction ve tekrar rasterizasyon döngüsü doğurabilir. Bu yüzden boundary adaylarını tam ekran yerine sabit, karmaşık ve sınırlı alanlı bölgelere indirin.

Opacity, ClipPath ve ImageFilter gibi efektler layer üretir. Statik bir harita boundary içinde olsa bile parent her karede değişen opacity uyguluyorsa sonucu yeniden compositlemek gerekir; ayrıca büyük blur alanları offscreen buffer maliyetini artırır. DevTools timeline'da uzun süren Canvas::saveLayer benzeri olaylar veya Raster thread yoğunluğu görürseniz önce blur yarıçapını ve efekt alanını küçültün. Bir BackdropFilter'ı tüm sayfaya koymak yerine küçük alanı ClipRect ile sınırlamak somut bir ilk adımdır.

Bu yaklaşım flutter kursu örneklerinde kullanılan basit animasyonlardan daha gerçekçidir: cache'i açıp kapatan bir bayrak aramak yerine, layer sınırını state değişim sıklığına göre kurar ve sonuçları FrameTiming ile doğrularsınız. Son kontrol olarak düşük donanımlı gerçek cihazda, 60 Hz ve yüksek yenileme hızlı cihazlarda aynı kayıt protokolünü çalıştırın; emulator GPU pipeline'ı raster darboğazını gizleyebilir.

İlgili Eğitim

Flutter Eğitimi

Sık Sorulan Sorular

Flutter mobil uygulama geliştirmede RepaintBoundary ne zaman kullanılmalı?

Bir alt ağaç pahalı çiziliyor, birkaç kare boyunca görsel girdisi değişmiyor ve parent veya kardeş animasyonlarından ayrıştırılabiliyorsa adaydır. Kararı flutter run --profile ve DevTools Frame chart ile verin: Raster thread darboğazı yoksa RepaintBoundary eklemek için ölçülebilir gerekçe yoktur.

Flutter state management cache invalidation sorununu nasıl azaltır?

State'i statik render verisi ve sık değişen etkileşim verisi olarak ayırın. Örneğin yollar için immutable List<Road> snapshot'ı, marker için ayrı StateProvider<Offset?> kullanın. Marker güncellenirken yol painter'ının girdisi değişmez, dolayısıyla hem rebuild alanı hem de olası raster cache geçersizliği sınırlanır.

Flutter eğitimi için raster cache performansı nasıl ölçülür?

Aynı cihazda profile modda en az beş tekrar yapın. DevTools'ta Raster p95 süresini ve kare bütçesi aşımını kaydedin; ardından yalnızca bir değişiklik, örneğin marker'ı RepaintBoundary dışına taşıma, uygulayın ve aynı gesture kaydını tekrarlayın. İlk turdaki shader ve image ısınma maliyetini ayrı raporlayın.

Dart programlama eğitimi sırasında CustomPainter shouldRepaint için hangi kural uygulanmalı?

Painter'ın çizimini etkileyen tüm girdiler immutable alanlar olmalı ve shouldRepaint bu alanları karşılaştırmalıdır. Mutable bir listeyi yerinde değiştirip false döndürmek ekranda bayat piksel bırakır; her veri güncellemesinde yeni liste veya sürüm numarası üretmek bu hatayı önler.

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.

Opendart Akademi llms.txt