Flutter/Dart - Use something like ChangeNotifier or setState within Future? - flutter

I'm using SocialProvider with ChangeNotifier. I want it to update its Consumer listener widgets once the user logs in and after posting to a database on the web.
Currently, I'm calling a Future method which inserts the new values to a Shared Preferences file upon a successful http post, but this won't update the UI until a call is made to a page which loads shared preferences.
In order to see an instant update, is there any way to access something like the ChangeNotifier or other kind of setState type function from within a Future? Here's the future;
Future<void> postSocialData(String email) async {
final url = "http://example.com/example.php?currentemail=$email"
final response = await http.get(url);
String currentuserid;
if (response.statusCode == 200) {
currentuserid = response.body;
setSharedPreferences(
currentavatar: "http://example.com/" + response.body + "-user.jpg",
currentemail: email,
currentlogged: true,
currentuserid: currentuserid,
);
print(response.body);
} else {
throw Exception('We were not able to successfully post social data.');
}
}
Any ideas on how to get an instant update from a Future method?

Turns out I was able to insert the Future<void> postSocialData within the scope of the class SocialProvider with ChangeNotifier itself and hence use The ChangeNotifier within the Future to alert the Consumers/listeners.

Related

Why is this listener never called?

I'm trying to use Riverpod for my project, however I'm hitting some issues.
I am not sure that I'm using it very well so don't hesitate to tell me if you see anything wrong with it :)
First I have my authProvider:
final authRepoProvider = ChangeNotifierProvider.autoDispose((ref) {
return AuthRepository();
});
class AuthRepository extends ChangeNotifier {
String? token;
Future signIn(String username, String password) async {
// Do the API calls...
token = tokenReturnedByAPI;
notifyListeners();
}
}
Then I have a service, let's say it allows to fetch blog Articles, with a stream to get live update about those.
class ArticleService {
StreamController<Article> _streamCtrl;
String? _token;
API _api;
ArticleService(this._api) : _streamCtrl = StreamController<Article>() {
_api.onLiveUpdate((liveUpdate) {
_streamCtrl.add(liveUpdate);
});
}
Stream<Article> get liveUpdates => _streamCtrl.stream;
Future markArticleAsRead(String id) async {
await _api.markAsRead(_token, id);
}
}
For that article service I would like to keep the current token up to date, but I don't want to rebuild the entire service every time the token changes as there are listeners and streams being used.
For that I would prefer to listen to the changes and update it myself, like such:
final articleServiceProvider = Provider.autoDispose((ref) {
final service = ArticleService(
ref.read(apiProvider),
);
ref.listen<AuthRepository>(authRepositoryProvider, (previous, next) {
service._token = next.token;
}, fireImmediately: true);
return service;
});
That piece of code seems correct to me, however when I authenticate (authRepository.token is definitely set) and then try to invoke the markArticlesAsRead method I end up with an empty token.
The ref.listen is never called, even tho AuthRepository called notifyListeners().
I have a feeling that I'm using all that in a wrong way, but I can't really pinpoint what or where.
Try ref.watch
final articleServiceProvider = Provider.autoDispose((ref) {
final service = ArticleService(
ref.read(apiProvider),
);
final repo = ref.watch<AuthRepository>(authRepositoryProvider);
service._token = repo.token;
return service;
});

Get Shared Preferences Value using Provider State Management

