Search code examples
flutterdartriverpod

riverpod FutureProvider will keep firing after adding .family modifier


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.