↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 5 — Konu 21: JSON Serialization (Manuel → `json_serializable`/`freezed`)

4 dk okuma #flutter
Dizi · 21/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. Sorun: Map<String, dynamic> ile Çalışmanın Riskleri
  2. Manuel Model Sınıfı — fromJson/toJson
  3. Listelerle Çalışma
  4. Manuel Yazmanın Sorunu — Tekrarlayan, Sıkıcı Kod
  5. json_serializable — Otomatik Üretim
  6. freezed — Immutable Modeller + JSON Serialization Bir Arada
  7. Manuel vs json_serializable vs freezed — Ne Zaman Hangisi?
  8. 🎯 Bu Dersten Çıkarılması Gerekenler
  9. 📝 Ödevler
  10. 🎮 Mini Uygulama — Gerçek API'den Gönderi Listesi

Bir önceki derste Map<String, dynamic> ile JSON verisiyle çalıştık — ama bu, Bölüm 4 Konu 18'de (Dart) "geçici bir kullanım, genelde sonra somut bir modele çevrilir" dediğimiz durumun tam olarak henüz o adımı atmadığımız halidir. Bu derste, JSON'u somut Dart sınıflarına çevirmeyi öğreneceğiz.

Sorun: Map<String, dynamic> ile Çalışmanın Riskleri

dart
final veri = jsonDecode(response.body);
print(veri['titel']); // ❌ Yazım hatası! "titel" değil "title" olmalıydı

Bu kod derlenir (çünkü dynamic — Dart Bölüm 2 Konu 8'i hatırla, tip kontrolünü kapatır) ama çalışma zamanında null döner (çünkü titel diye bir anahtar yok) — bu, tam olarak Bölüm 2'de "dynamic'ten mümkün olduğunca kaçın" dediğimiz riskin somut bir örneği.

Manuel Model Sınıfı — fromJson/toJson

dart
class Gonderi {
  final int id;
  final String title;
  final String body;

  Gonderi({required this.id, required this.title, required this.body});

  factory Gonderi.fromJson(Map<String, dynamic> json) {
    return Gonderi(
      id: json['id'],
      title: json['title'],
      body: json['body'],
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'title': title,
      'body': body,
    };
  }
}

factory Gonderi.fromJson(...) — Dart Bölüm 6 Konu 27'de öğrendiğimiz factory constructor'ı hatırlıyor musun? "Oluşturma mantığını özelleştirmeye izin verir" demiştik — işte tam olarak budur: bir Map<String, dynamic> alıp, onu doğrulayıp/dönüştürüp bir Gonderi nesnesi üreten özel bir oluşturma yolu.

Kullanımı:

dart
Future<Gonderi> gonderiGetir() async {
  final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));
  final Map<String, dynamic> json = jsonDecode(response.body);
  return Gonderi.fromJson(json); // artık tip güvenli!
}
dart
final gonderi = await gonderiGetir();
print(gonderi.title); // ✅ IDE, 'title' alanının VAR OLDUĞUNU biliyor, yazım hatası imkansız

