Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter: Detecting changes in the API

Tags:

flutter

dart

I currently have a simple API but my flutter widgets could not detect the changes happening in the API without restarting the whole application.

The API was made as a test API on https://www.mockable.io/. I managed to read from this API using a StreamBuilder. And still it does not change the values in the widget whenever i update their value in the API.

Default JSON

{
 "id": "123",
 "token": "1token",
 "status": "Valid"
}

After Value Change

{
 "id": "123",
 "token": "1token",
 "status": "Not Valid"
}

My StreamBuilder Code

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

final String url = "http://demo2722084.mockable.io/user";

class StreamPage extends StatefulWidget {

  @override
  StreamPageState createState() {
    return new StreamPageState();
  }
}

class StreamPageState extends State<StreamPage> {

  StreamController _userController;

  Future fetchUser() async{
    final response = await http.get(url);

    if(response.statusCode == 200){
      return json.decode(response.body);
    }else{
      print("Exception caught: Failed to get data");
    }
  }

  loadUser() async{
    fetchUser().then((res) async{
      _userController.add(res);
      return res;
    });
  }

  @override
  void initState() {
    _userController = new StreamController();
    loadUser();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {

    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Stream"),
      ),
      body: new StreamBuilder(
        stream: _userController.stream,
        builder: (context, snapshot){

          if(snapshot.hasError){
            print("Exception: ${snapshot.error}");
          }

          if(snapshot.hasData){

            var user = snapshot.data;

            var id = user["id"] ?? "";
            var token = user["token"] ?? "";
            var status = user["status"] ?? "";

            return new Center(
              child: new ListTile(
                title: new Text("Token: $token"),
                subtitle: new Text("Status: $status"),
                leading: new Text(id),
              ),
            );
          }

          if(snapshot.connectionState != ConnectionState.waiting){
            return new Center(
              child: new CircularProgressIndicator(),
            );
          }

          if(!snapshot.hasData && snapshot.connectionState != ConnectionState.done){
            return new Center(
              child: new CircularProgressIndicator(),
            );
          }
        },
      ),
    );
  }
}

I expected the StreamBuilder to pick up the changed status value but the app has to be restarted for the new value to be reflected in the app.

Is there an example of how I can achieve this properly since this is quite frankly my first time trying out a StreamBuilder.

like image 916
Asyraf Dayan Avatar asked Jan 11 '19 02:01

Asyraf Dayan


People also ask

How do I listen to API changes in Flutter?

An API can be used from an app to communicate with myapi.com. Through that communication, the API can List, Create, Edit or Delete items. The API needs to be given instructions, though. Webhooks, on the other hand, are automated calls from myapi.com to an app.


1 Answers

Your HTTP request results in just one data point, so for that you could use a FutureBuilder. But you want to know when something changes at the server. Your HTTP connection has finished when the change is made and so has no idea that it has happened.

There are several ways to detect changes at the server, for example, polling, user-initiated refresh, websockets, etc. For demonstration purposes, instead of calling loadUser in initState you could start a 1 second periodic timer to do that, which would update the stream every second and you should see the change. (Don't forget to cancel the timer in dispose.)

That's unlikely to be viable in production, so explore websockets, etc for pushed notifications from the web server. Also explore whether Firebase Cloud Messaging is a better way to send notification of changes to your application.

  @override
  void initState() {
    super.initState();
    _userController = StreamController();
    Timer.periodic(Duration(seconds: 1), (_) => loadUser());
  }
like image 188
Richard Heap Avatar answered Oct 17 '22 09:10

Richard Heap