Ana içeriğe atla

StateProvider

StateProvider, durumunu değiştirmek için bir yol sunan bir provider'dır. Çok basit kullanım durumları için bir StateNotifier sınıfı yazmak zorunda kalmamak için tasarlanmış bir StateNotifierProvider'ın basitleştirilmiş halidir.

StateProvider öncelikle basit değişkenlerin kullanıcı arayüzü tarafından değiştirilmesine izin vermek için vardır. StateProviderın durumu genellikle şu türlerden biridir:

  • bir enum, bir filtre türü olarak
  • bir String, genellikle bir metin alanının ham içeriği olarak
  • bir boolean, onay kutuları için
  • bir sayı, sayfalama veya yaş form alanları için

StateProviderı şu durumlarda kullanmamalısınız:

  • durumunuzun doğrulama mantığına ihtiyacı varsa
  • durumunuz karmaşık bir nesne ise (örneğin özel bir sınıf, Liste/Harita, ...)
  • durumunuzu değiştirme mantığı, basit bir count++'dan daha gelişmişse.

Daha gelişmiş durumlar için, StateNotifierProvider kullanmayı ve bir StateNotifier sınıfı oluşturmayı düşünün. Kod miktarı biraz daha fazla olabilir, ancak özel bir StateNotifier sınıfına sahip olmak, projenizin uzun vadeli bakım yeteneği için kritik öneme sahiptir, çünkü durumunuzun iş mantığını tek bir yerde merkezileştirir.

Kullanım örneği: Bir açılır menü kullanarak filtre türünü değiştirme

StateProviderın gerçek dünyadaki bir kullanım durumu, açılır menüler/metin ve alanlar/onay kutuları gibi basit form bileşenlerinin durumunu yönetmek olacaktır.

Özellikle, bir ürün listesini fiyat veya ada göre sıralamanıza izin verecek bir açılır menü uygulamak için StateProviderı nasıl kullanacağımızı göreceğiz.

Basitlik adına, elde edeceğimiz ürün listesi doğrudan uygulamada oluşturulacak ve aşağıdaki gibi olacaktır:


class Product {
Product({required this.name, required this.price});

final String name;
final double price;
}

final _products = [
Product(name: 'iPhone', price: 999),
Product(name: 'cookie', price: 2),
Product(name: 'ps5', price: 500),
];

final productsProvider = Provider<List<Product>>((ref) {
return _products;
});

Gerçek dünyadaki bir uygulamada, bu liste genellikle bir ağ isteği yaparken FutureProvider kullanılarak elde edilecektir.

Kullanıcı arayüzü, ürün listesini şu şekilde gösterebilir:


Widget build(BuildContext context, WidgetRef ref) {
final products = ref.watch(productsProvider);
return Scaffold(
body: ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return ListTile(
title: Text(product.name),
subtitle: Text('${product.price} \$'),
);
},
),
);
}

Şimdi temeli tamamladığımıza göre, ürünlerimizi fiyat veya ada göre sıralamamıza izin verecek bir açılır menü ekleyebiliriz. Bunun için DropDownButton kullanacağız.

// Bir filtre türünü temsil eden bir enum.
enum ProductSortType {
name,
price,
}

Widget build(BuildContext context, WidgetRef ref) {
final products = ref.watch(productsProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Ürünler'),
actions: [
DropdownButton<ProductSortType>(
value: ProductSortType.price,
onChanged: (value) {},
items: const [
DropdownMenuItem(
value: ProductSortType.name,
child: Icon(Icons.sort_by_alpha),
),
DropdownMenuItem(
value: ProductSortType.price,
child: Icon(Icons.sort),
),
],
),
],
),
body: ListView.builder(
// ...
),
);
}

Artık bir açılır menümüz olduğuna göre, bir StateProvider oluşturalım ve açılır menünün durumunu provider'larımızla senkronize edelim.

Öncelikle, StateProviderı oluşturalım:


final productSortTypeProvider = StateProvider<ProductSortType>(
// Devolvemos el tipo de clasificación predeterminado, aquí `name`.
(ref) => ProductSortType.name,
);

Ardından, bu provider'ı açılır menümüzle şu şekilde bağlayabiliriz:

DropdownButton<ProductSortType>(
// Cuando cambia el tipo de clasificación, esto reconstruirá el dropdown
// para actualizar el icono que se muestra.
value: ref.watch(productSortTypeProvider),
// Cuando el usuario interactúa con el dropdown, actualizamos el estado del provider.
onChanged: (value) =>
ref.read(productSortTypeProvider.notifier).state = value!,
items: [
// ...
],
),

Bununla, artık sıralama türünü değiştirebilmemiz gerekir. Bu, ürün listesi üzerinde henüz bir etkisi yok! Şimdi son aşama: ürün listesini sıralamak için productsProviderımızı güncelleme zamanı.

Bunu gerçekleştirmek için önemli bir bileşen, ref.watch kullanmaktır, böylece productsProviderımız sıralama türünü alır ve sıralama türü değiştiğinde ürün listesini yeniden hesaplar.

Uygulama şu şekilde olacaktır:


final productsProvider = Provider<List<Product>>((ref) {
final sortType = ref.watch(productSortTypeProvider);
switch (sortType) {
case ProductSortType.name:
return _products.sorted((a, b) => a.name.compareTo(b.name));
case ProductSortType.price:
return _products.sorted((a, b) => a.price.compareTo(b.price));
}
});

Hepsi bu kadar! Bu değişiklik, sıralama türü değiştiğinde kullanıcı arayüzünün otomatik olarak ürün listesini yeniden oluşturmasını sağlar.

İşte Dartpad'deki tam örnek:

Provider'ı iki kez okumadan önceki değere göre durumu nasıl güncelleyebilirsiniz Bazen, bir StateProviderın durumunu önceki değere göre güncellemek isteriz. Doğal olarak, şu şekilde yazabilirsiniz:

final counterProvider = StateProvider<int>((ref) => 0);

class HomeView extends ConsumerWidget {
const HomeView({super.key});


Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
// Estamos actualizando el estado del valor anterior, terminamos leyendo
// el provider ¡dos veces!
ref.read(counterProvider.notifier).state = ref.read(counterProvider.notifier).state + 1;
},
),
);
}
}

Bu kod parçasında özel bir yanlışlık olmasa da, sözdizimi biraz rahatsız edicidir.

Sözdizimini biraz iyileştirmek için, update işlevini kullanabiliriz. Bu işlev, mevcut durumu alacak bir geri çağırma alır ve yeni durumu döndürmesi beklenir. Önceki kodumuzu şu şekilde yeniden düzenlemek için kullanabiliriz:


final counterProvider = StateProvider<int>((ref) => 0);

class HomeView extends ConsumerWidget {
const HomeView({super.key});


Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
ref.read(counterProvider.notifier).update((state) => state + 1);
},
),
);
}
}

Bu değişiklik, aynı etkiyi sağlayarak sözdizimini biraz iyileştirir.