Result sinfi: sealed class və tam switch
Rəsmi Flutter sənədi Result-un konkret formasını verir və o, Dart 3-ün sealed siniflərinə söykənir:
sealed class Result<T>— bazadır;sealedolduğu üçün onun bütün alt tipləri eyni kitabxanada olmalıdır və kompilyator onları tanıyır.Ok<T>— uğurlu nəticə,valuedaşıyır.Error<T>— uğursuzluq,Exceptiondaşıyır.- İki
const factorykonstruktoru:Result.ok(value)vəResult.error(exception).
sealed-in verdiyi əsas şey: switch bütün alt tipləri əhatə etmədikdə kompilyator xəbərdarlıq edir. Yəni "xəta halını yazmağı unutdum" səhvi kompilyasiya vaxtında tutulur, məhsul rejimində deyil.
Ok və Error isə final elan olunur — onların da alt tipi olmur, deməli switch üçün iki hal həmişə kifayətdir.
// fayl: lib/utils/result.dart
/// Uğur (`Ok`) və uğursuzluq (`Error`) hallarını bir tipdə birləşdirir.
sealed class Result<T> {
const Result();
/// Uğurlu nəticə: [value] daşıyır.
const factory Result.ok(T value) = Ok._;
/// Uğursuz nəticə: [error] daşıyır.
const factory Result.error(Exception error) = Error._;
}
/// Uğurlu nəticə.
final class Ok<T> extends Result<T> {
const Ok._(this.value);
final T value;
}
/// Uğursuz nəticə.
final class Error<T> extends Result<T> {
const Error._(this.error);
final Exception error;
}
// ══ İstifadə: switch ifadəsi ══
final Result<UserProfile> result = await repository.fetchProfile();
switch (result) {
case Ok<UserProfile>():
userProfile = result.value;
case Error<UserProfile>():
error = result.error;
}
// ══ Daha yığcam: pattern ilə dəyəri birbaşa çıxarmaq ══
final message = switch (result) {
Ok(:final value) => 'Salam, ${value.name}',
Error(:final error) => 'Xəta: $error',
};
// ══ Yalnız bir hal lazımdırsa: if-case ══
if (result case Ok(:final value)) {
print(value.name);
}
// ══ Kompilyator nə tutur ══
// Aşağıdaki switch `Error` halını əhatə etmir:
// final x = switch (result) {
// Ok(:final value) => value.name,
// };
// → "The type 'Result<UserProfile>' is not exhaustively matched"
// Yəni xəta halını unutmaq KOMPİLYASİYA xətasıdır.Rəsmi sənədin verdiyi `Result` sinfi və `switch` ilə istifadəsi.
Adlandırma tələsi. Rəsmi nümunədə uğursuzluq sinfinin adı Error-dur — və bu ad Dart-ın öz dart:core Error sinfi ilə üst-üstə düşür. Həmin faylda Error yazdıqda artıq Result-un Error-u nəzərdə tutulur; dart:core-un Error-una müraciət etmək üçün prefiksli import lazım gəlir.
Bu, kompilyasiya xətası deyil, lakin praktik qarışıqlıq mənbəyidir — xüsusilə catch (e) { if (e is Error) ... } kimi kodda.
Üç praktik variant:
- Rəsmi adları saxlamaq (
Ok/Error) və faylı kiçik, təmiz saxlamaq. Ok/Err— qısa və qarışmır.Success<T>/FailureResult<T>— daha uzun, lakin oxuyarkən şübhə qalmır.
Hansı seçilirsə, layihə boyu bir ad dəsti olsun; iki fərqli Result tipi olan kod bazası ən pis haldır.
| Əlavə | Faydası | Ehtiyat |
|---|---|---|
| `bool get isOk` | Sadə şərtlərdə oxunaqlıdır | Dəyəri çıxarmır — sonra yenə `switch` lazım olur |
| `T? get valueOrNull` | Fallback dəyər üçün rahat: `valueOrNull ?? []` | Xətanı səssizcə udur — yalnız xəta əhəmiyyətsiz olduqda |
| `Result<R> map<R>(R Function(T) f)` | Uğurlu dəyəri çevirir, xətanı ötürür | `f` exception atarsa, o, bükülmür — özün tutmalısan |
| `R fold<R>(R Function(T) onOk, R Function(Exception) onError)` | Hər iki halı bir ifadədə emal edir | `switch` çox vaxt daha oxunaqlıdır — `fold` iki lambda tələb edir |
| `Result<void>` | Dəyər qaytarmayan əməliyyatlar üçün (silmək, göndərmək) | `Ok(null)` deyil, `Result<void>.ok(null)` yazılır — sintaksis qarışa bilər |
Result-un qatlar arasında yolu. Praktikada belə görünür:
1. Repository exception-ları tutur və Result qaytarır: Future<Result<List<Order>>>.
2. Use-case (varsa) Result-u ötürür ya da bir neçəsini birləşdirir.
3. Notifier Result-u switch ilə açır və state-ə (ya da AsyncValue-a) çevirir.
4. Widget artıq Result görmür — yalnız state görür.
Sonuncu bənd vacibdir: Result UI-a çatmamalıdır. Widget-də switch (result) yazmaq iki qatı qarışdırır — widget xəta tipləri barədə qərar verməyə başlayır.
Riverpod ilə birlikdə. AsyncValue da sealed-dir (Riverpod 3), ona görə iki mexanizm bir-birini təkrarlaya bilər. Praktik bölgü:
- Oxuma:
Result-a ehtiyac yoxdur —AsyncNotifier.buildexception-ıAsyncValue.error-a çevirir. - Yazma / əmr:
Resultfaydalıdır — nəticəni notifier-in metodu qaytarır, view isə ona görə snackbar göstərir ya da naviqasiya edir.
Praktika. lib/utils/result.dart faylını yuxarıdaki formada yarat (ad dəstini özün seç, lakin bir dəfə seç). Sonra bir yazma əməliyyatını (submit, delete, redeem kimi) Result üzərinə köçür: repository Result qaytarır, notifier-in metodu onu view-a ötürür, view isə uğur/xəta üçün fərqli snackbar göstərir.
Hazır sayılır: notifier-də try/catch yoxdur, widget-də Result tipi görünmür, və switch-dən bir halı silsən kompilyasiya xətası alırsan (bunu bir dəfə sınayıb geri qaytar — mexanizmin işlədiyini gözlə görmək faydalıdır).
📚 Mənbələr və sənədlər
- Result obyektləri ilə xəta idarəsirəsmidocs.flutter.dev
Yuxarıdaki sinfin mənbəyi: `sealed Result<T>`, `Ok`, `Error` və `switch` ilə istifadə.
- Dart: sinif modifikatorlarırəsmidart.dev
`sealed`-in tam `switch`-i necə təmin etdiyi və `final`-ın nə qadağan etdiyi.
- Dart: pattern-lərrəsmidart.dev
`Ok(:final value)` kimi obyekt pattern-ləri və `if-case` sintaksisi.
- Dart: switch və pattern uyğunluğurəsmidart.dev
`switch` ifadəsi (expression) və tam uyğunluq (exhaustiveness) qaydaları.