skip to Main Content

I’m using FutureProvider in Riverpod for Flutter to return an API call with data and update the UI. While its loading it shows the CircularIndicator, then when loaded it displays the data on the home page. This is working the first time.

However, at a later stage in the app, the user triggers the call again, and it updates the UI on the home page. This is working, and I refresh the provider from the other screen, and the UI of the main screen updates. However, it doesn’t show the loading indicator, it just updates automatically after a few seconds when it fetchs the result.

Why isn’t the loading() state being called the second time.

Here are my 2 providers:

final getCalendarApiProvider = Provider<GetCalendarApi>(
  (ref) => GetCalendarApi(),
);

final calendarResponseProvider = FutureProvider<List<CalendarResponse>>((ref) async {
  return ref.read(getCalendarApiProvider).getCalendar(ref.read(startDateProvider), ref.read(endDateProvider));
});

Here is my main page:

class MainMenuScreen extends ConsumerStatefulWidget {
  const MainMenuScreen({
    Key? key,
  }) : super(key: key);

  @override
  ConsumerState createState() => _MainMenuScreenState();
}

class _MainMenuScreenState extends ConsumerState<MainMenuScreen> {
  String _selectedDate = '';


  @override
  Widget build(BuildContext context) {

    print('Main Build Called');

    AsyncValue<List<CalendarResponse>> calendarResponse = ref.watch(calendarResponseProvider);

    return Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading: true,
        title: const Text(
          "My Sessions",
          style: TextStyle(fontSize: 18, fontWeight: FontWeight.normal),
        ),
        actions: [
          //
          IconButton(
            icon: Icon(
              Icons.settings,
              color: Colors.white,
            ),
            onPressed: () {
              ref.refresh(calendarResponseProvider);
            },
          )
        ],
        centerTitle: true,
      ),
      drawer: const DrawerWidget(),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          StartEndDateWidget(),
          calendarResponse.when(data: (data) {
            print('DATA length: ${data.length}');
            return Expanded(
              child: ListView.builder(

                  scrollDirection: Axis.vertical,
                  shrinkWrap: true,
                  itemCount: data.length,
                  itemBuilder: ((context, index) {

                    return Text(data[index].studentNames ?? '-');

                  })),
            );
          }, error: ((error, stackTrace) {
            return Text('Error: ${error.toString()}');
          }), loading: (() {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }))
        ],
      ),
    );
  }
}

Here is the class of the api call:

class GetCalendarApi {

  Future<List<CalendarResponse>> getCalendar(DateTime startTime, DateTime endTime) async {

    log('startTime $startTime', name: logGetCalendarApi);
    log('endTime $endTime', name: logGetCalendarApi);

    String token = await SharedPrefsMethods.getStringFromPrefs(PREF_TOKEN_KEY);

    String url = BASE_URL + SESSNOTE_URL + GET_CALENDAR_URL;

    List<CalendarResponse> calendarResponseList;

    Map<String, String> queryParameters = {
      "startTime": "${formatDate(startTime)}",
      "endTime": "${formatDate(endTime)}",

    };

    Uri uri = Uri.parse(url).replace(queryParameters: queryParameters);

    log('Uri: (logGetCalendarApi) $uri', name: logGetCalendarApi);

    return http.get(uri, headers: {
      HttpHeaders.contentTypeHeader: "application/json",
      HttpHeaders.authorizationHeader: "Bearer $token",
      'accept': 'text/plain'
    }).then((http.Response response) {

      int statusCode = response.statusCode;
      log('Status Code: $statusCode', name: logGetCalendarApi);
      log('RESPONSE BODY (get students and mandates): ${response.body}', name: logGetCalendarApi);

      // dynamic data = json.decode(DummyResponses.getMandatesForProvider);
      dynamic data = json.decode(response.body);

      if (statusCode < 200 || statusCode >= 400) {
        log('Error: ${response.body}', name: logGetCalendarApi);
        Fluttertoast.showToast(
            msg: data['Message'] ??
                'An internal error has occurred.',
            toastLength: Toast.LENGTH_LONG,
            timeInSecForIosWeb: 3);
        throw Exception(data['Message'] ?? 'An internal error has occurred.');
      }

      calendarResponseList = data
          .map<CalendarResponse>(
              (json) => CalendarResponse.fromJson(json))
          .toList();

      log('Calendar result List Size: ${calendarResponseList.length}', name: logGetCalendarApi);

      return calendarResponseList;

    });
  }
}

I call this from another screen to trigger the http request again:

 ref.refresh(calendarResponseProvider);

Thanks very much

2

Answers


  1. You need to check one more condition for that is check for isRefreshing for your provider variable.

    same as calendarResponse.isRefreshing

    calendarResponse.when(
             data: (data) {
                print('DATA length: ${data.length}');
               if (calendarResponse.isRefreshing){
                  return const CircularProgressIndicator();
               }
                return ...;
              }, error: ((error, stackTrace) {
                return Text('Error: ${error.toString()}');
              }), loading: (() {
                return const Center(
                  child: CircularProgressIndicator(),
                );
              }))
    
    Login or Signup to reply.
  2. AsyncValue.when takes a skipLoadingOnRefresh boolean which defaults to true, causing a refresh to go directly from the first value to the second value. If you want to get a loading there in the middle, set that to false.

    This is a breaking change between Riverpod 1 and 2, and causes some confusion, but I’m happy the default is to skip loading on refresh now. Just override that to get the classic behavior back again. Source: https://pub.dev/documentation/riverpod/latest/riverpod/AsyncValueX/when.html

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