↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 10 — Konu 48: DevTools Profiling

5 dk okuma #flutter
Dizi · 48/61 Flutter Türkçe Tutorial
  1. Bölüm 1 — Konu 1: Flutter Nedir, Mimarisi (Widget → Element → RenderObject, Skia/Impeller)
  2. Bölüm 1 — Konu 2: Ortam Kurulumu (Flutter SDK, Android Studio/VS Code, Emulator/Simulator, DevTools'a İlk Bakış)
  3. Bölüm 1 — Konu 3: İlk Proje Yapısı (`pubspec.yaml`, `lib/`, Klasör Mimarisi, `flutter create` Anatomisi)
  4. Bölüm 1 — Konu 4: Hot Reload / Hot Restart Ne Yapıyor, Neden Önemli
  5. Bölüm 2 — Konu 5: "Her Şey Widget'tır" Felsefesi, Widget Ağacı
  6. Bölüm 2 — Konu 6: StatelessWidget vs StatefulWidget + `setState()` Derinlemesine (Rebuild Mekanizması)
  7. Bölüm 2 — Konu 7: Temel Layout — Container, Row, Column, Stack, Padding, Align, Center
  8. Bölüm 2 — Konu 8: Constraint Sistemi — "Constraints Go Down, Sizes Go Up, Parent Sets Position"
  9. Bölüm 2 — Konu 9: `Expanded`, `Flexible`, `Spacer`, Intrinsic Widget'lar
  10. Bölüm 3 — Konu 10: MaterialApp/CupertinoApp, Scaffold, AppBar
  11. Bölüm 3 — Konu 11: `ListView`, `GridView`, `SingleChildScrollView` (+ Builder Pattern)
  12. Bölüm 3 — Konu 12: Text, TextStyle, Icon, Image, Asset Yönetimi
  13. Bölüm 3 — Konu 13: Custom Widget Yazma Prensipleri (Composition Over Inheritance)
  14. Bölüm 3 — Konu 14: Navigator 1.0 — Push/Pop, Named Routes
  15. Bölüm 4 — Konu 15: Form, TextField/TextFormField, GlobalKey<FormState>, Validasyon
  16. Bölüm 4 — Konu 16: Theme Sistemi (ThemeData, ColorScheme)
  17. Bölüm 4 — Konu 17: Responsive & Adaptive Tasarım (MediaQuery, LayoutBuilder, OrientationBuilder, Breakpoint Stratejileri)
  18. Bölüm 5 — Konu 18: Future/Async-Await Flutter Bağlamında, FutureBuilder
  19. Bölüm 5 — Konu 19: `Stream`, `StreamBuilder`
  20. Bölüm 5 — Konu 20: HTTP İstekleri (`http` / `dio` Paketleri)
  21. Bölüm 5 — Konu 21: JSON Serialization (Manuel → `json_serializable`/`freezed`)
  22. Bölüm 5 — Konu 22: Local Storage — `shared_preferences` → `Hive` → `sqflite`/Drift
  23. Bölüm 6 — Konu 23: InheritedWidget ve InheritedModel — "Neden setState Yetmiyor" Sorusunun Cevabı
  24. Bölüm 6 — Konu 24: Provider Paketi
  25. Bölüm 6 — Konu 25: Riverpod (Modern Yaklaşım, Provider'ın Halefi)
  26. Bölüm 6 — Konu 26: BLoC/Cubit Pattern (`flutter_bloc`)
  27. Bölüm 6 — Konu 27: GetX (Tartışmalı Ama Yaygın)
  28. Bölüm 6 — Konu 28: Karşılaştırma — Hangi Projede Hangisi?
  29. Bölüm 7 — Konu 29: Navigator 2.0 (Router, RouteInformationParser, RouterDelegate)
  30. Bölüm 7 — Konu 30: `go_router` Paketi (Pratik ve Modern Çözüm)
  31. Bölüm 7 — Konu 31: Deep Linking
  32. Bölüm 7 — Konu 32: Repository Pattern, Katmanlı Mimari (Data/Domain/Presentation)
  33. Bölüm 7 — Konu 33: Dependency Injection (`get_it`, `injectable`)
  34. Bölüm 7 — Konu 34: Clean Architecture Uyarlaması, SOLID Prensipleri
  35. Bölüm 8 — Konu 35: Implicit Animasyonlar
  36. Bölüm 8 — Konu 36: Explicit Animasyonlar (`AnimationController`, `Tween`, `Curve`)
  37. Bölüm 8 — Konu 37: Hero Animasyonları
  38. Bölüm 8 — Konu 38: `CustomPainter` / `Canvas`
  39. Bölüm 8 — Konu 39: Rive / Lottie Entegrasyonu
  40. Bölüm 9 — Konu 40: Platform Channels
  41. Bölüm 9 — Konu 41: Permission Yönetimi (`permission_handler`)
  42. Bölüm 9 — Konu 42: Kamera, Konum, Sensörler
  43. Bölüm 9 — Konu 43: Push Notification (Firebase Cloud Messaging)
  44. Bölüm 9 — Konu 44: Android/iOS Build Sistemleri
  45. Bölüm 10 — Konu 45: Test Yazımı (Unit, Widget, Integration, Golden)
  46. Bölüm 10 — Konu 46: CI/CD
  47. Bölüm 10 — Konu 47: Rebuild Optimizasyonu (`const`, `key` Kullanımı)
  48. Bölüm 10 — Konu 48: DevTools Profiling
  49. Bölüm 10 — Konu 49: Lazy Loading, Pagination, Büyük Liste Performansı
  50. Bölüm 11 — Konu 50: Firebase Ekosistemi (Auth, Firestore, Storage, Functions)
  51. Bölüm 11 — Konu 51: Supabase Alternatifi
  52. Bölüm 11 — Konu 52: GraphQL (Opsiyonel)
  53. Bölüm 11 — Konu 53: Store Yayınlama Süreci (İmzalama, Listing, Versiyonlama)
  54. Bölüm 11 — Konu 54: App Size, Obfuscation, Flavor Yönetimi
  55. Bölüm 12 — Konu 55: Flutter Web / Desktop
  56. Bölüm 12 — Konu 56: Custom `RenderObject` Yazımı
  57. Bölüm 12 — Konu 57: Engine Mimarisi Derinlemesine (Impeller vs Skia)
  58. Bölüm 12 — Konu 58: Plugin Geliştirme, pub.dev'e Paket Yayınlama
  59. Bölüm 12 — Konu 59: Monorepo Mimarisi (Melos)
  60. Bölüm 12 — Konu 60: Erişilebilirlik (Accessibility) Derinlemesine
  61. Bölüm 12 — Konu 61: Yerelleştirme (Localization / i18n)
Dizinin sayfası →
İçindekiler 10 başlık
  1. DevTools Nedir, Nasıl Açılır?
  2. Widget Inspector — Ağacı Gözle Görmek
  3. "Track Widget Rebuilds" — Konu 47'nin Kanıtı
  4. Performance / Timeline Görünümü — "Jank" Avcılığı
  5. CPU Profiler — "Hangi Fonksiyon Zaman Yiyor?"
  6. Memory Görünümü — Bellek Sızıntılarını Yakalamak
  7. Network Görünümü — HTTP İsteklerini İzleme
  8. Pratik Bir İş Akışı — "Bir Şey Yavaş" Şikayetiyle Karşılaştığında
  9. 🎯 Bu Dersten Çıkarılması Gerekenler
  10. 📝 Ödevler

Konu 47'de const/key ile teorik olarak nasıl optimize edeceğimizi öğrendik — ama "bu optimizasyon gerçekten işe yaradı mı?" sorusunu tahmin ederek değil, ölçerek cevaplamalısın. Bu derste, Flutter'ın hazır profiling aracı DevTools'u öğreneceğiz.

DevTools Nedir, Nasıl Açılır?

bash
flutter run --profile   # PROFILE modda çalıştır — Bölüm 9 Konu 44'ü hatırla

Uygulamayı --profile modunda çalıştırdığında, terminalde bir DevTools URL'si verilir (ya da IDE'nin Flutter eklentisi, DevTools'u otomatik açar). DevTools, tarayıcıda çalışan bir web arayüzüdür — uygulamanın canlı durumuna bağlanır ve çeşitli sekmeler halinde farklı analiz araçları sunar.

Neden --profile, --debug değil? Bölüm 9 Konu 44'te öğrendiğimiz build türlerini hatırla: debug modu, hot reload'ı mümkün kılan ekstra kod taşır — bu ekstra kod, performans ölçümlerini bozar (gerçekte var olmayan bir yavaşlık gösterir). Profile modu, release'e yakın performans sunar ama profiling araçlarının (DevTools'un bağlanabileceği) çalışmasına izin verir — bu yüzden her zaman --profile ile ölçüm yaparsın, --debug ile asla.

Widget Inspector — Ağacı Gözle Görmek

Bölüm 1'den beri zihninde kurduğun widget ağacı kavramını, Widget Inspector gerçek zamanlı, tıklanabilir bir ağaç olarak gösterir:

  • Ekranda bir widget'a tıklayarak, o widget'ın tam olarak hangi Dart koduna karşılık geldiğini bulabilirsin.
  • "Layout Explorer", Bölüm 2 Konu 8'de öğrendiğimiz constraint sistemini ("constraints go down, sizes go up") görsel olarak gösterir — bir widget'ın neden o boyutta olduğunu, hangi constraint'i kimden aldığını görebilirsin. Bir Expanded/Flexible (Bölüm 2 Konu 9) davranışını anlamıyorsan, bu araç en hızlı öğrenme yoludur.

"Track Widget Rebuilds" — Konu 47'nin Kanıtı

DevTools'un Performance sekmesinde, "Track widget rebuilds" seçeneğini açtığında, ekranın üzerine her widget'ın kaç kez yeniden build() edildiğini gösteren sayılar/renkler biner. Bu, Konu 47'de öğrendiğin const/key optimizasyonlarının gerçekten çalışıp çalışmadığını doğrulamanın doğrudan yoludur:

  • Bir widget'ı const yaptın ve rebuild sayısı hâlâ artıyorsa — muhtemelen const yanlış yere konmuş, ya da o widget'ın bir üst ebeveyni her seferinde yeni bir nesne oluşturuyor (örn. const olmayan bir parametre veriyorsun).
  • Beklenmedik şekilde çok sık rebuild olan bir widget bulursan — bu, "widget'ı küçük parçalara böl" (Konu 47) tavsiyesinin tam olarak nerede uygulanması gerektiğini gösterir.

Performance / Timeline Görünümü — "Jank" Avcılığı

metin
Hedef: 60 FPS (saniyede 60 kare) = her kare için ~16.6 milisaniye bütçe
120Hz ekranlarda: her kare için ~8.3 milisaniye bütçe

Flutter, akıcı bir deneyim için her "kare"yi (frame) belirli bir süre içinde çizmek zorundadır. Bir kare bu süreyi aşarsa, kullanıcı bunu "jank" (kasma, takılma) olarak hisseder. Timeline görünümü, her kareyi bir çubuk olarak gösterir — çubuk kırmızıysa, o kare bütçeyi aşmıştır.

Bir kare neden yavaş olur? İki ana kategori:

  1. UI thread'de yavaşlık — genelde çok karmaşık/derin bir widget ağacının build()'i, ya da Bölüm 8 Konu 38'de öğrendiğimiz CustomPainter'ın paint() metodunda pahalı bir hesaplama.
  2. Raster thread'de yavaşlık — widget ağacı hazır ama gerçek çizim (Skia/Impeller'ın pikselleri boyaması) yavaş; genelde çok fazla katman/efekt (gölgeler, saydamlıklar, ClipPath) üst üste bindiğinde görülür.

