3

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();
  }
}

2 Answers2

3

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.

Paul Halliday
  • 276
  • 1
  • 3
  • Thanks! This helped me a lot! – Golam Sarwar Shakil Oct 22 '21 at 20:36
  • 1
    It is critical that your custom class implements the == and hashCode overrides or you could get into an endless rebuild loop, hammering your api or firestore db. If your classes are marked up using freezed, you're good to go. Otherwise, implement the override bool operator ==(dynamic other) and int get hashCode => Object.hash override methods. – Troy Busot Dec 28 '22 at 17:49
0

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]));
Chatura Dilan
  • 1,502
  • 1
  • 16
  • 29