Artık gonderi.titel yazarsan, bu derleme zamanında hata verir (Dart Bölüm 6'da öğrendiğimiz alan erişimi kuralları geçerli) — tıpkı Bölüm 4'te (Dart) "JSON'u kısa süre sonra somut bir modele çevireceksen dynamic meşrudur" dediğimiz geçiş sürecinin tamamlandığı an.

Listelerle Çalışma

dart
Future<List<Gonderi>> gonderilerGetir() async {
  final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
  final List<dynamic> jsonListesi = jsonDecode(response.body);
  return jsonListesi.map((json) => Gonderi.fromJson(json)).toList();
}

jsonListesi.map(...) — Dart Bölüm 11'de öğrendiğimiz .map() metodu, burada son derece doğal bir kullanım alanı buluyor: her ham JSON nesnesini, Gonderi.fromJson() ile somut bir Dart nesnesine dönüştürüyoruz. Bu, Dart Bölüm 11'de öğrendiğimiz "koleksiyon dönüşümü" bilgisinin, gerçek dünya API entegrasyonunda doğrudan kullanıldığı bir andır.

Manuel Yazmanın Sorunu — Tekrarlayan, Sıkıcı Kod

Küçük modellerde manuel fromJson/toJson yazmak makul, ama büyük, çok alanlı modellerde bu hem uzun hem hataya açık hale gelir. İşte bu yüzden Dart Bölüm 13 Konu 66'da öğrendiğimiz kod üretimi (code generation) burada devreye giriyor.

json_serializable — Otomatik Üretim

bash
flutter pub add json_annotation
flutter pub add --dev json_serializable build_runner
dart
import 'package:json_annotation/json_annotation.dart';

part 'gonderi.g.dart'; // Dart Bölüm 12'de öğrendiğimiz part/part of!

@JsonSerializable()
class Gonderi {
  final int id;
  final String title;
  final String body;

  Gonderi({required this.id, required this.title, required this.body});

  factory Gonderi.fromJson(Map<String, dynamic> json) => _$GonderiFromJson(json);
  Map<String, dynamic> toJson() => _$GonderiToJson(this);
}
bash
dart run build_runner build

Bu, Dart Bölüm 13 Konu 66'da tam olarak işlediğimiz kalıbın birebir aynısı — @JsonSerializable() annotation'ı, build_runner'a "bu class için fromJson/toJson implementasyonunu otomatik üret" diyor. Üretilen gonderi.g.dart dosyası, _$GonderiFromJson/_$GonderiToJson fonksiyonlarının gerçek kodunu içeriyor — sen bunu elle yazmak zorunda değilsin.

Neden değerli? 20 alanlı bir modelde, her alanı elle json['alan_adi'] şeklinde eşleştirmek yerine, build_runner bunu senin için, hatasız yapıyor — ve model değiştiğinde (yeni alan eklediğinde), sadece dart run build_runner build'i tekrar çalıştırman yeterli.

freezed — Immutable Modeller + JSON Serialization Bir Arada

bash
flutter pub add freezed_annotation
flutter pub add --dev build_runner freezed
dart
import 'package:freezed_annotation/freezed_annotation.dart';

part 'gonderi.freezed.dart';
part 'gonderi.g.dart';

@freezed
class Gonderi with _$Gonderi {
  const factory Gonderi({
    required int id,
    required String title,
    required String body,
  }) = _Gonderi;

  factory Gonderi.fromJson(Map<String, dynamic> json) => _$GonderiFromJson(json);
}

Dart Bölüm 13 Konu 66'da freezed'i kısaca tanıtmıştık — burada tam olarak neyi otomatikleştirdiğini görelim. freezed, sadece JSON serialization'ı değil, tüm immutable veri sınıfı davranışlarını üretir:

  • == ve hashCode (içerik bazlı eşitlik — Dart Bölüm 6'da bahsettiğimiz ama elle yazmadığımız bir konu).
  • toString() (Bölüm 6'da elle yazdığımız kalıbın otomasyonu).
  • copyWith() — Bölüm 3 Konu 12'de (Flutter) öğrendiğimiz TextStyle.copyWith() kalıbının, kendi modellerin için otomatik üretilmiş hali!
dart
final gonderi = Gonderi(id: 1, title: 'Başlık', body: 'İçerik');
final guncellenmisGonderi = gonderi.copyWith(title: 'Yeni Başlık');
// id ve body aynı kaldı, sadece title değişti

Manuel vs json_serializable vs freezed — Ne Zaman Hangisi?

Manuel json_serializable freezed
Küçük, basit modeller ✅ Yeterli Gereksiz olabilir Gereksiz olabilir
Büyük, çok alanlı modeller ❌ Hataya açık ✅ İyi ✅ İyi
copyWith(), ==, toString() gerekiyor mu? Elle yazman gerekir Sadece JSON, diğerleri elle ✅ Hepsi otomatik
Sealed class / pattern matching ile kullanım (Dart Bölüm 13'ü hatırla) ❌ ❌ ✅ Güçlü destek

Pratik tavsiye: Küçük prototiplerde manuel yazmak hızlıdır. Gerçek, büyüyen bir projede, freezed, hem JSON serialization'ı hem immutability (Bölüm 6'da state management'ta çok değerli olacak) hem de copyWith()'i tek bir araçla hallettiği için genelde en tercih edilen seçenektir.


🎯 Bu Dersten Çıkarılması Gerekenler

  • Map<String, dynamic> ile doğrudan çalışmak, yazım hatalarını çalışma zamanına kadar gizler; somut model sınıfları (fromJson/toJson) bunu derleme zamanına taşır.
  • factory Gonderi.fromJson(...), Dart'ta öğrendiğimiz factory constructor'ın gerçek bir JSON dönüştürme senaryosudur.
  • .map() ile bir JSON listesini, somut model nesnelerinin listesine dönüştürmek, Dart'ta öğrendiğimiz koleksiyon dönüşümünün doğal bir uygulamasıdır.
  • json_serializable, build_runner ile fromJson/toJson kodunu otomatik üretir — Dart Bölüm 13'te öğrendiğimiz kod üretimi kalıbının gerçek kullanımı.
  • freezed, JSON serialization'a ek olarak copyWith(), ==, toString() gibi tüm immutable veri sınıfı davranışlarını otomatik üretir.

📝 Ödevler

  • [ ] Basit bir JSON yanıtı için (örn. bir kullanıcı: id, isim, email) manuel bir model sınıfı yaz, fromJson/toJson ekle.
  • [ ] Bir JSON listesini, .map() ile model nesnelerinin listesine dönüştür.
  • [ ] json_serializable kurup, aynı modeli @JsonSerializable() ile yeniden yaz, build_runner ile kod üret, üretilen dosyayı incele.
  • [ ] freezed kurup, aynı modeli @freezed ile yeniden yaz, copyWith()'i kullanarak bir alanı güncelle.
  • [ ] Kendi cümlelerinle, "neden büyük projelerde manuel JSON serialization yerine kod üretimi tercih edilir" sorusunu açıkla.

🎮 Mini Uygulama — Gerçek API'den Gönderi Listesi

Bölüm 5'te öğrendiğin her şeyi birleştiren bir mini proje:

Gereksinimler:

  • jsonplaceholder.typicode.com/posts API'sinden gönderi listesini çek (http paketi).
  • Ham JSON'u, kendi yazdığın Gonderi model sınıfına (fromJson ile) dönüştür.
  • FutureBuilder + initState()/late final kalıbıyla, listeyi bir ListView.builder (Bölüm 3 Konu 11'i hatırla) içinde göster.
  • Her gönderiye tıklandığında (Bölüm 3 Konu 14'ü hatırla), o gönderinin tam detayını gösteren bir ekrana geç (constructor parametresi ile veri gönder).
  • Yükleme sırasında CircularProgressIndicator, hata durumunda anlamlı bir mesaj göster.

Bu, artık gerçek, uçtan uca çalışan bir uygulama parçası — internetten veri çekiyor, tip güvenli modellere dönüştürüyor, listeliyorsun ve detaya gidebiliyorsun.


Sıradaki konu: Bölüm 5 — Konu 22: Local Storage (shared_preferences → Hive → sqflite/Drift)