I think this is done to GUARANTEE that we are getting the most up-to-date status of the provider. Therefore, one should use ref.read
before any action in (){}. This way we deprive ourselves of the possibility of making mistakes in the future :)
And here's a full example of a possible situation:
void main() => runApp(const ProviderScope(child: MyApp()));
final indexProvider = StateProvider<int>((ref) {
ref.listenSelf((previous, next) {
print(ref.controller.state);
});
return 0;
});
class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
print('build $MyApp');
return MaterialApp(
home: Center(
child: Column(
children: const [
SizedBox(height: 50.0),
ButtonInternalState(),
SizedBox(height: 50.0),
ButtonProviderState(),
],
),
),
);
}
}
class ButtonInternalState extends ConsumerWidget {
const ButtonInternalState({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
print('build $ButtonInternalState');
// Once upon a time, all we had to do was read the data once and not keep track of anything
final StateController<int> indexState = ref.read(indexProvider.state);
return ElevatedButton(
onPressed: () => indexState.state++,
// It would have avoided the mistake
// onPressed: () => ref.read(indexProvider.state).state++,
child: Text('The state our wrong provider - ${indexState.state}'),
);
}
}
class ButtonProviderState extends ConsumerWidget {
const ButtonProviderState({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
print('build $ButtonProviderState');
final int index = ref.watch(indexProvider);
return Column(
children: [
ElevatedButton(
onPressed: () => ref.read(indexProvider.notifier).state++,
child: Text('Here changes state the provider - $index')),
ElevatedButton(
onPressed: () => ref.refresh(indexProvider),
child: const Text('Refresh our provider')),
],
);
}
}