Immutable state: freezed, Equatable, == və rebuild
Immutability Flutter-də estetik seçim deyil — bütün optimizasiya mexanizmlərinin şərtidir. Səbəb sadədir: Flutter və state management library-ləri "nəyin dəyişdiyini" == müqayisəsi ilə müəyyən edir.
Bu mexanizmlərin hər biri ==-dən asılıdır:
InheritedWidget.updateShouldNotify— abunəçilərin oyanması.ValueNotifier—valuesetter-i==ilə müqayisə edir.- Provider-də
Selectorvəcontext.select— seçilən dəyər dəyişməyibsə rebuild yoxdur. - Riverpod-da
selectvə provider-lərin yenidən hesablanması. - Bloc-un təkrar state qaydası —
state == nextStateolduqda dəyişiklik baş vermir. BlocSelector— sənəd seçilən dəyərin immutable olmasını tələb edir.
Mutable obyektdə isə eyni instansiya dəyişdirilir: == fərq görmür, filtr işləmir və nəticə iki uc nöqtədən biri olur — ya UI heç vaxt yenilənmir, ya hər şey həmişə rebuild olunur.
| Alət | Nə verir | Nəyə diqqət |
|---|---|---|
| Əl ilə `==`/`hashCode` | Asılılıq yoxdur | Yeni sahə əlavə edildikdə yaddan çıxır |
| `package:equatable` | `props` siyahısı ilə qısa `==` | `props`-a yeni sahəni əlavə etmək yaddan çıxarsa, müqayisə yalan danışır |
| `package:freezed` | `==`, `hashCode`, `copyWith`, sealed union-lar kod generasiyası ilə | `build_runner` build vaxtı; generated fayllar repo-da idarə olunmalıdır |
| Dart `record`-ları | Hazır dəyər semantikası, kod generasiyası olmadan | Ad verilmiş tip yoxdur; böyük modellər üçün uyğun deyil |
freezed haqqında vacib yenilik. Paketin sənədi when/map metodlarını artıq legacy kimi işarələyir və izahı belədir: Dart 3-də dilin özündə sealed siniflər üzərində pattern matching var, ona görə Freezed-in generasiya etdiyi metodlara etibar etmək lazım deyil. Tövsiyə olunan yol — switch ifadələri və if-case konstruksiyaları.
copyWith isə tam şəkildə generasiya olunmağa davam edir və dərin (nested) formada da işləyir.
Müsahibədə bu detalı bilmək faydalıdır: when/map istifadə edən kod işləyir, lakin yeni kodda Dart 3 pattern matching üstün tutulur.
// 1) Equatable: props siyahısı — yeni sahə əlavə edildikdə buraya da yazılmalıdır.
class CartState extends Equatable {
const CartState({this.items = const [], this.promo});
final List<Item> items;
final String? promo;
CartState copyWith({List<Item>? items, String? promo}) =>
CartState(items: items ?? this.items, promo: promo ?? this.promo);
@override
List<Object?> get props => [items, promo];
}
// 2) freezed: ==, hashCode, copyWith generasiya olunur; sealed union-lar dəstəklənir.
@freezed
sealed class CheckoutState with _$CheckoutState {
const factory CheckoutState.idle() = CheckoutIdle;
const factory CheckoutState.submitting() = CheckoutSubmitting;
const factory CheckoutState.success(Receipt receipt) = CheckoutSuccess;
const factory CheckoutState.failure(String message) = CheckoutFailure;
}
// Dart 3 pattern matching — freezed sənədinin tövsiyə etdiyi yol
// (when/map artıq legacy sayılır).
Widget build(BuildContext context) => switch (state) {
CheckoutIdle() => const CheckoutForm(),
CheckoutSubmitting() => const CircularProgressIndicator(),
CheckoutSuccess(:final receipt) => ReceiptView(receipt: receipt),
CheckoutFailure(:final message) => ErrorView(message: message),
};
// 3) record: kiçik, adsız dəyərlər üçün — kod generasiyası lazım deyil.
typedef SearchFilter = ({String query, int page});
final filter = (query: 'telefon', page: 1); // == dəyər üzrə işləyirImmutable state üç üsulla.
Immutable state-in praktik qiyməti də var: hər dəyişiklik yeni obyekt yaradır. Böyük siyahılarda hər emit-də [...items, item] yazmaq O(n) kopyalama deməkdir. Kiçik siyahılarda bu əhəmiyyətsizdir; minlərlə element olduqda isə səhifələmə, ListView.builder və dəyişiklik sahəsini məhdudlaşdıran struktur (məsələn siyahını id → element şəklində saxlamaq) lazımdır. Müsahibədə bu balansı qeyd etmək "immutable həmişə yaxşıdır" cavabından yetkindir.
📚 Mənbələr və sənədlər
- package:freezedrəsmipub.dev
when/map-in legacy statusu və Dart 3 pattern matching tövsiyəsi burada yazılıb.
- package:equatablerəsmipub.dev
- package:json_serializablerəsmipub.dev
Immutable modellərin JSON-a çevrilməsi — adətən freezed ilə birlikdə istifadə olunur.
- Bloc: state modelləşdirmərəsmibloclibrary.dev
@immutable, copyWith, const və Equatable tövsiyələrinin mənbəyi.