Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fetch Api Data Automatically with Interval in Flutter

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.

like image 211
Şansal Birbaş Avatar asked Dec 10 '19 10:12

Şansal Birbaş


2 Answers

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

like image 155
Victor Eronmosele Avatar answered Nov 13 '22 17:11

Victor Eronmosele


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
    );
  }
}
like image 26
pr0gramist Avatar answered Nov 13 '22 17:11

pr0gramist