Flutter Riverpod: how to pass multiple arguments in family?

6.8k Views Asked by At

I can pass one argument along with ProviderScope in Riverpod ChangeNotifierProvider family. But I need to pass more than one/multiple arguments/dependencies. For example I have to pass context to access other providers value via context.read(provider) and dependencies from UI widget, may be some more also.

Example here:

final restaurantProvider = ChangeNotifierProvider.family(
  (ref, BuildContext context, Restaurant? restaurant) => RestaurantNotifier(
    context: context,
    restaurant: restaurant,
  ),
);

class RestaurantNotifier extends ChangeNotifier {
  RestaurantNotifier(
      {required BuildContext context, required Restaurant? restaurant}) {
    getPlaceMark(restaurant);
    checkIsSaved(context, restaurant!.id);
  }

  getPlaceMark(Restaurant? restaurant) async {
    if (restaurant!.latitude != null && restaurant.longitude != null) {
      List<Placemark> placemarkData = await LocationHelper.getPlaceMark(
        lat: double.tryParse(restaurant.latitude!)!,
        long: double.tryParse(restaurant.longitude!)!,
      );
      placemark = placemarkData[0];
    }
  }

  checkIsSaved(BuildContext context, int? id) {
    final savedRestaurantsId = context.read(savedRestaurantsIdProvider.state);
    isSaved = savedRestaurantsId.contains(id);
    notifyListeners();
  }
}
4

There are 4 best solutions below

2
On BEST ANSWER

If you want to pass multiple values into the Provider when you create it, you can use the family modifier with a custom type.

For example, in this instance, you may want to replace the String value with a Person value:

final multipleGreetingProvider = Provider.family<String, Person>(
  (_, person) {
    return "Hello, ${person.name} ${person.surname}!";
  },
);

You can now pass a Person value into the Provider when you create it:

// prints "Hello, Paul Halliday!"
sayHello(WidgetRef ref) {
  ref.read(
    multipleGreetingProvider(
      Person('Paul', 'Halliday'),
    )
  );
}

This can be extended infinitely. If you now have multiple classes you'd like to pass in, you can combine this into a logical unit.

For example, this may look like:

class Household {
  final List<Person> members;
  final int rooms;

  Household(this.members, this.rooms);
}

final householdProvider = Provider.family<String, Household>(
  (_, household) {
    return "Household has ${household.rooms} rooms and ${household.members.length} members.";
  },
);

// prints "Household has 3 rooms and 1 members."
householdData(WidgetRef ref) {
  ref.read(householdProvider(
    Household(
      [
        Person('Paul', 'Halliday'),
      ],
      3,
    ),
  ));
}

This shows how to get around the problem of having to pass in multiple values into the Provider using family.

ref.

0
On

If I have to send a list of Strings to the provider, I create a wrapper class to hold the list.

  • You have to either properly override the == operator or use the Equatable package otherwise the provider will keep reloading.
class ListWrapper extends Equatable {
  final List<String> items;

  ListWrapper(this.items);

  @override
  // TODO: implement props
  List<Object?> get props => [items];
}
0
On

This is another way of providing multiple arguments. Use List<dynamic>>

final restaurantProvider = FutureProvider.autoDispose
.family<RecordModel, List<dynamic>>(
    (ref, args) => ref.read(rsProvider).load(args[0], args[1]));
0
On

You can use typedef with Dart 3. It offers easy and simple use with the family parameter.

typedef Parameters= ({String type, int maxPrice});

Then you can use provider like this

final providerName= FutureProvider.family<Activity, Parameters>((ref, arguments) async {
    .......
});

It's pretty simple now

Use it like so:

providerName((type: "a", maxPrice: 1));

Notice the double parentheses.