Skip to main content

Consumer Widgets

To read and listen to providers in your Flutter UI, Riverpod replaces standard Flutter widgets with custom subclasses that expose a WidgetRef object.


1. What is WidgetRef?

WidgetRef is the primary interface used to interact with providers from the widget tree. It allows you to:

  • Read provider values using ref.watch().
  • Listen to state transitions using ref.listen().
  • Access provider controllers without listening to changes using ref.read().

2. ConsumerWidget (Stateless replacement)

ConsumerWidget is the direct replacement for standard StatelessWidget. It overrides build to include WidgetRef ref:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'provider-types.md'; // Assumed provider definitions

class WeatherScreen extends ConsumerWidget {
const WeatherScreen({super.key});


Widget build(BuildContext context, WidgetRef ref) {
// Watch provider values reactively (rebuilds widget when data changes)
final weatherAsync = ref.watch(weatherProvider);

return Scaffold(
appBar: AppBar(title: const Text('Weather')),
body: Center(
child: weatherAsync.when(
data: (weather) => Text('Weather: $weather'),
loading: () => const CircularProgressIndicator(),
error: (err, stack) => Text('Error: $err'),
),
),
);
}
}

3. ConsumerStatefulWidget (Stateful replacement)

ConsumerStatefulWidget and ConsumerState are the direct replacements for StatefulWidget and State. The ref object is made globally available as a class property, similar to widget.

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class TimerPage extends ConsumerStatefulWidget {
const TimerPage({super.key});


ConsumerState<TimerPage> createState() => _TimerPageState();
}

class _TimerPageState extends ConsumerState<TimerPage> {

void initState() {
super.initState();
// ref is accessible in lifecycle methods!
// Always use ref.read (not watch) inside initState
final config = ref.read(configProvider);
print('API Url config: ${config['apiUrl']}');
}


Widget build(BuildContext context) {
return const Scaffold(
body: Center(child: Text('Check your logs')),
);
}
}

4. ref.watch vs ref.read vs ref.listen

Understanding the differences between these operations is key to avoiding bugs and optimization issues:

MethodWhere to UsePurposeRebuilds Widget?
ref.watch()In widget build() methods.Binds the widget state reactively.Yes (when state changes)
ref.read()Inside callbacks (clicks, lifecycles).Reads the value once without observing.No
ref.listen()Inside build() or initState().Executes side effects (snackbars/navigation).No

Example: Using ref.listen for Side Effects


Widget build(BuildContext context, WidgetRef ref) {
ref.listen<List<String>>(todoListProvider, (previous, next) {
if (next.length > (previous?.length ?? 0)) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Todo added!')),
);
}
});

return const TodoListUI();
}