Bölüm 5 — Konu 21: JSON Serialization (Manuel → `json_serializable`/`freezed`)
Dizi · 21/61 Flutter Türkçe Tutorial
- Bölüm 1 — Konu 1: Flutter Nedir, Mimarisi (Widget → Element → RenderObject, Skia/Impeller)
- Bölüm 1 — Konu 2: Ortam Kurulumu (Flutter SDK, Android Studio/VS Code, Emulator/Simulator, DevTools'a İlk Bakış)
- Bölüm 1 — Konu 3: İlk Proje Yapısı (`pubspec.yaml`, `lib/`, Klasör Mimarisi, `flutter create` Anatomisi)
- Bölüm 1 — Konu 4: Hot Reload / Hot Restart Ne Yapıyor, Neden Önemli
- Bölüm 2 — Konu 5: "Her Şey Widget'tır" Felsefesi, Widget Ağacı
- Bölüm 2 — Konu 6: StatelessWidget vs StatefulWidget + `setState()` Derinlemesine (Rebuild Mekanizması)
- Bölüm 2 — Konu 7: Temel Layout — Container, Row, Column, Stack, Padding, Align, Center
- Bölüm 2 — Konu 8: Constraint Sistemi — "Constraints Go Down, Sizes Go Up, Parent Sets Position"
- Bölüm 2 — Konu 9: `Expanded`, `Flexible`, `Spacer`, Intrinsic Widget'lar
- Bölüm 3 — Konu 10: MaterialApp/CupertinoApp, Scaffold, AppBar
- Bölüm 3 — Konu 11: `ListView`, `GridView`, `SingleChildScrollView` (+ Builder Pattern)
- Bölüm 3 — Konu 12: Text, TextStyle, Icon, Image, Asset Yönetimi
- Bölüm 3 — Konu 13: Custom Widget Yazma Prensipleri (Composition Over Inheritance)
- Bölüm 3 — Konu 14: Navigator 1.0 — Push/Pop, Named Routes
- Bölüm 4 — Konu 15: Form, TextField/TextFormField, GlobalKey<FormState>, Validasyon
- Bölüm 4 — Konu 16: Theme Sistemi (ThemeData, ColorScheme)
- Bölüm 4 — Konu 17: Responsive & Adaptive Tasarım (MediaQuery, LayoutBuilder, OrientationBuilder, Breakpoint Stratejileri)
- Bölüm 5 — Konu 18: Future/Async-Await Flutter Bağlamında, FutureBuilder
- Bölüm 5 — Konu 19: `Stream`, `StreamBuilder`
- Bölüm 5 — Konu 20: HTTP İstekleri (`http` / `dio` Paketleri)
- Bölüm 5 — Konu 21: JSON Serialization (Manuel → `json_serializable`/`freezed`)
- Bölüm 5 — Konu 22: Local Storage — `shared_preferences` → `Hive` → `sqflite`/Drift
- Bölüm 6 — Konu 23: InheritedWidget ve InheritedModel — "Neden setState Yetmiyor" Sorusunun Cevabı
- Bölüm 6 — Konu 24: Provider Paketi
- Bölüm 6 — Konu 25: Riverpod (Modern Yaklaşım, Provider'ın Halefi)
- Bölüm 6 — Konu 26: BLoC/Cubit Pattern (`flutter_bloc`)
- Bölüm 6 — Konu 27: GetX (Tartışmalı Ama Yaygın)
- Bölüm 6 — Konu 28: Karşılaştırma — Hangi Projede Hangisi?
- Bölüm 7 — Konu 29: Navigator 2.0 (Router, RouteInformationParser, RouterDelegate)
- Bölüm 7 — Konu 30: `go_router` Paketi (Pratik ve Modern Çözüm)
- Bölüm 7 — Konu 31: Deep Linking
- Bölüm 7 — Konu 32: Repository Pattern, Katmanlı Mimari (Data/Domain/Presentation)
- Bölüm 7 — Konu 33: Dependency Injection (`get_it`, `injectable`)
- Bölüm 7 — Konu 34: Clean Architecture Uyarlaması, SOLID Prensipleri
- Bölüm 8 — Konu 35: Implicit Animasyonlar
- Bölüm 8 — Konu 36: Explicit Animasyonlar (`AnimationController`, `Tween`, `Curve`)
- Bölüm 8 — Konu 37: Hero Animasyonları
- Bölüm 8 — Konu 38: `CustomPainter` / `Canvas`
- Bölüm 8 — Konu 39: Rive / Lottie Entegrasyonu
- Bölüm 9 — Konu 40: Platform Channels
- Bölüm 9 — Konu 41: Permission Yönetimi (`permission_handler`)
- Bölüm 9 — Konu 42: Kamera, Konum, Sensörler
- Bölüm 9 — Konu 43: Push Notification (Firebase Cloud Messaging)
- Bölüm 9 — Konu 44: Android/iOS Build Sistemleri
- Bölüm 10 — Konu 45: Test Yazımı (Unit, Widget, Integration, Golden)
- Bölüm 10 — Konu 46: CI/CD
- Bölüm 10 — Konu 47: Rebuild Optimizasyonu (`const`, `key` Kullanımı)
- Bölüm 10 — Konu 48: DevTools Profiling
- Bölüm 10 — Konu 49: Lazy Loading, Pagination, Büyük Liste Performansı
- Bölüm 11 — Konu 50: Firebase Ekosistemi (Auth, Firestore, Storage, Functions)
- Bölüm 11 — Konu 51: Supabase Alternatifi
- Bölüm 11 — Konu 52: GraphQL (Opsiyonel)
- Bölüm 11 — Konu 53: Store Yayınlama Süreci (İmzalama, Listing, Versiyonlama)
- Bölüm 11 — Konu 54: App Size, Obfuscation, Flavor Yönetimi
- Bölüm 12 — Konu 55: Flutter Web / Desktop
- Bölüm 12 — Konu 56: Custom `RenderObject` Yazımı
- Bölüm 12 — Konu 57: Engine Mimarisi Derinlemesine (Impeller vs Skia)
- Bölüm 12 — Konu 58: Plugin Geliştirme, pub.dev'e Paket Yayınlama
- Bölüm 12 — Konu 59: Monorepo Mimarisi (Melos)
- Bölüm 12 — Konu 60: Erişilebilirlik (Accessibility) Derinlemesine
- Bölüm 12 — Konu 61: Yerelleştirme (Localization / i18n)
İçindekiler 10 başlık
- Sorun: Map<String, dynamic> ile Çalışmanın Riskleri
- Manuel Model Sınıfı — fromJson/toJson
- Listelerle Çalışma
- Manuel Yazmanın Sorunu — Tekrarlayan, Sıkıcı Kod
- json_serializable — Otomatik Üretim
- freezed — Immutable Modeller + JSON Serialization Bir Arada
- Manuel vs json_serializable vs freezed — Ne Zaman Hangisi?
- 🎯 Bu Dersten Çıkarılması Gerekenler
- 📝 Ödevler
- 🎮 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
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
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ı:
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!
}final gonderi = await gonderiGetir();
print(gonderi.title); // ✅ IDE, 'title' alanının VAR OLDUĞUNU biliyor, yazım hatası imkansızArtı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
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
flutter pub add json_annotation
flutter pub add --dev json_serializable build_runnerimport '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);
}dart run build_runner buildBu, 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
flutter pub add freezed_annotation
flutter pub add --dev build_runner freezedimport '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:
==vehashCode(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ğimizTextStyle.copyWith()kalıbının, kendi modellerin için otomatik üretilmiş hali!
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ştiManuel 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_runnerilefromJson/toJsonkodunu 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 olarakcopyWith(),==,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/toJsonekle. - [ ] Bir JSON listesini,
.map()ile model nesnelerinin listesine dönüştür. - [ ]
json_serializablekurup, aynı modeli@JsonSerializable()ile yeniden yaz,build_runnerile kod üret, üretilen dosyayı incele. - [ ]
freezedkurup, aynı modeli@freezedile 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/postsAPI'sinden gönderi listesini çek (httppaketi).- Ham JSON'u, kendi yazdığın
Gonderimodel sınıfına (fromJsonile) dönüştür. FutureBuilder+initState()/late finalkalıbıyla, listeyi birListView.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)