skip to Main Content

I want to show CircularProgressIndicator while waiting for future to resolve using flutter_riverpod, here is my code snippet.
But it’s not showing, I am using ConsumerStatefulWidget is this right way to do it?

ElevatedButton(
                    onPressed: () {
                      rejectResponse = ref
                          .read(notificationRepositoryProvider)
                          .approveDocument(1);
                      Navigator.of(context).pop();
                      setState(() {});
                    },
                    child: FutureBuilder(
                        future: rejectResponse,
                        builder: (context, snapshot) {
                          if (snapshot.connectionState ==
                              ConnectionState.done) {
                            if (snapshot.hasData) {
                              return Text('Yes');
                            } else if (snapshot.hasError) {
                              return Text('Error');
                            }
                          } else if (snapshot.connectionState ==
                              ConnectionState.waiting) {
                            return CircularProgressIndicator();
                          }
                          return Text('Yes');
                        }),
                  ),

2

Answers


  1. You can use ConnectionState, but I think it can also be in none state. So you can check if its done, and if not, you should probably show the loading indicator anyway.

    builder: (context, snapshot) {
       if (snapshot.connectionState != ConnectionState.done) {
          return CircularProgressIndicator();
       } 
       else 
          return Text('Yes');
       } 
    }
    
    

    I usually use the hasData property instead. This lets me reduce the number of states I have to deal with, and safely assume snapshot.data is not null. I use the below pattern.

    // strongly type the builder so snapshot.data is typed as well.
    FutureBuilder<MyType>( 
        future: myFuture
        builder: (context, snapshot) {
           if (!snapshot.hasData) return Loading();
    
           final data = snapshot.data!; // '!' is safe since hasData is true
           return DisplayData(data);
        }
    )
    
    Login or Signup to reply.
  2. The preferred way of doing this in Riverpod is using a FutureProvider and AsyncValue:

    final notificationRepositoryProvider = FutureProvider<bool?>((ref) async {
      Future<bool> approveDocument() => Future.delayed(Duration(seconds: 2), () => Future.value(Random().nextBool()));
    
      return approveDocument();
    });
    
    class HomeView extends ConsumerStatefulWidget {
      const HomeView({Key? key}) : super(key: key);
    
      @override
      HomeViewState createState() => HomeViewState();
    }
    
    class HomeViewState extends ConsumerState<HomeView> {
      @override
      Widget build(BuildContext context) {
        AsyncValue<bool?> rejectResponse = ref.watch(notificationRepositoryProvider);
        return ElevatedButton(
            onPressed: () {
             ref.refresh(notificationRepositoryProvider.future);
            },
            child: rejectResponse.when(
              loading: () => const CircularProgressIndicator(
                color: Colors.white,
              ),
              skipLoadingOnRefresh: false,
              error: (err, stack) => Text('Error'),
              data: (data) => Text('Yes: $data'),
            ));
      }
    }
    

    Note that after the initial loading, the FutureProvider will return the previous value but will set AsyncValue.isRefreshing to true. To avoid this and always show the loader on refresh, you can set skipLoadingOnRefresh to false.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search