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.
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.
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());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With