'What is the efficient way to pass arguments to a Riverpod provider each time it gets initialized in Flutter?

I am currently trying to create an instance of a widget's state (ChangeNotifier) using a global auto-disposable ChangeNotifierProvider. The notifier instance takes in a few arguments to initialize each time the UI is built from scratch.

Let's assume we have the following simple state (or notifier):

class SomeState extends ChangeNotifier {
  int _someValue;

  SomeState({required int initialValue})
    : _someValue = initialValue;

  int get someValue => _someValue;

  set someValue(int someValue) {
    _someValue = someValue;
    notifyListeners();
  }
}

I used to use the Provider package before switching to Riverpod, where this could've easily been done like so:

class SomeWidget extends StatelessWidget {
  
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      // Passing 2 into state initializer, which may be
      // obtained from a different state, but not necessarily.
      create: (_) => SomeState(initialValue: 2),
      builder: (context, child) => Consumer<SomeState>(
        builder: (context, state, child) {
          // Will print 2, as it's currently the default value.
          return Text('${state.someValue}');
        },
      ),
    );
  }
}

So with Provider, you can manually call to SomeState constructor with arbitrary arguments when the state is being set up (i.e. provided). However, with Riverpod, it doesn't seem as intuitive to me, mainly because the provider is made to be declared globally:

static final someProvider = ChangeNotifierProvider.autoDispose((ref) => SomeState(2));

Which would end up being used like so:

class SomeWidget extends ConsumerWidget {

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final state = ref.watch(someProvider);
    return Text('${state.someValue}');
  }
}

However, with this approach I can't pass parameters like I did in the example using Provider. I also don't want to use the family modifier because I would need to pass the same parameter each time I read/watch the state, even if it's already created.

If it helps, in my current situation I am trying to pass a function (say String Function()? func) into my state on initialization. It's also not feasible to depend on a different provider in this case which would provide such function.

How could I replicate the same functionality in the Provider example, but with Riverpod?

P.S. Apologies if code has syntax errors, as I hand-typed this and don't have an editor with me at the moment. Also, this is my first post so apologies for lack of clarity or format.



Solution 1:[1]

If you do not want to use family then you can put value in another way by combining two providers.

final someValue = StateProvider((ref) => 0);

final someProvider = ChangeNotifierProvider.autoDispose((ref) {
  final value = ref.watch(someValue);
  return SomeState(value);
});

class SomeState extends ChangeNotifier {
  int _someValue;

  SomeState(int initialValue) : _someValue = initialValue;

  int get someValue => _someValue;

  set someValue(int someValue) {
    _someValue = someValue;
    notifyListeners();
  }
}

USAGE:

// From everywhere you can put new value to your ChangeNotifier.
ref.read(someValue.notifier).state++;

But in your case, it's better to use the `family method. It's cleaner and less complicated.

Solution 2:[2]

Use provider overrides with the param that you need:

First, let's ensure the ProviderScope in the root of the widget-tree.

// Root
ProviderScope(
  child: MaterialApp(...)
)

After, create another one in some widget:

Widget build(BuildContext context) {
  return ProviderScope(
    overrides: [
      someProvider.overrideWithProvider(
        ChangeNotifierProvider.autoDispose((ref) => SomeState(5)),
      ),
    ],
    child: Consumer(
      builder: (context, ref, child) {
        final notifier = ref.watch(someProvider);
        final value = notifier.someValue;
        return Text('$value'); // shows 5 instead of 2
      }
    ),
  );
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Arnas
Solution 2 Frank Moreno