Refresh FutureProvider without showing loader

574

There is likely to be a better solution for this in the future (see this issue).

However, in the meantime, I have come up with a solution that should yield your desired behavior.

final myFutureProvider = FutureProvider.autoDispose<List<int>>((ref) async {
  return Future.delayed(
    const Duration(seconds: 2),
    () => List<int>.generate(100, (_) => Random().nextInt(1000)),
  );
});

final cachedProvider = StateProvider.autoDispose<List<int>>((ref) => []);

class ExampleListView extends StatelessWidget {
  const ExampleListView({Key? key, required this.data}) : super(key: key);

  final List<int> data;

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: data.length,
      itemBuilder: (context, index) => ListTile(title: Text(data[index].toString())),
    );
  }
}

class Refresher extends ConsumerWidget {
  const Refresher({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, ScopedReader watch) {
    return RefreshIndicator(
      onRefresh: () async {
        await context.refresh(myFutureProvider);
      },
      child: watch(myFutureProvider).when(
        loading: () {
          final cachedData = watch(cachedProvider).state;
          return cachedData.isNotEmpty
              ? ExampleListView(data: cachedData)
              : Center(child: CircularProgressIndicator());
        },
        error: (e, s) => Text(''),
        data: (data) {
          WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
            watch(cachedProvider).state = data;
          });
          return ExampleListView(data: data);
        },
      ),
    );
  }
}

Essentially we are saving the previous data into a StateProvider so that we can continue displaying the previous data as the new data is loading. I made this example generic so it's in a ready-to-go state you can try out before applying it to your code.

Share:
574
Kelvin
Author by

Kelvin

Updated on December 29, 2022

Comments

  • Kelvin
    Kelvin over 1 year

    I have a wallet provider which uses a future provider to get the wallet balance from the server, everything works fine, the only issue I'm having is refreshing the wallet provider to fetch the latest data from the server using context.refresh(walletProvider). This works well but I don't want the loading widget to show when it's trying to get the new data from the server instead it should show the old data until the new data comes back from the server.

    Below is the code:

    final walletProvider =
        FutureProvider<WalletModel>((ref) => WalletService().fetchWalletBalance());
    
    Consumer(builder: (context, watch, child) {
    final wallet = watch(walletProvider);
    
    
    return Column(
     children: [
      wallet.when(
       data: (value) {
       return walletContainer(
      context: context, walletModel: value);
    },
      loading: () => walletLoading(),
       error: (err, stack) {
      return Text(
    '${err.toString()}',
     },
    )
    ],
    ),
    

    Then I call context.refresh(walletProvider); from a button, it refreshes but showing the loader widget which is not what I want. I want the previous data to be showing while the refresh is going on.

    Please any help on how to achieve this?

    • gurnisht
      gurnisht about 2 years
      Hi, did you solve this finally using Alex's method?
  • gurnisht
    gurnisht about 2 years
    Hi Alex, is there an update on this? Caching the old data just to display while loading doesn't feel right...