↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 11 — Konu 54: App Size, Obfuscation, Flavor Yönetimi

6 dk okuma #flutter
Dizi · 54/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 17 başlık
  1. App Size — Uygulama Boyutunu Küçültme
  2. Neden Önemli?
  3. --analyze-size — Neyin Yer Kapladığını Görmek
  4. --split-per-abi — Mimariye Özel APK'lar
  5. İkon/Font Tree-Shaking — Otomatik Bir Kolaylık
  6. Obfuscation (Kod Gizleme)
  7. Neden Gerekli?
  8. Flavor Yönetimi — Aynı Kod Tabanından Birden Fazla Sürüm
  9. Sorunu Tanımlayalım
  10. Android Tarafı — build.gradle
  11. Dart Tarafı — Ortama Özel Giriş Noktaları
  12. iOS Tarafı — Xcode Schemes
  13. flutter_flavorizr — Kurulumu Otomatikleştiren Bir Paket
  14. Bölüm 11'i Kapatırken — Yayınlama Sürecinin Tam Resmi
  15. 🎯 Bu Dersten Çıkarılması Gerekenler
  16. 📝 Ödevler
  17. 🎮 Mini Uygulama — Bölüm 11 Checkpoint: Yayına Hazır Uygulama

Bölüm 11'in son konusu. Bu derste üç farklı ama hepsi yayınlama sürecinin "son rötuşları" sayılabilecek konuyu ele alıyoruz: uygulamanın boyutunu küçültmek, kodunu korumak, ve birden fazla sürümünü (geliştirme/test/üretim) aynı kod tabanından yönetmek.

App Size — Uygulama Boyutunu Küçültme

Neden Önemli?

Büyük bir uygulama, daha uzun indirme süresi, daha fazla depolama kullanımı demektir — özellikle düşük bant genişliği olan pazarlarda (gelişmekte olan ülkeler gibi), uygulamanın boyutu doğrudan indirme oranını etkiler.

--analyze-size — Neyin Yer Kapladığını Görmek

bash
flutter build apk --analyze-size