I try to Get Value from SharedPrefereces but I get a null value if I try to get Data in Main Page, I try to get the data when the state on the main page is created but I sometimes get null like this Available URL: http://169.172.70.108:8008/api/v1/iksk/self?idtraining=null
but after hot reload I managed to get the result like this
Available URL: http://169.172.70.208:8008/api/v1/iksk/self?idtraining=2021-01-21
this is my code
#override
void initState() {
// get pelatihan
MySharedPreferences.instance
.getStringValue(key: 'namaPelatihan')
.then((value) {
namaPelatihan = value;
// get nama Peserta
MySharedPreferences.instance
.getStringValue(key: 'namaPeserta')
.then((value) {
namaPeserta = value;
});
});
how do I get real-time results (get results when redirecting to the main page) using provider state management?
first create a function then use async and await with it
then fellow this code
_transitionToNextPageAfterSplash() async {
final auth = await SharedPreferences.getInstance()
.then((value) => value.getBool('auth') ?? false);
}

Unable to use a Future value - Flutter/Dart

I've fetched a json object and deserialized it and then returned it too.
I want to use this in another file.
I'm unable to assign the values that I'm getting in the first step.
Here are all the codes...
Service
Future getGeoPoints(String accessToken, String tripId) async {
String requestUrl;
var response = await get(
Uri.parse(requestUrl),
headers: {
'Authorization': "Bearer $accessToken",
},
);
if (response.statusCode == 200) {
Map<String, dynamic> responseBody = json.decode(response.body);
GetGeoPoints geoPoints = GetGeoPoints.fromJson(responseBody);
List listOfGeoPoints = [];
for (var geoPoint in geoPoints.geoPoints) {
listOfGeoPoints.add(
{
'latitude': geoPoint.latitude,
'longitude': geoPoint.longitude,
'timestamp': geoPoint.timeStamp,
},
);
}
// print('List of geo points: ' + '$listOfGeoPoints');
return listOfGeoPoints;
} else {
throw Exception('Failed to load data from server');
}
}
File where I need the above values
List routeCoordinates;
Future<void> getValues() async {
getGeoPoints(widget.accessToken, widget.tripId)
.then((value) => routeCoordinates = value);
}
When I run the app, routeCoordinates is null but when I hotreload, it contains the value.
I want to have the values as soon as the screen starts. What is the right way to assign the values here?
I've also tried this:
routeCoordinates = getGeoPoints...
It throws error..
Please help.. Thanks..
The function getGeoPoints() is an asynchronous one. But on the other file, you are not using the await keyword, instead you are using then(). So your code is not waiting for that function to return value.
Try using below code,
List routeCoordinates;
Future<void> getValues() async {
routeCoordinates = await getGeoPoints(widget.accessToken, widget.tripId);
}
Let us know how it went.
You need to use a FutureBuilder to define a behaviour depending on the state of the request. You'll be able to tell the widget what to return while your app is waiting for the response to your request. You can also return a specific widget if you get an error(if your user is offline, for example).
Edit: I've linked the official docs but give this article a read if it's not clear enough.

How to convert Future List instance to List String in flutter

I am saving strings list in shared procedure and fetching that like below
Future<List<String>> getList() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getStringList("key");
}
Issue is that I need to send that list to server but facing issue as I need to convert that future list to simple List
How can I do that? or is there any other way as I need to send list of ids save by user to server.
When you mark a function as async it will return a future.
If you dont wait for the future you will get 'Future instance' this means your future(data) is not available yet.
If you want to wait for the future(data) to be resolved you need to use the await keyword.
So in your case you can create a List<String> myList; then create a function to wait for the future and assign the data to the previous List.
List<String> myList;
void getStringList() async {
var tempList = await getList();
// Or use setState to assign the tempList to myList
myList = tempList;
}
Or use Then:
getList().then(List<String> myList {
// TODO: Send myList to server.
});
Hope this helpe!!
When you work with async data you should "wait" while data will not completely loaded. You can use await word in async methods like that:
foo() async {
final Future<List<dynamic>> futureList = fetchSomeFutureList();
final list = await futureList;
}
or use Future's then() method to delegate some work.
You also can wait for futures in widget tree using FutureBuilder.
Check Dart Docs page for see details.

I want to execute a function when the Flutter app starts

I want to send an ID to the server and receive json when the app is launched.
Flow
1.Start my app (Show splash screen)
2.Json request to server
3.If there is data, display page1. If not, display page2
it seems you my need to get a bit more learning about Flutter, my sugest is to start with this one only 10 euros will give you base from where will be easier to learn the rest, that said, to get a databse i'm using this code:
//lib/services/networking_service.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class NetworkHelper {
final String json;
final url = 'HERE YOU CAN PUT YOUR API URL';
NetworkHelper(this.json);
Map<String, String> headers = {
"Content-type": "application/x-www-form-urlencoded"
};
Future getData(BuildContext context) async {
http.Response response = await http.post(url, body: json, headers: headers);
if (response.statusCode == 200) {
Map<String, dynamic> decodedResp = jsonDecode(response.body);
print(decodedResp);
return decodedResp;
} else {
print(response.statusCode);
return null;
}
}
}
You can call it from your main like this:
static getCategories(BuildContext context) async {
String json =
'q={"f":"listCategories","Store_id":"$storeId","LANG":"$lang","UID":"$uid"}';
//THIS json VARIABLE IS WHERE YOU NEED TO PUT YOUR API CALL LĂ“GIC TO GET THAT ID, I LEAVE THIS FOR YOUR BETTER UNDERSTANDING
NetworkHelper networkHelper = NetworkHelper(json);
var decodedResp = await networkHelper.getData(context);
final CategoriesModel respData = CategoriesModel.fromJson(decodedResp);
print(respData);
//HERE YOU MAY RETURN O STORE IN PROVIDER YOUR RESPONSE AND SEND THE USER TO THE PAGE YOU CONSIDER
}
If you need more help I'm happy to help, but consider taking the course o learn a bit more, it will be lots more easy and enjoyable after.
use SchedulerBinding it runs when page is opened and widgets are build.
#override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
// your code after page opens,splash keeps open until work is done
});
}
#override
void initState() {
super.initState();
Timer(
Duration(seconds: 3),// you can do your stuff here when splash screen run
() => Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (BuildContext context) => LoginScreen())));}
and please put this code into the spalsh screen