Johny Gates
Johny Gates

Reputation: 135

Usage of ref.read() and ref.watch() inside OnPressed function in Flutter

The Riverpod documentation clearly states that:

The watch method should not be called asynchronously, like inside an onPressed of an ElevatedButton. Nor should it be used inside initState and other State life-cycles.
In those cases, consider using ref.read instead.

You might be tempted to use ref.read to optimize the performance of a widget by doing: #
For example instead of:

@riverpod
class Counter extends _$Counter {
  @override
  int build() => 0;
  void increment() => state = state + 1;
}

Widget build(BuildContext context, WidgetRef ref) {
  // use "read" to ignore updates on a provider
  final counter = ref.read(counterProvider.notifier);
  return ElevatedButton(
    onPressed: counter.increment,
    child: const Text('button'),
  );
}

we could do:

@riverpod
class Counter extends _$Counter {
  @override
  int build() => 0;
  void increment() => state = state + 1;
}

Widget build(BuildContext context, WidgetRef ref) {
  Counter counter = ref.watch(counterProvider.notifier);
  return ElevatedButton(
    onPressed: () => counter.increment(),
    child: const Text('button'),
  );
}

Both snippets achieve the same effect: our button will not rebuild when the counter increments.
On the other hand, the second approach supports cases where the counter is reset.

The documentation confuses me with using or not using ref.watch() inside onPressed.

There are a few more things I needed answers for:

// inside build
var notif = ref.read|watch(myProvider.notifier);
// somewhere inside return statement in a button's onPressed:
{
    notif.myMethod();
}

Which approach must be preferred in which cases? And if latter method is more prone to unexpected outcomes based on the differences, How?

Upvotes: 1

Views: 2045

Answers (1)

Qwadrox
Qwadrox

Reputation: 808

  • Avoiding read method inside the build method: This refers to avoiding calling ref.read directly inside the build method before the return statement. Using ref.read elsewhere, like in other lifecycle methods, is generally okay, or inside an onpressed.

  • watch method inside onPressed: If you use ref.watch inside the onPressed callback, you are watching the provider every time the button is pressed. It could lead to unexpected behavior, especially if the provider changes outside of the build method. Instead, you should use ref.read inside onPressed since you're merely reading the provider's value, not watching for changes.

  • Avoiding watch for async calls: This advice generally applies to both providers (ref.watch(myProvider)) and notifiers (ref.watch(myProvider.notifier)). You typically use watch within the build method to rebuild the widget when the provider changes, not in callbacks like onPressed. Or inside other providers.

  • Watching notifiers: You are correct that watching notifiers themselves (e.g., ref.watch(myProvider.notifier)) doesn't necessarily cause rebuilds, as it depends on how the provider is structured. However, the general advice to avoid using watch in callbacks like onPressed still applies, as it can lead to unexpected behavior.

  • What difference does it make? Please check this answer from Rémi Rousselet

I'd like to emphasize that the architecture of your app should govern the correct usage of your providers. It might be beneficial to categorize your UI relates providers into two distinct groups: Controller providers and State-holding providers.

Controller Providers: These providers would primarily involve calling void methods, reading other providers, triggering state changes, and managing navigation. Since these providers would merely consist of a class with methods, there's no need to watch for changes from them. They would be implemented using simple Providers.

State-holding Providers: These providers can be monitored for changes, and they play a crucial role in maintaining and reflecting the state within your application. Before you begin building, I would advise exploring some potential architectures to ensure you're on the right track. Here's a helpful discussion that can guide you: Riverpod Discussion on Architecture. By dividing your providers in this manner, you can cultivate a more coherent and effective design for your application.

Upvotes: 1

Related Questions