riverpod FutureProvider will keep firing after adding .family modifier

Issue

so I have future provider like this

final additionalCostsProvider = FutureProvider.autoDispose.family<List<ExtraCost>, List<String>>((ref, cartCodes) {
  final cartAPI = ref.watch(cartAPIProvider);
  return cartAPI.getAdditionalCosts(cartCodes: cartCodes);
});

as you can see, I pass cart codes ( List<String> ) as a parameter for my FutureProvider

and then I use it like this

@override
  Widget build(context, ref) {
    final List<String> cartCodes = carts.map((cart) => cart.code).toList();
    final additionalCostsProviderAsynValue = ref.watch(additionalCostsProvider(cartCodes));

    return Scaffold(
      body: additionalCostsProviderAsynValue.when(
        loading: () => CircularProgressIndicator(),
        error: (error, stackTrace) {
          return ErrorDisplay(
            onButtonClicked: () => ref.refresh(additionalCostsProvider(cartCodes)),
          );
        },
        data: (results) {

          return Container();

        },
      ),
    );
  }

and then it will make my app request to the server over and over again.

I believe the problem is on the .family modifier. because if change the FutureProvider without using .family like the code below, the problem will not occurred.

final additionalCostsProvider = FutureProvider.autoDispose<List<ExtraCost>>((ref) {
  final cartAPI = ref.watch(cartAPIProvider);
  return cartAPI.getAdditionalCosts(cartCodes: ["BA3131"]); // <--- if I hardcode it in here, the problem will not occurred
});

Solution

https://riverpod.dev/docs/concepts/modifiers/family/#parameter-restrictions

This is the official documents for the Riverpod modifier .family.

For families to work correctly, it is critical for the parameter passed to a
provider to have a consistent hashCode and ==.

Since it is constant, you have to declare a class for it, you can create a class like below, using package Equatable.

class ExtraCostParameter extends Equatable {
  final List<String> cartCodesList;

  const ExtraCostParameter({required this.cartCodesList});

  @override
  List<Object?> get props => [cartCodesList];
}

And your new future provider will look like this.

final additionalCostsProvider = FutureProvider.autoDispose.family<List<ExtraCost>, ExtraCostParameter>((ref, param) {
  final cartAPI = ref.watch(cartAPIProvider);
  return cartAPI.getAdditionalCosts(cartCodes: param.cartCodesList);
});

On the other hand, if your List<String> is not constant, you may want to use .autoDispose.

Hopefully it works.

Answered By – KeroJohn97

Answer Checked By – David Goodson (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *