Introduction
Displaying a popup is a rather trivial task for any seasoned Flutter developer. But when you are using Riverpod as state management solution, it can be a bit tricky. Here is my approach on how to show a popup in your Flutter app with Riverpod.
Why addPostFrameCallback doesn’t work
First, you might come up with this approach:
class MyView extends StatefulWidget {
const MyView({super.key});
@override
State<MyView> createState() => _MyViewState();
}
class _MyViewState extends State<MyView> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await showDialog(
context: context,
builder: (context){
return SomeDialogWidget();
});
});
}
@override
Widget build(BuildContext context) {
return Container();
}
}
Using the addPostFrameCallback
function is a good way to perform actions after the first frame is rendered. The widget is build and the dialog appears on top of it. Great in theory, but this doesn’t work with Riverpod notifiers.
Here is the same code but this time, the build method relies on data from a Notifier.
class MyView extends ConsumerStatefulWidget {
const MyView({super.key});
@override
ConsumerState<MyView> createState() => _MyViewState();
}
class _MyViewState extends ConsumerState<MyView> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await showDialog(
context: context,
builder: (context) {
return SomeDialogWidget();
},
);
});
}
@override
Widget build(BuildContext context) {
final dataNotifier = ref.watch(someNotifier);
return dataNotifier.when(
data: (data) => Container(),
loading: () => CircularProgressIndicator(),
error: (e, st) => ErrorWidget(e),
);
}
}
The first build cycle will usually return the loading
state. After the rendering, the addPostFrameCallback
function is executed and the dialog is shown. However, as soon as the notifier changes its state, another rebuild is triggered (with either data
or error
state) and the dialog is gone. You might not even have seen it although it was there for a short amount of time!
It doesn’t matter if you have a stateless or stateful widget. Riverpod notifiers always start in the loading state before returning data or an error which leads to one inital build and one rebuild. The rebuild wipes your dialog.
Moving the addPostFrameCallback
to the build method could work in some cases. However, for one-time popups this isn’t an option. The dialog would reappear every time the notifier state changes.
ref.listen is the solution
Riverpod offers a solution for this problem: ref.listen()
. In short: It works similar to ref.watch()
but doesn’t trigger a rebuild. For more details have a look at these articles here:

All we need is a little notifier that stores a boolean to indicate if the dialog can be shown. Here is an example:
final canShowDialogNotifier =
NotifierProvider<DialogNotifierState, bool>(
DialogNotifierState.new,
);
class DialogNotifierState extends Notifier<bool> {
@override
bool build() {
ref.keepAlive();
return false;
}
void setState(bool newState){
state = newState;
}
}
You can also reverse the logic and let the notifier indicate if the dialog has already been shown. Just make sure to keep it alive or the dialog might pop up again without you wanting it 🙂
Next, we wire everything up in the build
method of our widget:
class MyView extends ConsumerWidget {
const MyView({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.listen<bool>(canShowDialogNotifier, (prev, next) async {
if (next) {
await showDialog(
context: context,
builder: (context) {
return SomeDialogWidget();
},
);
}
});
final dataNotifier = ref.watch(someNotifier);
return dataNotifier.when(
data: (data) => Container(),
loading: () => CircularProgressIndicator(),
error: (e) => ErrorWidget(e),
);
}
}
next
contains the new notifier value, prev
the old one. You can add more logic here to decide if a dialog should be triggered. Change the notifier state from false
to true
and the dialog will appear.
💡 Tip
ref.listen requires a state change.
It does not react on the value provided by the build method of the notifier!
You can even remove the state from the widget (StatefulConsumerWidget -> ConsumerWidget
) with this approach. No need for the initState
method anymore!
Conclusion
In this article you learned how to show a popup in your Flutter app with Riverpod. As we have seen, addPostFrameCallback
doesn’t work in every case but with ref.listen
, there is an easy alternative to make it work again.
Related articles