Bu komut, build sonunda hangi paketin/varlığın uygulamanın ne kadarını oluşturduğunu gösteren bir rapor üretir (DevTools'un App Size aracıyla açılabilir bir dosya olarak). Bölüm 10 Konu 48'de öğrendiğimiz "tahmin etme, ölç" prensibinin burada da geçerli olduğunu görüyorsun — hangi paketin gerçekten büyük olduğunu bilmeden optimize etmeye çalışmak, zaman kaybıdır.

--split-per-abi — Mimariye Özel APK'lar

bash
flutter build apk --split-per-abi

Android cihazlar, farklı işlemci mimarilerinde (ARM, ARM64, x86) çalışır — normal bir .apk, hepsini birden içerir (kullanmayacağın mimarilerin kodu da dahil). --split-per-abi, her mimari için ayrı bir APK üretir — her kullanıcı, sadece kendi cihazına uygun olanı indirir, bu da belirgin bir boyut azalması sağlar.

Önemli hatırlatma: Konu 53'te öğrendiğimiz Play App Signing ve .aab formatı, aslında bu sorunu zaten büyük ölçüde çözüyor — Play Store, .aab'den, her cihaza özel optimize edilmiş bir APK kendisi üretiyor. --split-per-abi, daha çok Play Store dışı dağıtım (elle .apk paylaşma) senaryolarında değerlidir.

İkon/Font Tree-Shaking — Otomatik Bir Kolaylık

bash
flutter build apk --release
# Çıktıda göreceğin satır:
# Font asset "MaterialIcons-Regular.otf" was tree-shaken, reducing it from 1.6MB to 12KB

Dart Bölüm 13'te öğrendiğimiz tree-shaking (kullanılmayan kodun derlemeden çıkarılması) kavramı, sadece koda değil, Icons. ile kullandığın ikon fontuna da uygulanır — Flutter, projende hangi ikonları kullandığını analiz eder ve sadece onları içeren küçültülmüş bir font dosyası üretir. Bu otomatik çalışır, elle bir şey yapman gerekmez — ama IconData'yı dinamik (çalışma zamanında hesaplanan) bir şekilde kullanırsan (örn. bir Map'ten okuyarak), Flutter hangi ikonların kullanılacağını önceden bilemez ve bu optimizasyon devre dışı kalır.

Obfuscation (Kod Gizleme)

Neden Gerekli?

bash
flutter build apk --obfuscate --split-debug-info=build/debug-info

Normalde, bir Dart/Flutter uygulamasının AOT-derlenmiş (Bölüm 1'de öğrendiğimiz derleme türü) hali bile, sınıf isimleri, fonksiyon isimleri gibi okunabilir bilgiler içerir — biri, senin .apk'nı açıp inceleyerek, kodunun yapısını (sınıf/fonksiyon isimlerinden yola çıkarak) kısmen anlayabilir. --obfuscate, bu isimleri anlamsız kısa kodlara (a, b, Xy3 gibi) dönüştürür — kodun çalışma mantığını değiştirmeden, okunabilirliğini ortadan kaldırır.

--split-debug-info=build/debug-info — obfuscation'ın bir maliyeti var: bir kullanıcının cihazında bir çökme (crash) olduğunda, normalde hata raporu okunabilir fonksiyon isimleri içerir; obfuscate edilmiş bir build'de bu isimler anlamsız kodlara dönüştüğü için, hata raporu da anlamsız olur. --split-debug-info, obfuscation sırasında hangi anlamsız kodun hangi gerçek isme karşılık geldiğini tutan bir eşleme dosyası üretir — bu dosyayı güvenli bir yerde saklarsın (Play Store'a da yüklenebilir), ve bir çökme raporunu bu dosyayla "deobfuscate" ederek (Konu 53'te tanıdığımız Firebase'in Crashlytics gibi araçları bunu otomatik yapar) tekrar okunabilir hale getirirsin.

Pratik tavsiye: Obfuscation, hassas iş mantığı (örn. bir fiyatlandırma algoritması) içeren ticari uygulamalarda değerlidir; basit bir hobi projesinde genelde gerekli değildir.

Flavor Yönetimi — Aynı Kod Tabanından Birden Fazla Sürüm

Sorunu Tanımlayalım

Gerçek bir geliştirme sürecinde, genelde üç ortamın aynı anda cihazında kurulu olmasını istersin:

metin
Dev     → geliştirme sırasında test ettiğin, sahte/test API'sine bağlı sürüm
Staging → gerçek API'ye yakın, yayın öncesi son test ortamı
Prod    → gerçek kullanıcıya gidecek, üretim API'sine bağlı sürüm

Flavor'lar (Android'de "product flavor", iOS'ta "scheme"), aynı Dart kodundan, farklı applicationId/Bundle Identifier, farklı uygulama ikonu/ismi, ve farklı yapılandırma (API URL'si gibi) ile birbirinden bağımsız üç ayrı uygulama üretmeni sağlar — üçü de aynı cihaza, aynı anda kurulabilir (farklı applicationId'ler sayesinde, sistem onları farklı uygulamalar sanır).

Android Tarafı — build.gradle

gradle
android {
    flavorDimensions "ortam"
    productFlavors {
        dev {
            dimension "ortam"
            applicationIdSuffix ".dev"       // com.senirket.uygulaman.dev
            resValue "string", "app_name", "Uygulamam (Dev)"
        }
        prod {
            dimension "ortam"
            resValue "string", "app_name", "Uygulamam"
        }
    }
}

applicationIdSuffix ".dev" — Konu 44'te öğrendiğimiz applicationId'nin sonuna bir ek getirerek, dev ve prod sürümlerinin Play Store/cihaz açısından tamamen farklı uygulamalar olmasını sağlıyor.

Dart Tarafı — Ortama Özel Giriş Noktaları

dart
// lib/main_dev.dart
import 'app_config.dart';
import 'main.dart' as app;

void main() {
  AppConfig.apiUrl = 'https://dev-api.ornek.com';
  app.main();
}
dart
// lib/main_prod.dart
import 'app_config.dart';
import 'main.dart' as app;

void main() {
  AppConfig.apiUrl = 'https://api.ornek.com';
  app.main();
}
dart
// lib/app_config.dart
class AppConfig {
  static late String apiUrl; // Dart Bölüm 2 — late, geç atanacak alan
}

Bu kalıp neden işe yarıyor? Dart Bölüm 12'de öğrendiğimiz import ... as (isim çakışmasını önleme) burada kritik — her flavor'ın kendi main_X.dart dosyası, ortak main.dart'ı (asıl uygulama) farklı bir yapılandırmayla çağırıyor. AppConfig.apiUrl, Bölüm 5 Konu 20'de http/dio çağrılarında kullandığın temel URL'in artık sabit kodlanmış olmadığı, flavor'a göre değiştiği anlamına geliyor.

bash
flutter run --flavor dev -t lib/main_dev.dart
flutter build apk --flavor prod -t lib/main_prod.dart

--flavor dev, Android/iOS'a hangi flavor'ı derleyeceğini söyler; -t lib/main_dev.dart (target), Dart tarafına hangi giriş noktasından başlayacağını söyler — bu ikisinin birlikte verilmesi gerekir, biri native tarafı, diğeri Dart tarafını yapılandırıyor.

iOS Tarafı — Xcode Schemes

iOS'ta aynı fikir, Xcode Scheme'leri ve Configuration'lar ile uygulanır — Konu 44'te öğrendiğimiz Runner.xcworkspace içinde, her flavor için ayrı bir scheme (Dev/Staging/Prod) tanımlanır, her biri kendi Bundle Identifier'ına ve Info.plist ayarlarına sahip olur. Bu kısım genelde Xcode arayüzünden elle yapılandırılır — CLI tarafından tam otomatik değildir, Android'e göre biraz daha manuel bir süreçtir.

flutter_flavorizr — Kurulumu Otomatikleştiren Bir Paket

bash
flutter pub add dev:flutter_flavorizr

Yukarıdaki Android + iOS + Dart yapılandırmasının hepsini elle kurmak, özellikle iOS tarafında hataya açık ve tekrarlayıcı bir iştir — flutter_flavorizr, pubspec.yaml'a yazacağın bir yapılandırmadan, bu dosyaların çoğunu otomatik üretir. Bu, Dart Bölüm 13'te öğrendiğimiz build_runner/code generation felsefesinin, build sistemi yapılandırmasına uygulanmış hali.

Bölüm 11'i Kapatırken — Yayınlama Sürecinin Tam Resmi

metin
Kod yaz (Bölüm 1-10) 
  → flavor'lara göre yapılandır (dev/staging/prod)
  → boyutu analiz et (--analyze-size), gerekiyorsa küçült
  → obfuscate et (--obfuscate), debug-info'yu sakla
  → imzala (Konu 44 + Konu 53'teki Play App Signing)
  → test track'lerinden geçir (iç → kapalı → açık)
  → staged rollout ile üretime çık

🎯 Bu Dersten Çıkarılması Gerekenler

  • --analyze-size, hangi paketin/varlığın uygulamanın boyutunu ne kadar etkilediğini ölçerek gösterir; --split-per-abi, Play Store dışı dağıtımda mimariye özel küçük APK'lar üretir (Store'da bu işi zaten .aab yapıyor).
  • İkon tree-shaking, kullanılan Icons. ikonlarını otomatik tespit edip font dosyasını küçültür — dinamik ikon kullanımı bunu devre dışı bırakabilir.
  • --obfuscate, kod okunabilirliğini kaldırır; --split-debug-info, obfuscate edilmiş çökme raporlarını tekrar okunabilir hale getirmek için gereken eşleme dosyasını üretir.
  • Flavor'lar, aynı kod tabanından farklı applicationId/Bundle Identifier, isim, ikon ve yapılandırmayla (dev/staging/prod) bağımsız uygulamalar üretmeni sağlar.
  • --flavor (native yapılandırma) ve -t (Dart giriş noktası) birlikte kullanılır; flutter_flavorizr, bu kurulumu büyük ölçüde otomatikleştirir.

📝 Ödevler

  • [ ] flutter build apk --analyze-size çalıştır, raporu DevTools'ta aç, en büyük 3 bileşeni tespit et.
  • [ ] --obfuscate --split-debug-info ile bir release build üret, üretilen debug-info dosyasının ne işe yaradığını kendi cümlelerinle açıkla.
  • [ ] Android tarafında dev/prod flavor'ları kur (farklı applicationIdSuffix ve uygulama ismiyle), main_dev.dart/main_prod.dart giriş noktalarını oluştur, ikisini aynı cihaza aynı anda kurup farklı uygulamalar olduklarını doğrula.
  • [ ] AppConfig.apiUrl'i her flavor için farklı bir sahte URL'e ayarla, hangi flavor'ın hangi URL'i kullandığını ekranda göster.
  • [ ] Kendi cümlelerinle, "neden .aab + Play App Signing kullanan bir uygulamanın, --split-per-abi'ye genelde ihtiyaç duymadığını" açıkla.

🎮 Mini Uygulama — Bölüm 11 Checkpoint: Yayına Hazır Uygulama

Bölüm 11'in tamamını (Firebase/Supabase, versiyonlama, boyut/obfuscation, flavor) birleştiren bir mini proje:

Gereksinimler:

  • Bölüm 11 Konu 50 veya 51'de kurduğun Auth + veritabanı akışını temel alan basit bir uygulama.
  • dev/prod iki flavor kur, her biri farklı bir Firebase/Supabase projesine (ya da en azından farklı bir sahte API URL'sine) bağlansın.
  • pubspec.yaml'da anlamlı bir semantic version + build number akışı uygula (Konu 53'ü hatırla).
  • --analyze-size ile boyutu ölç, en az bir optimizasyon (örn. kullanılmayan bir paketi kaldırma) uygula ve öncesi/sonrası boyutu karşılaştır.
  • Release modda, --obfuscate ile bir build üret.
  • Bonus: flutter build appbundle --flavor prod ile üretilen .aab'yi, (bir Play Console hesabın varsa) iç test track'ine yükle.

Bölüm 11 tamamlandı. Sıradaki konu: Bölüm 12 — Konu 55: Flutter Web / Desktop (Uzmanlık Seviyesi Başlangıcı)