Flutter mobil uygulama geliştirme sürecinde ilk ekran geçişlerindeki shader jank sorununu ölçmeyi, Skia ve Impeller farklarını ayırmayı, hedefli shader warm-up uygulamayı ve regresyon testi kurmayı ele alır.
Flutter Mobil Uygulama Geliştirmede Shader Jank Isınma Stratejileri
Flutter mobil uygulama geliştirmede shader jank belirtilerini ölçmek
Shader jank, bir widget rebuild sorunu gibi görünse de çoğu zaman ilk kez kullanılan bir grafik pipeline'ının GPU sürücüsünde derlenmesinden kaynaklanır. Belirtiyi doğrulamak için uygulamayı debug yerine profile modda, fiziksel cihazda ve tekrarlanabilir bir kullanıcı akışıyla çalıştırın. Debug moddaki Dart VM servis maliyeti ve assertion kontrolleri frame sürelerini bozduğu için karar vermek için uygun değildir. Her hedef cihazda aynı rotayı en az 10 kez açın; ilk geçiş ile sonraki geçişlerin raster sürelerini ayrı kaydedin.
flutter run --profile
flutter devtoolsDevTools Performance ekranında frame chart'ta 16.67 ms üstündeki frame'leri 60 Hz cihaz için, 8.33 ms üstündekileri 120 Hz cihaz için işaretleyin. UI thread uzunken widget build, layout veya Dart hesaplaması şüphelidir; raster thread tek seferlik sıçrayıp sonraki aynı etkileşimlerde normale dönüyorsa shader veya pipeline hazırlığı daha olasıdır. Bu ayrımı yapmadan yapılan flutter state management refactor'ları, GPU tarafında derlenen pipeline'ı değiştirmediği için ilk geçişteki takılmayı ortadan kaldırmaz.
İz kaydını daha ayrıntılı incelemek için Skia tabanlı çalıştırmalarda Skia event'lerini açın ve aynı gesture akışını tekrar edin. Elde edilen timeline'da shader compile ile frame spike'ın aynı zaman aralığında olup olmadığını kontrol edin; yalnızca CPU örnekleme grafiğine bakmak yeterli değildir.
flutter run --profile --trace-skiaFlutter eğitimi kapsamında Skia ve Impeller için doğru teşhis
Flutter eğitimi içeriklerinde sık yapılan hata, her backend için eski SKSL paketleme tarifini koşulsuz uygulamaktır. Skia, runtime shader derlemelerini azaltmak için SKSL capture ve bundle akışından faydalanabilir. Impeller ise birçok pipeline bileşenini önceden hazırlayan farklı bir renderer mimarisine sahiptir; dolayısıyla SKSL dosyası üretmek, Impeller etkin bir hedefte aynı mekanizmayı çözmez. Önce cihazdaki trace ile renderer davranışını doğrulayın, sonra seçtiğiniz hedef platformun build çıktısında gerçekten kullanılan renderer'a göre aksiyon alın.
Android tarafında Flutter timeline'ını işletim sistemi zamanlayıcı ve GPU olaylarıyla eşlemek gerektiğinde Perfetto kaydı alın. Aynı anda birden fazla CPU çekirdeğinin frekans düşürmesi veya ana thread'in başka bir süreç tarafından preempt edilmesi de shader compile'a benzeyen bir raster gecikmesi üretebilir. Aşağıdaki kayıt, `sched`, `freq`, `gfx` ve `view` kategorilerini 15 saniye boyunca toplar; dosyayı çekip ui.perfetto.dev üzerinde Flutter trace zaman damgasıyla karşılaştırın.
adb shell perfetto -o /data/misc/perfetto-traces/flutter_shader.perfetto -t 15s sched freq gfx view
adb pull /data/misc/perfetto-traces/flutter_shader.perfettoÖnce-sonra karşılaştırmasında ortalama frame süresini kullanmayın. Örneğin bir rota 300 frame üretiyor, sadece ilk 3 frame 70 ms sürüyorsa ortalama kabul edilebilir görünebilir ama kullanıcı geçişte üç ayrı takılma hisseder. Her cihaz sınıfı için cold route transition P95 raster süresini, 16.67 ms üzerindeki raster frame sayısını ve ilk etkileşimdeki maksimum raster süresini CI artefact'ına yazın.
Hedefli ShaderWarmUp ile ilk rota maliyetini taşımak
Birçok uygulamada başlangıç ekranında hiç görünmeyen yüzlerce efekt için warm-up çalıştırmak ters etki yaratır: cold start CPU ve GPU işini büyütür, giriş ekranını geciktirir ve düşük bellekli cihazlarda kaynak baskısını artırır. Bunun yerine ilk 30 saniyede gerçekten kullanılan gradient, clip, blur ve image filter kombinasyonlarını küçük bir canvas üzerinde üretin. `ShaderWarmUp`, Flutter binding başlatılırken çalıştığı için aşağıdaki örnekteki çizimler ilk rota gösterilmeden önce yapılır.
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
class AppShaderWarmUp extends ShaderWarmUp {
@override
void warmUpOnCanvas(Canvas canvas) {
const rect = Rect.fromLTWH(0, 0, 320, 160);
final gradient = ui.Gradient.linear(
const Offset(0, 0),
const Offset(320, 160),
const [Color(0xff276EF1), Color(0xffA855F7)],
);
canvas.drawRRect(
RRect.fromRectAndRadius(rect, const Radius.circular(24)),
Paint()..shader = gradient,
);
canvas.saveLayer(rect, Paint());
canvas.drawCircle(
const Offset(160, 80),
48,
Paint()..maskFilter = const MaskFilter.blur(BlurStyle.normal, 12),
);
canvas.restore();
}
}
void main() {
PaintingBinding.instance.shaderWarmUp = AppShaderWarmUp();
runApp(const App());
}Warm-up çizimi ile üretimdeki çizim pipeline'ı eşleşmelidir. Örneğin üretimde `BackdropFilter` kullanıp warm-up içinde yalnızca `MaskFilter.blur` çizmek, farklı render pass ve blend davranışı nedeniyle gerekli pipeline'ı hazırlamayabilir. DevTools trace'inde ilgili rota açılırken hala ilk-seferlik raster spike varsa, üretimdeki `BlendMode`, clip şekli, sample count veya shader parametrelerinin warm-up örneğinden farklı olup olmadığını kontrol edin.
Skia tabanlı hedeflerde, profil cihazında tekrar edilen gerçek kullanıcı akışıyla SKSL capture alıp build aşamasında paketlemek bazı dağıtımlarda uygun olabilir. Capture dosyasını emülatörde değil, hedef GPU ailesini temsil eden fiziksel cihazda üretin; sürücü ve GPU farkları nedeniyle tek bir cihazdan üretilen veri tüm Android filosunu temsil etmez.
flutter run --profile --cache-sksl
flutter build apk --bundle-sksl-path flutter_01.sksl.jsonDart programlama eğitimi ile runtime fragment shader yükleme sırası
Dart programlama eğitimi sırasında `FragmentProgram.compile` çağrısının async olması genellikle bilinir, fakat kritik ayrıntı şudur: asset yüklemesini erken yapmak tek başına ilk GPU kullanım maliyetini garanti olarak ortadan kaldırmaz. Yine de route build anında disk veya asset bundle erişimi beklememek için programı uygulama servisinde bir kez yükleyip sonucu paylaşın. Aynı shader için her kart widget'ında `compile` çağrısı yapmak, asset çözümleme ve hata yönetimini gereksizce çoğaltır.
class ShaderCatalog {
ShaderCatalog._();
static final ShaderCatalog instance = ShaderCatalog._();
late final Future glowProgram =
ui.FragmentProgram.compile(assetKey: 'shaders/glow.frag');
Future createGlow() async {
final program = await glowProgram;
return program.fragmentShader();
}
} Bu kaynağı widget state'inde başlatacaksanız sonucu `build` içinde oluşturmayın. `build` tekrar çağrıldığında her seferinde yeni Future yaratmak, `FutureBuilder`'ın bekleme durumuna dönmesine ve hızlı state değişimlerinde görsel titreşime yol açabilir. `initState` içinde Future referansını saklayın; rota değişmeden önce preload yapmak istiyorsanız uygulamanızın izin ve ağ akışlarından bağımsız, bounded bir başlangıç görevi olarak planlayın.
class HeroGlow extends StatefulWidget {
const HeroGlow({super.key});
@override
State createState() => _HeroGlowState();
}
class _HeroGlowState extends State {
late final Future _shader;
@override
void initState() {
super.initState();
_shader = ShaderCatalog.instance.createGlow();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _shader,
builder: (context, snapshot) => snapshot.hasData
? CustomPaint(painter: GlowPainter(snapshot.data!))
: const SizedBox(height: 180),
);
}
} Cross platform mobil uygulama geliştirme için frame regresyon kapısı
Cross platform mobil uygulama geliştirme sürecinde Android ve iOS için tek bir P95 eşiği koymak yanıltıcı olabilir; ekran yenileme hızı, GPU sürücüsü ve renderer farklıdır. Ancak aynı cihaz havuzunda karşılaştırılabilir bir kapı kurabilirsiniz. `addTimingsCallback` ile release-benzeri test akışında `FrameTiming` örneklerini toplayın, `rasterDuration` değerlerini sıralayın ve P95 ile maksimum değeri JSON olarak test artefact'ına yazın. Ölçümden önce en az bir ısınma turu, ardından ayrı bir cold-route turu çalıştırarak iki davranışı bilinçli olarak ayırın.
class FrameMetricCollector {
final List rasterUs = [];
void start() {
WidgetsBinding.instance.addTimingsCallback((frames) {
rasterUs.addAll(frames.map((f) => f.rasterDuration.inMicroseconds));
});
}
Map summary() {
final values = [...rasterUs]..sort();
final p95Index = (values.length * 0.95).floor().clamp(0, values.length - 1);
return {
'frames': values.length,
'p95RasterUs': values[p95Index],
'maxRasterUs': values.last,
};
}
} CI kararını sadece bu uygulama içi sayaçla vermeyin. Firebase Test Lab, BrowserStack App Automate veya kurum içi fiziksel cihaz rafında aynı APK/IPA ve sabit test verisiyle rota senaryosu koşturun. Örnek bir politika: orta segment Android referans cihazında cold product-route akışında maksimum raster süre 35 ms'yi, P95 16.67 ms'yi geçerse build başarısız olsun; warm path için daha sıkı bir 16.67 ms maksimum sınırı uygulayın. Eşik değişikliği pull request içinde trace bağlantısı ve cihaz modeliyle gerekçelendirilmelidir.
Bu disiplin, flutter kursu projelerinde de üretim ekiplerinde de görsel efekt ekleme kararını ölçülebilir hale getirir. Yeni bir blur veya fragment shader eklendiğinde önce baseline trace saklanır, sonra değişiklikten sonraki cold ve warm rota trace'leri karşılaştırılır. Böylece değerlendirme 'ekran akıcı hissettiriyor' yorumundan, belirli cihazdaki raster P95 ve first-use spike verisine taşınır.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Sık Sorulan Sorular
Flutter state management değişikliği shader jank sorununu çözer mi?
Yalnızca timeline'da UI thread build veya layout süresi uzunsa çözebilir. Raster thread'de sadece ilk kullanımda görülen spike için Provider, Bloc veya Riverpod refactor'u yerine DevTools trace ile pipeline derlemesini doğrulayın; sonra hedefli ShaderWarmUp veya renderer'a uygun paketleme uygulayın.
Flutter mobil uygulama geliştirmede SKSL her platformda kullanılmalı mı?
Hayır. SKSL akışı Skia davranışıyla ilişkilidir ve Impeller kullanan hedefte aynı çözüm değildir. Önce profile trace alın, kullanılan renderer'ı doğrulayın ve capture işlemini hedef GPU ailesini temsil eden fiziksel cihazda yapın. Emülatörden alınan capture üretim cihazındaki sürücü davranışını temsil etmez.
Cross platform mobil uygulama geliştirme için jank eşiği kaç ms olmalı?
Eşik ekran yenileme hızına bağlıdır: 60 Hz için frame bütçesi 16.67 ms, 120 Hz için 8.33 ms'dir. CI'da cihaz bazlı ölçün; `FrameTiming.rasterDuration` ile cold-route maksimumunu ve P95'i ayrı raporlayın. Ortalama değer, az sayıdaki ağır first-use frame'i gizlediği için release kapısı olmamalıdır.
Flutter eğitimi projelerinde ShaderWarmUp ne zaman eklenmeli?
Önce profile modda fiziksel cihaz trace'i alın. Aynı gradient, blur, clip veya blend kombinasyonu ilk rota geçişinde raster spike üretiyorsa ekleyin. Warm-up canvas'ında üretimdeki render operasyonunu olabildiğince aynı parametrelerle çizin; farklı efekt türü çizmek gerekli pipeline'ı tetiklemeyebilir.
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.


