I have this snippet:
final countProvider = StateProvider<int>((ref) {
return 0;
});
class CountWidget extends ConsumerWidget {
const CountWidget();
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(countProvider);
return Column(
children: [
Text(count.toString()),
IconButton(
icon: const Icon(Icons.add),
onPressed: () {
ref.read(countProvider.notifier).state++;
},
),
],
);
}
}
This is a pretty simplified code, but the idea is that it is using a state provider.
I would like to write a test and verify that, after some actions, the provider is in a specific state (without relying on the UI, here I could use find.text()
, but my state could be much more complex).
I would like to access the model in my test after pumping my widget:
await tester.pumpWidget(const CountWidget());
await tester.tap();
await tester.pump();
// ... Some other actions.
final currentCountState = // ?
expect(currentCountState, 3); // For example.
How can I do that?
ProviderScope
has a static method .containerOf
which returns the ProviderContainer
of the closer ProviderScope
of the current context
.
Let's say you want to get the WidgetRef ref
associated/active for the widget with a key Key('key')
, you can obtain its context
with tester.element
. Then you can use ProviderScope.containerOf
:
final context = tester.element(find.byType(Key('key')));
final providerContainer = ProviderScope.containerOf(context); // <- Your `ref`.
Here, CountWidget
extends ConsumerWidget
which extends ConsumerStatefulWidget
which extends StatefulWidget
.
In riverpod's code, we can see that the created state is actually a _ConsumerState
:
class _ConsumerState extends ConsumerState<ConsumerWidget> {
@override
WidgetRef get ref => context as WidgetRef;
@override
Widget build(BuildContext context) {
return widget.build(context, ref);
}
}
context
and ref
are actually the same object.
And this is because in ConsumerStatefulWidget
:
/// A [StatefulWidget] that can read providers.
abstract class ConsumerStatefulWidget extends StatefulWidget {
/// A [StatefulWidget] that can read providers.
const ConsumerStatefulWidget({Key? key}) : super(key: key);
@override
// ignore: no_logic_in_create_state
ConsumerState createState();
@override
ConsumerStatefulElement createElement() {
return ConsumerStatefulElement(this);
}
}
The associated element (which is the what is used for the context
is a ConsumerStatefulElement
:
class ConsumerStatefulElement extends StatefulElement implements WidgetRef {
// ...
}
So in the test, you can use tester.element
to get ref
:
await tester.pumpWidget(const CountWidget());
await tester.tap();
await tester.pump();
// ... Some other actions.
final ref = tester.element<ConsumerStatefulElement>(find.byType(CountWidget));
final currentCountState = ref.read(countProvider);
expect(currentCountState, 3); // For example.