Search code examples
flutterdartflutter-riverpod

How to periodically fetch data from an api(CoinGecko) in Flutter. -With Riverpod-


I want to fetch data from coin gecko with its api. Using FutureProvider to do this. FutureProvider only fetches the data once. So the data comes and UI builds successfully.However, I want to listen all changes on each cryptocurrency, and rebuild the widget. I am using riverpod's FutureProvider. If I was able to send request to service, and fetch data every 15 seconds, and rebuild the UI, this would solve my problem. But i couldn't do it. Moreover, I want to learn what is the most efficient way to do this am I following the right path? Any help appreciated. :)

final marketProvider = FutureProvider<List<Coin>>(
  (ref) async => await DataService().fetch_coin_data(),
);

class CoinListPage extends ConsumerWidget {
  const CoinListPage({super.key});
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    AsyncValue<List<Coin>> coinList = ref.watch(marketProvider);
    return Scaffold(
      appBar: AppBar(
        title: const Text("T I T L E"),
      ),
      body: Center(
        child: coinList.when(
          data: (data) => CoinList(coinList: data),
          error: (e, stack) => Text('Error: $e'),
          loading: () => const CircularProgressIndicator(),
        ),
      ),
    );
  }
}
Future<List<Coin>> fetch_coin_data() async {
    List<Coin> _coinList = <Coin>[];
    final resp = await http.get(Uri.parse(
        "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=10&page=1&sparkline=false&price_change_percentage=24h"));

    if (resp.statusCode == 200) {
      // If the call to the server was successful, parse the JSON
      List<dynamic> decoded = json.decode(resp.body);
      for (dynamic coin in decoded) {
        _coinList.add(Coin.fromJson(coin));
      }

      return _coinList;
    } else {
      // If that call was not successful, throw an error.
      throw Exception('Request Error!');
    }
  }

I couldn't figure out which provider should I use for this purpose. Any help appreciated!


Solution

  • FutureProvider is designed to solve simple use-cases. For more advanced scenarios, consider using StateNotifierProvider.

    FutureProvider returns an AsyncValue, so we can create a StateNotifierProvider with state type AsyncValue, which will update fetch data and update state every 15 seconds.

    final marketProvider = StateNotifierProvider<MarketNotifier, AsyncValue>((ref) {
      return MarketNotifier();
    });
    
    class MarketNotifier extends StateNotifier<AsyncValue> {
      MarketNotifier() : super(const AsyncValue.loading()) {
        fetchLoopWithDelay(const Duration(seconds: 15));
      }
    
      Future<void> fetchLoopWithDelay(Duration delay) async {
        while (true) {
          try {
            final data = await DataService().fetch_coin_data();
            state = AsyncValue.data(data);
          } catch (e) {
            state = AsyncValue.error(e, StackTrace.current);
          }
    
          await Future.delayed(delay);
        }
      }
    }