Fetch Api Data Automatically with Interval in Flutter

9,490

Solution 1

You can refactor your FutureBuilder to use a Future variable instead of calling the method in the FutureBuilder. This would require you to use a StatefulWidget and you can set up the future in your initState and update it by calling setState.

So you have a future variable field like:

Future< SakaryaAir> _future;

So your initState would look like this :

@override
  void initState() {
    super.initState();
    setUpTimedFetch();
  }

where setUpTimedFetch is defined as

  setUpTimedFetch() {
    Timer.periodic(Duration(milliseconds: 5000), (timer) {
      setState(() {
        _future = getSakaryaAir();
      });
    });
  }

Finally, your FutureBuilder will be changed to:

FutureBuilder<SakaryaAir>(
          future: _future,
          builder: (context, snapshot) {
            //Rest of your code
          }),

Here is a DartPad demo: https://dartpad.dev/2f937d27a9fffd8f59ccf08221b82be3

Solution 2

Futures can have 2 states: completed or uncompleted. Futures cannot "progress", but Streams can, so for your use case Streams make more sense.

You can use them like this:

Stream.periodic(Duration(seconds: 5)).asyncMap((i) => getSakaryaAir())

periodic emits empty events every 5 seconds and we use asyncMap to map that event into another stream, which get us the data.

Here is working example:

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class ExamplePage extends StatelessWidget {
  Future<String> getSakaryaAir() async {
    String url =
        'https://www.random.org/integers/?num=1&min=1&max=6&col=1&base=10&format=plain&rnd=new';
    final response =
        await http.get(url, headers: {"Accept": "application/json"});

    return response.body;
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
      stream: Stream.periodic(Duration(seconds: 5))
          .asyncMap((i) => getSakaryaAir()), // i is null here (check periodic docs)
      builder: (context, snapshot) => Text(snapshot.data.toString()), // builder should also handle the case when data is not fetched yet
    );
  }
}
Share:
9,490
Şansal Birbaş
Author by

Şansal Birbaş

Updated on December 04, 2022

Comments

  • Şansal Birbaş
    Şansal Birbaş over 1 year

    On my flutter application I am trying to show updating data. I am successful in getting data from weather api manually. But I need to constantly grab data every 5 seconds. So it should be updated automatically. Here is my code in Flutter :

    class MyApp extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Sakarya Hava',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: Scaffold(
            appBar: AppBar(
              title: Text('Sakarya Hava'),
            ),
            body: Center(
              child: FutureBuilder<SakaryaAir>(
                future: getSakaryaAir(), //sets the getSakaryaAir method as the expected Future
                builder: (context, snapshot) {
                  if (snapshot.hasData) { //checks if the response returns valid data
                    return Center(
                      child: Column(
                        children: <Widget>[
                          Text("${snapshot.data.temp}"), //displays the temperature
                          SizedBox(
                            height: 10.0,
                          ),
                          Text(" - ${snapshot.data.humidity}"), //displays the humidity
                        ],
                      ),
                    );
                  } else if (snapshot.hasError) { //checks if the response throws an error
                    return Text("${snapshot.error}");
                  }
                  return CircularProgressIndicator();
                },
              ),
            ),
          ),
        );
      }
    
    
      Future<SakaryaAir> getSakaryaAir() async {
        String url = 'http://api.openweathermap.org/data/2.5/weather?id=740352&APPID=6ccf09034c9f8b587c47133a646f0e8a';
        final response =
        await http.get(url, headers: {"Accept": "application/json"});
    
    
        if (response.statusCode == 200) {
          return SakaryaAir.fromJson(json.decode(response.body));
        } else {
          throw Exception('Failed to load post');
        }
      }
    }
    

    I found such a snippet to benefit from :

    // runs every 5 second
    Timer.periodic(new Duration(seconds: 5), (timer) {
       debugPrint(timer.tick);
    });
    

    Probably I need to wrap and call FutureBuilder with this snippet but I was not able to understand how to do it.

  • pianoman102
    pianoman102 almost 3 years
    hey @pr0gramist, i know this is kind of old, but I'm finding your answer useful here, with one additional question. Do you know how you would fit this into using bloc for your state management? I have a few examples that someone with your experience might be able to advise on.
  • pr0gramist
    pr0gramist almost 3 years
    @pianoman102 I think Timer example of the bloc is somewhat comparable: bloclibrary.dev/#/fluttertimertutorial Once you got ticker setup you can send events and fetch new data.
  • pianoman102
    pianoman102 almost 3 years
    Yeah i saw that actually, but thought it was a little overkill...I was thinking you could setup your bloc to fetch events via a repository, and then in the ui somewhere, setup a timer to send a fetchData event to the bloc on an interval. But I don't know if that's the best way or not.