DevTools, hangi kare hangi thread'de ne kadar zaman harcadığını ayrı ayrı gösterir — bu ayrım, nereye odaklanman gerektiğini belirler (kod mu optimize edilmeli, yoksa görsel efektler mi sadeleştirilmeli).

CPU Profiler — "Hangi Fonksiyon Zaman Yiyor?"

metin
main() 
 └─ build() — %45
     └─ pahaliHesaplama() — %38   ← İŞTE SORUN BURADA
 └─ diğer — %55

CPU Profiler, bir kayıt süresi boyunca hangi fonksiyonun toplam sürenin ne kadarını tükettiğini, çağrı zincirleriyle birlikte (bir "flame chart" olarak) gösterir. Bölüm 8 Konu 38'de shouldRepaint'i her zaman true döndürmenin neden kötü olduğunu tartışmıştık — CPU Profiler, tam olarak böyle bir hatayı, "bu fonksiyon beklenmedik kadar sık çağrılıyor" şeklinde somut bir kanıtla ortaya çıkarır.

Memory Görünümü — Bellek Sızıntılarını Yakalamak

Bölüm 8 Konu 36'da AnimationController.dispose()'u unutmanın bir bellek sızıntısına yol açtığını, Bölüm 9 Konu 42'de bir StreamSubscription.cancel()'ı unutmanın aynı sınıf hata olduğunu öğrenmiştik — Memory görünümü, bu tür sızıntıları somut olarak görmeni sağlar:

  • Bir ekrana gir-çık yapıp, bellek kullanımının başlangıç seviyesine dönüp dönmediğini gözlemlersin. Sürekli yükselen bir çizgi (her gir-çıkta biraz daha fazla bellek kullanımı), bir sızıntının işaretidir.
  • "Garbage Collector'ı zorla çalıştır" butonu, Dart Bölüm 13'te öğrendiğimiz memory model/GC konusunu hatırlarsan, kullanılmayan nesnelerin gerçekten temizlenip temizlenmediğini doğrulamana yardımcı olur — GC çalıştıktan sonra bile bellek düşmüyorsa, bir yerlerde hâlâ referans tutulduğu (örn. dispose() edilmemiş bir controller) anlamına gelir.

