Ana içeriğe atla

Family

Riverpod'un en güçlü özelliklerinden biri "Family"lerdir.
Kısaca, bir provider'ın benzersiz bir parametre kombinasyonuna dayanarak birden fazla bağımsız durumla ilişkilendirilmesini sağlar.

Tipik bir kullanım senaryosu, yanıtın bazı parametrelere (kullanıcı ID'si, arama sorgusu veya sayfa numarası gibi) bağlı olduğu uzak bir API'den veri çekmektir. Bu sayede, olası her parametre kombinasyonunu çekmek ve önbelleğe almak için tek bir provider tanımlayabilirsiniz.

Bir family'nin bir provider'ı birden fazla bağımsız duruma nasıl bağladığını gösteren grafik

bilgi

Sıradan provider'lar bir değişkene benzetilebiliyorsa, "family" provider'ları da bir Map'e benzetilebilir.

Bir Family oluşturmak

Bir family tanımlamak, provider tanımını bir parametre alacak şekilde hafifçe değiştirmekten ibarettir.

Fonksiyonel provider'lar için sözdizimi şu şekildedir:

// When not using code-generation, providers can use ".family".
// This adds one generic parameter corresponding to the type of the parameter.
// The initialization function then receives the parameter.
final userProvider = FutureProvider.autoDispose.family<User, String>((ref, id) async {
final dio = Dio();
final response = await dio.get('https://api.example.com/users/$id');

return User.fromJson(response.data);
});

Notifier provider'lar için ise sözdizimi şöyledir:

// With notifiers providers, we also use ".family" and receive and extra
// generic argument.
// The main difference is that the associated Notifier needs to define
// a constructor+field to accept the argument.
final userProvider = AsyncNotifierProvider.autoDispose.family<UserNotifier, User, String>(
UserNotifier.new,
);

class UserNotifier extends AsyncNotifier<User> {
// We store the argument in a field, so that we can use it
UserNotifier(this.id);
final String id;


Future<User> build() async {
final dio = Dio();
final response = await dio.get('https://api.example.com/users/$id');

return User.fromJson(response.data);
}
}
bilgi

Kesinlikle zorunlu olmasa da, family kullanırken Otomatik yok etme özelliğini etkinleştirmeniz şiddetle tavsiye edilir.

Bu sayede, parametre değiştiğinde ve önceki duruma artık ihtiyaç kalmadığında bellek sızıntılarının önüne geçilir.

Bir Family kullanmak

Parametre alan provider'ların kullanımı da biraz farklılaşır.

Uzun lafın kısası, provider'ınızın beklediği parametreleri şu şekilde iletmeniz gerekir:

final user = ref.watch(userProvider('123'));
DİKKAT

İletilen parametrelerin tutarlı bir ==/hashCode davranışına sahip olması gerekir.

"family"yi bir Map olarak düşünün: parametreler anahtar, provider'ın durumu ise değerdir. Dolayısıyla bir parametrenin ==/hashCode değeri değişirse, elde edilen değer de farklı olacaktır.

Bu nedenle aşağıdaki gibi bir kod yanlıştır:

// Hatalı parametre, çünkü `[1, 2, 3] != [1, 2, 3]`
ref.watch(myProvider([1, 2, 3]));

Bu hatayı fark etmenize yardımcı olması için riverpod_lint paketini kullanmanız ve provider_parameters lint kuralını etkinleştirmeniz önerilir. Böylece yukarıdaki kod parçası bir uyarı gösterir. Kurulum adımları için Başlarken bölümüne bakın.

İstediğiniz kadar "family" provider'ı okuyabilirsiniz; hepsi birbirinden bağımsız olacaktır. Dolayısıyla şu kullanım geçerlidir:

class Example extends ConsumerWidget {

Widget build(BuildContext context, WidgetRef ref) {
final user1 = ref.watch(userProvider('123'));
final user2 = ref.watch(userProvider('456'));

// user1 ve user2 birbirinden bağımsızdır.
}
}

Bir family'nin tüm aktif provider'larına erişmek

ProviderContainers/ProviderScopes kullanarak, bir `Family` ile ilişkili tüm provider'lara `allProviders` metodu sayesinde erişebilirsiniz:
for (final reference in ref.container.allProviders(family: userProvider)) {
ref.invalidate(reference.provider);
}

Family'leri geçersiz kılmak

Testlerde bir provider'ı sahte bir örnekle değiştirmek isterken, bir family provider'ını geçersiz kılmak isteyebilirsiniz.

Bu durumda iki seçeneğiniz vardır:

  • Yalnızca belirli bir parametre kombinasyonunu geçersiz kılmak:
    await tester.pumpWidget(
    ProviderScope(
    overrides: [
    userProvider('123').overrideWith((ref) => User(name: 'User 123')),
    ],
    child: const MyApp(),
    ),
    );
  • Tüm parametre kombinasyonlarını geçersiz kılmak:
    await tester.pumpWidget(
    ProviderScope(
    overrides: [
    userProvider.overrideWith((ref, arg) => User(name: 'User $arg')),
    ],
    child: const MyApp(),
    ),
    );