Search code examples
flutterdartriverpod

Flutter + Riverpod - Show loading dialog depending on provider state


I have an application with Flutter and Riverpod, so i created a provider for the AuthController like this

final authControllerProvider = StateNotifierProvider<AuthController, bool>((ref) {
  final authApi = ref.watch(authApiProvider);
  final userApi = ref.watch(userApiProvider);
  final localStorage = ref.watch(localStorageProvider);

  return AuthController(authAPI: authApi, userAPI: userApi, localStorage: localStorage);
});

As you can see it's a StateNotifierProvider, so inside AuthController sometimes i set state = false or state = true to say if it's loading or not.

Here's the definition of AuthController

class AuthController extends StateNotifier<bool> implements IAuthController { 
      // ....
      void signupWithGoogle(....) {
           state = true;
           // do all the signup stuff
           state = false;
      }
}

In the VIEW, i can read the value of this state by using:

bool isLoading = ref.watch(authControllerProvider);

Problem:

But the problem is... How do i show a Dialog depending on this value? I tried putting in many places inside the code but all of them break somehow.

Last thing i tried (don't laugh cuz i already tried many things before this) was this:

@override
  Widget build(BuildContext context) {
    final isLoading = ref.watch(authControllerProvider);

    if (isLoading) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        showLoadingAnimation(context);
      });
    } else {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        closeLoadingAnimation(context);
      });
    }
....

But with this approach the screen gets all black (without any error)

So what's the correct way of showing and closing a dialog in this case?


Solution

  • As I understood, you want a way to listen to provider state changes and update UI depending on it.

    First of all, Use ref.read to call the function responsible for updating the state:

    ref.read(authControllerProvider.notifier).updateState();
    

    then in your build method:

    ref.listen(authControllerProvider, (previous, next) {
      if(next == true){
        showDialog(context: context, builder: (_){
          return Center(child: CircularProgressIndicator(),);
        });
      }else{
        Navigator.pop(context);
      }
    });
    

    In this way, each time state updated the listen function will triggered to give you prev and next state,

    what I suggest for you, for loading process, don't show dialog, instead, do it using stack and visibility widget like this

    @override
    Widget build(BuildContext context) {
    ref.watch(authControllerProvider);
    return Scaffold(
      body: Center(
        child: Stack(
          children: [
            ///your code
            ///
            Visibility(
              visible: ref.watch(authControllerProvider.notifier).state,
              child: Center(
                child: CircularProgressIndicator(),
              ),
            )
          ],
        ),
      ),
    );
    }