Provider'lar nasıl önden başlatılır
Tüm provider'lar varsayılan olarak tembel (lazy) şekilde başlatılır. Yani bir provider ancak ilk kez kullanıldığında başlatılır. Bu, yalnızca uygulamanın belirli bölümlerinde kullanılan provider'lar için oldukça kullanışlıdır.
Ne yazık ki Dart'ın çalışma biçimi gereği (tree shaking amacıyla), bir provider'ı önden başlatılması (eager initialization) gerektiği şeklinde işaretlemenin bir yolu yok. Ancak bir çözüm var: önden başlatmak istediğiniz provider'ları uygulamanızın kökünde zorla okumak.
Önerilen yaklaşım, ProviderScopeunuzun hemen altına yerleştirilmiş bir
Consumer içinde provider'ı basitçe "watch" etmektir:
void main() {
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return const _EagerInitialization(
// TODO: Uygulamanızı burada oluşturun
child: MaterialApp(),
);
}
}
class _EagerInitialization extends ConsumerWidget {
const _EagerInitialization({required this.child});
final Widget child;
Widget build(BuildContext context, WidgetRef ref) {
// Provider'ları dinleyerek önden başlatın.
// "watch" kullanıldığında provider hayatta kalır ve yok edilmez.
ref.watch(myProvider);
return child;
}
}
Başlatmayı yapan consumer'ı "MyApp" içine ya da herkese açık (public) bir widget içine koymayı düşünün. Böylece mantığı main'inizden çıkararak testlerinizin de aynı davranışı kullanmasını sağlarsınız.
SSS
Provider değiştiğinde bu, uygulamamızın tamamını yeniden oluşturmaz mı?
Hayır, öyle olmaz.
Yukarıdaki örnekte, önden başlatmadan sorumlu consumer ayrı bir widget'tır ve
bir child döndürmek dışında hiçbir şey yapmaz.
Kilit nokta, MaterialAppi kendisinin oluşturması yerine bir child
döndürmesidir. Bu da şu anlama gelir: _EagerInitialization yeniden
oluşturulsa bile child değişkeni değişmemiş olur. Bir widget değişmediğinde
ise Flutter onu yeniden oluşturmaz.
Dolayısıyla, başka bir widget da o provider'ı dinlemiyorsa yalnızca
_EagerInitialization yeniden oluşturulur.
Bu yaklaşımı kullanırken yükleme ve hata durumlarını nasıl yönetebilirim?
Yükleme/hata durumlarını bir Consumer içinde normalde nasıl yönetiyorsanız
öyle yönetebilirsiniz.
_EagerInitialization widget'ınız bir provider'ın "loading" durumunda olup
olmadığını kontrol edebilir ve öyleyse child yerine bir
CircularProgressIndicator döndürebilir:
class _EagerInitialization extends ConsumerWidget {
const _EagerInitialization({required this.child});
final Widget child;
Widget build(BuildContext context, WidgetRef ref) {
final result = ref.watch(myProvider);
// Hata ve yükleme durumlarını yönetin
if (result.isLoading) {
return const CircularProgressIndicator();
} else if (result.hasError) {
return const Text('Oopsy!');
}
return child;
}
}
Yükleme/hata durumlarını yönettim ama diğer Consumer'lar hâlâ bir AsyncValue alıyor! Her widget'ta yükleme/hata durumlarını yönetmek zorunda kalmamanın bir yolu var mı?
Provider'ınızın bir AsyncValue sunmamasını sağlamaya çalışmak yerine,
widget'larınızın AsyncValue.requireValue kullanmasını sağlayabilirsiniz.
Bu sayede desen eşleme (pattern matching) yapmadan veriyi doğrudan okursunuz.
Gözden kaçan bir hata olması durumunda ise net bir mesajla bir istisna fırlatır.
- riverpod
- riverpod_generator
// Önden başlatılmış bir provider.
final exampleProvider = FutureProvider<String>((ref) async => 'Hello world');
class MyConsumer extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final result = ref.watch(exampleProvider);
/// Provider doğru şekilde önden başlatıldıysa, veriyi doğrudan
/// "requireValue" ile okuyabiliriz.
return Text(result.requireValue);
}
}
// Önden başlatılmış bir provider.
Future<String> example(Ref ref) async => 'Hello world';
class MyConsumer extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final result = ref.watch(exampleProvider);
/// Provider doğru şekilde önden başlatıldıysa, veriyi doğrudan
/// "requireValue" ile okuyabiliriz.
return Text(result.requireValue);
}
}
Bu durumlarda yükleme/hata durumlarını dışarıya hiç açmamanın yolları olsa da
(kapsamlamaya (scoping) dayanarak), bunu yapmak genellikle önerilmez.
İki provider oluşturmanın ve override kullanmanın getirdiği ek karmaşıklık
zahmetine değmez.