# 22. Hafta — JSON ile Yerel Veri

## Bu hafta ne yapacağız?

Birden fazla alanı olan notları ve tarifleri basit anahtar-değer tercihleriyle saklamak uygun değildir. Bu hafta Dart modellerini JSON nesnelerine dönüştürecek, uygulamanın belge klasöründe gerçek bir `.json` dosyasına yazacak ve uygulama tekrar açıldığında dosyayı okuyarak modelleri yeniden oluşturacağız.

## Kazanımlar

- Model için `toJson` ve `fromJson` dönüşümleri yazma
- `jsonEncode`/`jsonDecode` ile Dart ve JSON arasında dönüşüm yapma
- `path_provider` ile uygulamaya ait belge klasörünü bulma
- `File` ile UTF-8 metin dosyası okuma ve yazma
- Dosya yok, boş veya eski alan eksik durumlarını yönetme
- Depolama sınıfını arayüzden ayırarak test edilebilir kod geliştirme

## Kurulum

```yaml
dependencies:
  flutter:
    sdk: flutter
  path_provider: ^2.1.5
```

## Çözümlü uygulama — JSON Not Defteri

### 1. Modeli JSON'a dönüştürme

```dart
class Not {
  Not({required this.baslik, required this.icerik, required this.tarih});

  final String baslik;
  final String icerik;
  final DateTime tarih;

  Map<String, dynamic> toJson() => {
    'baslik': baslik,
    'icerik': icerik,
    'tarih': tarih.toIso8601String(),
  };

  factory Not.fromJson(Map<String, dynamic> json) => Not(
    baslik: json['baslik'] as String,
    icerik: json['icerik'] as String,
    tarih: DateTime.parse(json['tarih'] as String),
  );
}
```

JSON doğrudan `DateTime` saklayamaz. Bu nedenle tarih ISO metnine çevrilir ve okunurken `DateTime.parse` ile geri alınır.

### 2. Dosya yolunu hazırlama

```dart
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final klasor = await getApplicationDocumentsDirectory();
  final dosya = File('${klasor.path}/notlar.json');
  runApp(NotApp(depo: NotDeposu(dosya)));
}
```

Sabit ve platforma özel bir yol yazmak yerine Android, iOS, Windows veya diğer platformların uygun belge klasörü paketten alınır.

### 3. Listeyi dosyaya yazma

```dart
Future<void> yaz(List<Not> notlar) async {
  await dosya.parent.create(recursive: true);
  final json = const JsonEncoder.withIndent('  ').convert(
    notlar.map((not) => not.toJson()).toList(),
  );
  await dosya.writeAsString(json);
}
```

Girintili JSON, öğrencinin dosyayı açıp veri yapısını okuyabilmesini kolaylaştırır.

### 4. Dosyayı okuyup modelleri üretme

```dart
Future<List<Not>> oku() async {
  if (!await dosya.exists()) return [];
  final metin = await dosya.readAsString();
  if (metin.trim().isEmpty) return [];

  final liste = jsonDecode(metin) as List;
  return liste
      .map((e) => Not.fromJson(Map<String, dynamic>.from(e as Map)))
      .toList();
}
```

## Gerçek uygulama ekranı

Test “Proje fikri” notunu arayüzden eklemiş, gerçek JSON dosyasının içeriğini kontrol etmiş ve uygulamayı aynı dosyayla yeniden kurduğunda notun geri geldiğini doğrulamıştır.

![JSON Not Defteri gerçek Flutter ekranı](../../screenshots/week-22-adapted.png)

## Özgün uygulama — JSON Tarif Kutusu

Tarifte iç içe bir liste ve değişebilir favori bilgisi bulunur:

```dart
class Tarif {
  Tarif({
    required this.ad,
    required this.malzemeler,
    required this.dakika,
    this.favori = false,
  });

  final String ad;
  final List<String> malzemeler;
  final int dakika;
  bool favori;
}
```

### Liste alanını çözümleme

```dart
malzemeler: List<String>.from(json['malzemeler'] as List),
```

### Eski dosyalarla uyumluluk

```dart
favori: (json['favori'] as bool?) ?? false,
```

Önceki sürümde `favori` alanı bulunmuyorsa uygulama çökmek yerine `false` kullanır. Kullanıcı kalp düğmesine bastığında model değişir ve listenin tamamı yeniden dosyaya yazılır.

![JSON Tarif Kutusu gerçek Flutter ekranı](../../screenshots/week-22-original.png)

## Örnek JSON dosyası

```json
[
  {
    "ad": "Meyveli Yoğurt",
    "malzemeler": ["yoğurt", "muz", "yulaf"],
    "dakika": 8,
    "favori": true
  }
]
```

## Hata durumları

- Dosya ilk çalıştırmada yoksa boş liste gösterilir.
- Dosya boşsa `jsonDecode` çağrılmadan boş liste döndürülür.
- Zorunlu alanlar kayıttan önce doğrulanır.
- Yeni isteğe bağlı alanlarda varsayılan değer kullanılır.
- Üretim uygulamasında bozuk JSON için `FormatException` yakalanıp kullanıcıya kurtarma seçeneği sunulmalıdır.

## Uygulama görevleri

### Kolay — Alıntı arşivi

Alıntı, yazar ve tarih alanlarını JSON dosyasına kaydedin; silme özelliği ekleyin.

### Orta — Film listesi

Film adı, tür, puan ve izlendi alanlarını saklayın. Türe ve izlendi durumuna göre filtreleyin.

### İleri — Ders çalışma günlüğü

Ders, konu, süre, tarih ve etiket listesi içeren kayıtları saklayın. Düzenleme, silme, günlük toplam süre ve eski JSON sürümüyle uyumluluk ekleyin.

## Test ve kontrol

- Dosya yokken uygulama açılmalı.
- Türkçe karakterler yazılıp geri okunabilmeli.
- Kayıt sonrasında uygulama yeniden oluşturulduğunda veri görünmeli.
- Silme ve favori değişikliği dosyaya yansımalı.
- Eksik isteğe bağlı alan varsayılan değerle açılmalı.
- Hatalı sayı ve boş zorunlu alan dosyaya yazılmamalı.

## Geri bildirim ve çözüm erişimi

Öğrenci kendi JSON modelini ve dosya deposunu gönderdikten sonra **İpucunu göster** dönüşüm ve dosya akışını açıklar. **Örnek çözümü göster** gerçek Dart kaynaklarını; indirme bağlantısı ise `pubspec.yaml`, modeller ve depolama koduyla eksiksiz projeyi sunar.

## Kaynak projeler

- Konu uygulaması: `06 - Yerel Veri Saklama/Uygulamalar/Uygulama-22-Not Defteri`
- Özgün uygulama: `06 - Yerel Veri Saklama/Uygulamalar/Ozgun-22-Tarif Kutusu`
