2.0'dan 3.0'a geçiş
Değişikliklerin listesi için lütfen Riverpod 3.0'da neler yeni sayfasına bakın.
Riverpod 3.0, kodunuzu güncellemenizi gerektirebilecek bir dizi kırıcı değişiklik getiriyor. Bunlar genel olarak nispeten küçük değişikliklerdir, ancak bu sayfayı dikkatle okumanızı öneririz.
Bu geçişin sorunsuz olması amaçlanmıştır.
Belirsiz bir nokta varsa veya geçişi zor olan bir senaryoyla karşılaştıysanız,
lütfen bir issue açın.
Geçişin mümkün olduğunca sorunsuz olması bizim için önemlidir; bu yüzden size yardımcı olmak, geçiş rehberini iyileştirmek, hatta geçişi kolaylaştıracak yardımcılar eklemek için elimizden geleni yapacağız.
Otomatik yeniden deneme
Riverpod 3.0 artık başarısız olan provider'ları varsayılan olarak otomatik olarak yeniden deniyor. Yani bir provider değerini hesaplamayı başaramazsa, başarılı olana kadar otomatik olarak yeniden denenecektir.
Genel olarak bu iyi bir şeydir; uygulamanızı geçici hatalara karşı daha dayanıklı hale getirir. Yine de bazı durumlarda bu davranışı devre dışı bırakmak/özelleştirmek isteyebilirsiniz.
Otomatik yeniden denemeyi global olarak devre dışı bırakmak için bunu ProviderContainer/ProviderScope üzerinde yapabilirsiniz:
- ProviderScope
- ProviderContainer
void main() {
runApp(
ProviderScope(
// Hiçbir provider'ı asla yeniden deneme
retry: (retryCount, error) => null,
child: MyApp(),
),
);
}
void main() {
final container = ProviderContainer(
// Hiçbir provider'ı asla yeniden deneme
retry: (retryCount, error) => null,
);
}
Alternatif olarak, provider'ın retry parametresini kullanarak otomatik yeniden denemeyi provider bazında devre dışı bırakabilirsiniz:
- riverpod
- riverpod_generator
final todoListProvider = NotifierProvider<TodoList, List<Todo>>(
TodoList.new,
// Bu provider'ı asla yeniden deneme
retry: (retryCount, error) => null,
);
// Bu provider'ı asla yeniden deneme
Duration? retry(int retryCount, Object error) => null;
(retry: retry)
class TodoList extends _$TodoList {
List<Todo> build() => [];
}
Görünüm dışındaki provider'lar duraklatılır
Riverpod 3.0'da görünüm dışındaki provider'lar varsayılan olarak duraklatılır.
Şu anda bu davranışı global olarak devre dışı bırakmanın bir yolu yok, ancak duraklatma davranışını TickerMode widget'ını kullanarak consumer düzeyinde denetleyebilirsiniz.
class MyWidget extends StatelessWidget {
Widget build(BuildContext context) {
return TickerMode(
enabled: true, // Alt ağaçtaki hiçbir dinleyiciyi asla duraklatma.
child: Consumer(
builder: (context, ref, child) {
// Bu "watch", TickerMode kaldırılana kadar otomatik
// duraklatma davranışını izlemeyecek.
final value = ref.watch(myProvider);
return Text(value.toString());
},
),
);
}
}
StateProvider, StateNotifierProvider ve ChangeNotifierProvider yeni bir import'a taşındı
Riverpod 3.0'da StateProvider, StateNotifierProvider ve ChangeNotifierProvider "legacy" (eski) kabul edilir.
Kaldırılmadılar, ancak artık ana API'nin bir parçası değiller. Bunun amacı, yeni Notifier
API'si lehine bunların kullanımını caydırmaktır.
Bunları kullanmaya devam etmek için import'larınızı aşağıdakilerden biriyle değiştirmeniz gerekir:
import 'package:hooks_riverpod/legacy.dart';
import 'package:flutter_riverpod/legacy.dart';
import 'package:riverpod/legacy.dart';
Provider'ların tamamı artık güncellemeleri filtrelemek için == kullanıyor
Önceden Riverpod, provider'lara gelen güncellemeleri filtreleme biçiminde tutarsızdı.
Bazı provider'lar güncellemeleri filtrelemek için == kullanırken, bazıları identical kullanıyordu.
Riverpod 3.0'da artık tüm provider'lar güncellemeleri filtrelemek için == kullanıyor.
Bu değişiklikten etkilenmenizin en olası yolu StreamProvider/StreamNotifier kullanmaktır;
çünkü artık stream değerleri == ile filtrelenecek.
İhtiyaç duyarsanız, davranışı özelleştirmek için Notifier.updateShouldNotify metodunu geçersiz kılabilirsiniz.
- riverpod
- riverpod_generator
class TodoList extends StreamNotifier<Todo> {
Stream<Todo> build() => Stream(...);
bool updateShouldNotify(AsyncValue<Todo> previous, AsyncValue<Todo> next) {
// Özel uygulama
return true;
}
}
class TodoList extends _$TodoList {
Stream<Todo> build() => Stream(...);
bool updateShouldNotify(AsyncValue<Todo> previous, AsyncValue<Todo> next) {
// Özel uygulama
return true;
}
}
Bir Notifier kullanmadığınız senaryoda, provider'ınızı notifier karşılığına dönüştürebilirsiniz
(Örneğin StreamProvider'ı StreamNotifierProvider'a çevirmek gibi).
ProviderObserver'ın arayüzü biraz değişti
mutation'lar uğruna, ProviderObserver arayüzü biraz değişti.
ProviderContainer ve ProviderBase için iki ayrı parametre yerine,
tek bir ProviderObserverContext nesnesi aktarılıyor.
Bu nesne hem container'ı, hem provider'ı, hem de ek bilgileri (ilişkili mutation gibi) içerir.
Geçiş yapmak için observer'larınızın tüm metotlarını şu şekilde güncellemeniz gerekir:
class MyObserver extends ProviderObserver {
@override
- void didAddProvider(ProviderBase provider, Object? value, ProviderContainer container) {
+ void didAddProvider(ProviderObserverContext context, Object? value) {
// ...
}
}
Sadeleştirilmiş Ref ve kaldırılan Ref alt sınıfları
Sadeleştirme adına, Ref tip parametresini kaybetti ve tip parametresini kullanan tüm özellikler/metotlar Notifier'lara taşındı.
Daha somut olarak, ProviderRef.state, Ref.listenSelf ve FutureProviderRef.future sırasıyla
Notifier.state, Notifier.listenSelf ve AsyncNotifier.future ile değiştirilmelidir.
- riverpod
- riverpod_generator
// Önce:
final valueProvider = FutureProvider<int>((ref) async {
ref.listen(anotherProvider, (previous, next) {
ref.state++;
});
ref.listenSelf((previous, next) {
print('Log: $previous -> $next');
});
ref.future.then((value) {
print('Future: $value');
});
return 0;
});
// Sonra
class Value extends AsyncNotifier<int> {
Future<int> build() async {
ref.listen(anotherProvider, (previous, next) {
ref.state++;
});
listenSelf((previous, next) {
print('Log: $previous -> $next');
});
future.then((value) {
print('Future: $value');
});
return 0;
}
}
final valueProvider = AsyncNotifierProvider<Value, int>(Value.new);
// Önce:
Future<int> value(ValueRef ref) async {
ref.listen(anotherProvider, (previous, next) {
ref.state++;
});
ref.listenSelf((previous, next) {
print('Log: $previous -> $next');
});
ref.future.then((value) {
print('Future: $value');
});
return 0;
}
// Sonra
class Value extends _$Value {
Future<int> build() async {
ref.listen(anotherProvider, (previous, next) {
ref.state++;
});
listenSelf((previous, next) {
print('Log: $previous -> $next');
});
future.then((value) {
print('Future: $value');
});
return 0;
}
}
Benzer şekilde, tüm Ref alt sınıfları kaldırıldı (bunlarla sınırlı olmamak üzere ProviderRef, FutureProviderRef vb.).
Bu, öncelikli olarak kod üretimini etkiler. MyProviderRef yerine artık doğrudan Ref kullanabilirsiniz:
@riverpod
-int example(ExampleRef ref) {
+int example(Ref ref) {
// ...
}
AutoDispose arayüzleri kaldırıldı.
Otomatik yok etme özelliği sadeleştirildi. Tüm arayüzlerin bir kopyasına dayanmak yerine,
arayüzler birleştirildi. Kısacası, AutoDisposeProvider, AutoDisposeNotifier vb. yerine
artık Provider, Notifier vb. var. Davranış aynı, ancak API sadeleşti.
Kolayca geçiş yapmak için, büyük/küçük harfe duyarlı bir değiştirme ile AutoDispose ifadesini (boş dize) ile değiştirebilirsiniz.
Notifier'ların family varyantı kaldırıldı
Bir önceki maddeye benzer şekilde, Notifier'ların family varyantı da kaldırıldı.
Artık yalnızca Notifier/AsyncNotifier/StreamNotifier kullanıyoruz; FamilyNotifier/... ise kaldırıldı.
Geçiş yapmak için şunları değiştirmeniz gerekecek:
FamilyNotifier->NotifierFamilyAsyncNotifier->AsyncNotifierFamilyStreamNotifier->StreamNotifier
Ardından şunları yapmanız gerekecek:
buildmetodundan parametreyi kaldırın- Notifier'ınıza bir kurucu (constructor) ekleyin
Bu geçişin bir örneği şöyledir:
final provider = NotifierProvider.family<CounterNotifier, int, String>(CounterNotifier.new);
-class CounterNotifier extends FamilyNotifier<int, String> {
+class CounterNotifier extends Notifier<int> {
+ CounterNotifier(this.arg);
+ final String arg;
@override
- int build(String arg) {
+ int build() {
// `arg`i gerektiği gibi kullanın
return 0;
}
}
Provider hataları artık ProviderException olarak yeniden fırlatılıyor
Riverpod 3.0'da tüm provider hataları ProviderException olarak yeniden fırlatılır.
Yani bir provider değerini hesaplamayı başaramazsa, onu okumak orijinal hata yerine bir ProviderException fırlatacaktır.
Belirli hataları ele almak için orijinal hata tipine güveniyorduysanız bu sizi etkileyebilir.
Geçiş yapmak için ProviderExceptionı yakalayıp orijinal hatayı içinden çıkarabilirsiniz:
try {
await ref.read(myProvider.future);
-} on NotFoundException {
- // NotFoundException'ı ele al
+} on ProviderException catch (e) {
+ if (e.exception is NotFoundException) {
+ // NotFoundException'ı ele al
+ }
}
Bu yalnızca, böyle bir hatayı ele almak için açıkça try/catch'e güveniyorduysanız gereklidir.
Hataları kontrol etmek için [AsyncValue] kullanıyorsanız hiçbir şeyi değiştirmenize gerek yok:
AsyncValue<int> value = ref.watch(myProvider);
if (value.error is NotFoundException) {
// NotFoundException'ı ele al
// Bu bugün hâlâ çalışıyor
}