Flutter Dart kullanıyor , Dart DataClass ve Unions desteğini henüz sunmuyor peki çözüm ne olaki? Doğru bildin ufak bir paket ile bu problemi çözebiliriz ve bu paket başlıktanda anlaşılacağı üzere Freezed .
Data Class?
https://kotlinlang.org/docs/data-classes.html
Data Class yani veri sınıfı henüz Dart tarafından direk olarak desteklenmemekte.
data class Droidim{
...
}
şeklinde bir tanımlama yapamıyoruz.Alternatif olarak elle veya IDE eklentileri ile gerekli kodu otomatik oluşturabiliriz.Örnek amacıyla QuickType yardımıyla bir data class dengi oluşturalım.
Örnek JSON verimiz
{
"greeting": "Welcome to quicktype!",
"instructions": [
"Type or paste JSON here",
"Or choose a sample above",
"quicktype will generate code in your",
"chosen language to parse the sample data"
]
}
JSON verisini API den aldıktan sonra OOP ortamında kullanabilmemiz için kullandığımız kullandığımız model sınıfımız
// To parse this JSON data, do
//
// final droidim = droidimFromJson(jsonString);
import 'dart:convert';
Droidim droidimFromJson(String str) => Droidim.fromJson(json.decode(str));
String droidimToJson(Droidim data) => json.encode(data.toJson());
class Droidim {
Droidim({
this.greeting,
this.instructions,
});
String greeting;
List<String> instructions;
Droidim copyWith({
String greeting,
List<String> instructions,
}) =>
Droidim(
greeting: greeting ?? this.greeting,
instructions: instructions ?? this.instructions,
);
factory Droidim.fromJson(Map<String, dynamic> json) => Droidim(
greeting: json["greeting"],
instructions: List<String>.from(json["instructions"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"greeting": greeting,
"instructions": List<dynamic>.from(instructions.map((x) => x)),
};
}
Artık webten veri çektiğimizde gelen veriyi kolaylıkla Dart sınıfına dönüştürüp kullanabiliriz.
//meselaa
var x = await getData();
Droidim y = Droidim.fromJson(x);
Oluşturulan sınıf ile json verilerini işleyebiliyoruz.Ek olarak copyWidth metodu ile immutable örnekler oluşturabiliyoruz.Peki bunları tek bir satırda yapsak? Kod karmaşıklığını kaldırsak ve kotlinde olduğu gibi sadece parametre ve metodları tanımlasak olmaz mı? Elbette! işte Freezed ‘ın devreye girdiği konulardan biri.
Hadi aynı sınıfı birde Freezed ile oluşturalım.
https://pub.dev/packages/freezed
import 'package:freezed_annotation/freezed_annotation.dart';
part 'droidim.freezed.dart';
part 'droidim.g.dart';
@freezed
class Droidim with _$Droidim {
factory Droidim({required String greeting,required List<String> instructions}) = _Droidim;
factory Droidim.fromJson(Map<String, dynamic> json) =>
_$DroidimFromJson(json);
}
Yukarıdaki kodu IDE henüz gerekli çıktılar üretilmediğinden görmeyecektir. Lakin build runner çalıştırdıktan sonra problem giderilecektir.
flutter pub run build_runner watch
Watch yerine build yazarak tek seferlik çalışmasını sağlayabilirsiniz.
PS C:\Projeler\droidim> flutter pub run build_runner watch
[INFO] Generating build script...
[INFO] Generating build script completed, took 420ms
[INFO] Setting up file watchers...
[INFO] Setting up file watchers completed, took 22ms
[INFO] Waiting for all file watchers to be ready...
[INFO] Waiting for all file watchers to be ready completed, took 160ms
[INFO] Initializing inputs
[INFO] Reading cached asset graph...
[INFO] Reading cached asset graph completed, took 89ms
[INFO] Checking for updates since last build...
[INFO] Checking for updates since last build completed, took 1.3s
[INFO] Running build...
[INFO] 1.0s elapsed, 7/10 actions completed.
[INFO] 2.1s elapsed, 7/10 actions completed.
[INFO] 3.2s elapsed, 7/10 actions completed.
[INFO] 12.0s elapsed, 7/10 actions completed.
[INFO] 13.0s elapsed, 18/21 actions completed.
[INFO] Running build completed, took 13.8s
[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 45ms
[INFO] Succeeded after 13.9s with 5 outputs (24 actions)
Örnek çıktı sonrası oluşturulan Freezed sınıfımız artık gerekli dosyaların oluşturulması ile tanımlanmış hale geldi.
Freezed sınıfımızın bulunduğu klasöre bakarsak **.g.dart ve ** freezed .dart dosyalarını görebiliriz bunlar build_runner ile türetilen dosyalar.Herhangi bir değişiklik yapmayın bu dosyalarda 🙂
Kullanıma geçecek olursak görüldüğü gibi bize copyWidth ve toJson gibi çeşitli özellikleri az yukarıda yazdığımız Freezed sınıfı ile otomatik oluşturabiliriz.
Şimdi gelelim Unions lara
Sınırlı opsiyonlar sunup bu opsiyonlar içerisinde veri tutmak istiyorsak enumlar yetersiz kalmakta işte burada unions lar devreye girmekte.
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'droidim.freezed.dart';
part 'droidim.g.dart';
@freezed
class Droidim with _$Droidim {
factory Droidim({required String greeting,required List<String> instructions}) = _Droidim;
factory Droidim.fromJson(Map<String, dynamic> json) =>
_$DroidimFromJson(json);
const factory Droidim.xTipi() = _xTipi;
const factory Droidim.yTipi({required String isim}) = _yipi;
const factory Droidim.zTipi({required String isim,required String yas}) = _zTipi;
}
Kullanımı ise oldukça basit
pedantic önerilerini kafaya takmazsak görüldüğü gibi aynı tipte farklı verileri tutan alt tipler oluşturduk.
Aslında bu mantık ile BLOC üzerinde yapılan kullanım oldukça güzel bir sonuç ortaya çıkaracak.
Örnek bir Auth bloc a ait statelere bakalım
part of 'auth_bloc.dart';
@freezed
class AuthState with _$AuthState {
const factory AuthState.initial() = _Initial;
const factory AuthState.unAuhtenticated(
{@Default(false) bool showLoginRegisterDirectly}) = _UnAuthenticated;
const factory AuthState.auhtenticated(Oyuncu oyuncu) = _Authenticated;
const factory AuthState.loading() = _Loading;
const factory AuthState.tryLogin(
{required LoginProvider loginProvider,
String? email,
String? pasword}) = _TryLogin;
const factory AuthState.tryRegister(
{required LoginProvider loginProvider,
String? email,
String? pasword}) = _TryRegister;
const factory AuthState.tryLogOut({required bool showAuthPage}) = _TryLogOut;
const factory AuthState.error(String errorMsg) = _Error;
}
Şimdi bunu nasıl yöneteceğiz?
when,maybeWhen , map ve maybeMap
BlocBuilder<AuthBloc, AuthState>(
builder: (context, AuthState state) {
return state.when(
tryLogOut: (s) => Loading(),
initial: () => Loading(),
unAuhtenticated: (s) => LandingPage(),
auhtenticated: (s) => HomeScreen(),
loading: () => Loading(),
error: (s) => Scaffold(
body: Center(
child: Text(s),
),
),
tryLogin: (loginProvider, e, p) => Loading(
isItLoginRegister: true,
),
tryRegister: (loginProvider, email, pasword) => Loading(
isItLoginRegister: true,
),
);
If else yapmadan tüm durumları ele aldık.Böylece hiçbir durumu kaçırmadan hepsine cevap verebildik ve state sınıfı üzerinde ileride yapılacak değişikliklerin neleri etkilediğini görebileceğiz.
Kısaca özetlersek bir API den veri çekiyorsanız veya Firestore vb kullanıyorsanız fromJson toJson metodlarını otomatik yaratmak oldukça zevkli bir hale getiriyor bu süreci.Bu arada json için json_seralizable paketi kullanılmakta ve pubsec e eklenmesi gerekmekte.
Ayrıca BLOC tarafında sunduğu unions lar state management yapısını oldukça güzel bir hale getirmekte.
VS Code ve Android Studio da Freezed desteğ için ayrı ayrı güzel paketlerde bulnmakta bir göz atın derim