Network Görünümü — HTTP İsteklerini İzleme

Bölüm 5 Konu 20'de öğrendiğimiz http/dio çağrılarının tümünü, DevTools'un Network sekmesinde gerçek zamanlı izleyebilirsin — hangi endpoint'e, ne zaman, ne kadar sürede istek atıldığını, dönen JSON'u (Bölüm 5 Konu 21) doğrudan görebilirsin. Bu, "API'den yanlış veri geliyor" gibi sorunları, print() satırları eklemeden doğrudan teşhis etmeni sağlar.

Pratik Bir İş Akışı — "Bir Şey Yavaş" Şikayetiyle Karşılaştığında

  1. --profile modunda çalıştır, sorunun olduğu ekrana git.
  2. Timeline'da kayıt başlat, sorunlu etkileşimi (örn. bir listeyi kaydır) yap, kaydı durdur.
  3. Kırmızı kareleri bul — UI thread mi, raster thread mi yavaş?
  4. UI thread yavaşsa → "Track widget rebuilds" ile hangi widget'ın gereksiz yeniden çizildiğine bak (Konu 47'deki const/key çözümleri burada devreye girer).
  5. Raster thread yavaşsa → görsel efektleri (gölgeler, ClipPath, saydamlık katmanları) sadeleştirmeyi düşün (Konu 49'da, büyük listeler bağlamında bunu daha fazla göreceğiz).
  6. Şüpheli bir fonksiyon varsa → CPU Profiler ile o fonksiyonun gerçekten zaman yiyip yemediğini doğrula.

🎯 Bu Dersten Çıkarılması Gerekenler

  • Performans ölçümü her zaman --profile modunda yapılır — --debug modu, hot reload yükü nedeniyle yanıltıcı sonuçlar verir.
  • Widget Inspector + Layout Explorer, widget ağacını ve constraint akışını görsel olarak anlamanı sağlar.
  • "Track widget rebuilds", Konu 47'deki const/key optimizasyonlarının gerçekten işe yarayıp yaramadığını doğrudan doğrular.
  • Timeline görünümü, hangi karelerin 16.6ms bütçesini aştığını (jank) ve bunun UI thread mi raster thread mi kaynaklı olduğunu ayırt eder.
  • CPU Profiler, hangi fonksiyonun zamanı gerçekten tükettiğini bir flame chart ile gösterir.
  • Memory görünümü, Konu 36/42'de öğrendiğimiz dispose()/cancel() ihmallerinin yol açtığı bellek sızıntılarını somut olarak ortaya çıkarır.

📝 Ödevler

  • [ ] Bir uygulamayı --profile modunda çalıştır, DevTools'u aç, Widget Inspector ile bir ekrandaki widget ağacını keşfet, Layout Explorer ile bir Row/Column'ın constraint akışını incele.
  • [ ] Konu 47'deki ödevlerden birinde yaptığın const optimizasyonunu, "Track widget rebuilds" ile doğrula — rebuild sayısının gerçekten azaldığını göster.
  • [ ] Bilerek bir AnimationController.dispose() çağrısını kaldır, Memory görünümünde ekrana gir-çık yaparak bellek kullanımının yükseldiğini gözlemle, sonra dispose()'u geri ekleyip düzeldiğini doğrula.
  • [ ] Bir ekranda ağır bir işlem (örn. büyük bir listeyi sıralama) çalıştır, CPU Profiler ile bu işlemin flame chart'ta ne kadar yer kapladığını incele.
  • [ ] Kendi cümlelerinle, "UI thread'deki bir yavaşlıkla raster thread'deki bir yavaşlığın çözümünün neden farklı olduğunu" açıkla.

Sıradaki konu: Bölüm 10 — Konu 49: Lazy Loading, Pagination, Büyük Liste Performansı (RepaintBoundary vb.) — Bölüm 10'un Son Konusu