Make a get request after the first one is finished Getx - flutter

i want make get request after the first one finished because i need to send data from the response of first request with the body of second request to the server .. how i do this with getx
thanks
class ProductsController extends GetxController with StateMixin<Posts> {
#override
void onInit() {
getData();
super.onInit();
}
getData() async {
try {
change(null, status: RxStatus.loading());
await postsApiProvider
.getPosts()
.then((value) {
change(value, status: RxStatus.success());
});
} catch (exception) {
change(null, status: RxStatus.error(exception.toString()));
}
}
// i want this function fire after getData()
_getRelated() async {
try {
await postsApiProvider
.getRelated(
price: value.region ----> because i need access to getDate values
)
.then((value) {
});
} catch (e) {
debugPrint(e.toString());
}
}
}
I tried that method but it didn't work :
#override
void onReady() {
_getRelated();
super.onReady();
}

You can achieve your result by using finally. But I would suggest creating a function for that, so that in future if you want to add one more function based on second function's callback then you can easily achieve it.
Here's the example:
gloablfun()async{
await getData();
await _getRelated();
}
then you can call the global function in the onInit method:
#override
void onInit() {
gloablfun();
super.onInit();
}
By using this way, you can add more functions in the future and your code will look cleaner than before.

try using try with finally e.g
//a sample model which is have data
final Rx<MyModel> model = Model().obs;
#override
void onInit() {
myFunctiontocall();
super.onInit();
}
myFunctiontocall() async{
try{
// from the first function
// make a function that will have data to the model
myFirstFunctionToRun();
}finally{
// from done process in the above next is the finally
// which next to be run
// call the second function but make sure the model
// have data
thenFinallymySecondFunctiontoRun();
}
}

Related

Flutter ensure I have a value in Async/Await and init functions [duplicate]

This question already has answers here:
What is a Future and how do I use it?
(6 answers)
Closed 20 days ago.
How can I make sure I have a state variable available after an async function call? My belief is because getValues() is async, it should "wait" until moving on to the next line. Thus, getValues() shouldn't exit and configValue() shouldn't be invoked until after my call to setState has finished. However the behavior I'm seeing it that values is an empty array in my Widget.
late List values = [];
#override
void initState() {
super.initState();
getValues();
configValue();
}
getValues() async {
final String response = await rootBundle.loadString('assets/values.json');
final vals = await json.decode(response)['values'];
setState(() {
values = vals;
});
}
void configValue() {
// How to make sure I have values[0] here?
}
Thanks in advance!
You can change your getValues to this:
Future<List> getValues() async {
final String response = await rootBundle.loadString('assets/values.json');
final vals = await json.decode(response)['values'];
return vals;
}
then create another middle function like this:
callasyncs() async {
var result = await getValues();
configValue(result);
}
and call it inside initState like this:
#override
void initState() {
super.initState();
callasyncs();
}
also change your configValue to this:
void configValue(List values) {
// now you have updated values here.
}
here your both configValue and getValues are separated from each other and also your configValue will wait for the getValues result.
you need to use await before the method to complete the future. also can be use .then.
Future<void> getVids() async { //I prefer retuning value
final String response = await rootBundle.loadString('assets/values.json');
final vals = await json.decode(response)['values'];
setState(() {
values = vals;
});
}
void configValue() async {
await getVids();
}
Try the following code:
List? values;
#override
void initState() {
super.initState();
getValues();
configValue();
}
Future<void> getVids() async {
final String response = await rootBundle.loadString('assets/values.json');
final vals = await json.decode(response)['values'];
setState(() {
values = vals;
});
}
void configValue() {
if (values != null) {
if (values!.isNotEmpty) {
…
}
}
}

Instance of _Future<int> is all I get when I try to get the total 'document' of my 'collection' in Firestore

Following is my code. I'm trying to get all the 'Babies' which are in documents:
class _HomePageeState extends State<HomePagee> {
String t_babies = getCount().toString();
}
Future getCount() async {
return FirebaseFirestore.instance.collection('Babies').snapshots().length;
}
Instead I get this error: instance of \_future\<int\>
Here is my Database. I expect to get 2 counts:
You need to use await when getting Future values and also you should pass Future and the type Future<int>:
Future<int> getCount() async {
return await FirebaseFirestore.instance.collection('Babies').snapshots().length;
}
and also get the method using await but inside and async function:
void example() async { // <---- here the async you need to add to use await
int babiesLength = await getCount(); // here use await
}
You should use setState to update the string , because the fetch takes time as it involves network.
String t_babies = '';
Future<void> _getCount() async {
setState((){
t_babies = FirebaseFirestore.instance.collection('Babies').snapshots().length.toString();
});
}
#override
void initState() {
super.initState();
_getCount();
}

Asynchronous method not running in proper order

I have these methods, for some reason fetchItems is being called first before initPosition, how come dart wont wait for it to finish and proceeds to the second method? I've added async/await but it still doesn't work. I've also checked my backend logs to confirm this. Am I doing something wrong?
Future<void> initPosition() async {
if (_latitude != null && _longitude != null) {
await Socket.updatePosition(
lat: 51,
lon: 17,);
}
}
Future<void> initMarkers() async {
await initPosition();
await Provider.of<Items>(context, listen: false)
.fetchItems();
}
void initMapState() async {
await getCurrentLocation().then((_) async {
await initMarkers();
setState(() {
_loaded = true;
});
});
}
#override
void initState() {
super.initState();
_location.enableBackgroundMode(enable: false);
WidgetsBinding.instance?.addPostFrameCallback((_) {
initMapState();
});
}
Future<void> fetchItems() async {
itemList = await repository.getItemList();
notifyListeners();
}
Working with multiple asynchronous functions inside Futures depends on whether one is finished or not, not every single one. For this, you can call the "whenComplete" method so you can assure that your future function have finished running. Like this:
For your initMarkers() function:
Future<void> initMarkers() async {
await initPosition().whenComplete((){
Provider.of<Items>(context, listen: false)
.fetchItems();
});
}
For your initMapState() function:
void initMapState() async {
await getCurrentLocation().whenComplete(() async {
await initMarkers().whenComplete((){
setState(() {
_loaded = true;
});
});
});
}
Keep in mind that, in your code, you are not working with the returning value of your getCurrentLocation() function, so instead of using the "then" method use the "whenComplete" method, assuring that you changed or returned your values with this function. Finally, for the initState(), make the function body with asynchronous:
#override
void initState() {
super.initState();
_location.enableBackgroundMode(enable: false);
WidgetsBinding.instance?.addPostFrameCallback((_) async {
initMapState();
});
}
This should work.

How do I ensure a specific execution order when forced to use Dart async/await

I am writing a basic programme to teach myself Dart/Flutter. Part of the programme uses the http.dart package to get some data and the http.get command returns a Future value. In order to unpack this value, I need to use an await command, which then changes the execution order of my code. I cannot work out how to preserve the intended execution order whilst using async/await. I am new to this, so appreciate that I am probably missing something obvious.
Code example 1 below uses async/await through a series of functions. This approach gives more or less the correct output order (other than the end of main()), but would mean (I think) that I would need to have an async build() method, which is not valid in Flutter.
// Cascading async methods with local variables and await statements
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() async {
print('build: Before getName.');
String name = await _getName();
print('build: After getName.');
}
// Get some data, make some decisions
Future<String> _getName() async {
print('getName: before getData');
String name = await _getData();
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: returning body.data');
return name;
} else {
print('getName: returning Bob');
return 'Bob';
}
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
final data = await http.get(Uri.parse('http://www.google.co.uk'));
print('getData: After http get.');
return data.body;
}
The output from this is (I have truncated the html data that is returned):
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
Main: end.
getData: After http get.
getName: after getData
getName: returning body.data
build: After getName. Name is: <html data>
The second code example below uses a global variable to capture data in the _getName() method so that I can avoid using async/await in the build() method. This does not give the correct execution order or the correct output.
// Use global variable to receive awaited data and avoid cascading async methods
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
String name = "";
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() {
print('build: Before getName.');
_getName();
print('build: After getName. Name is: $name');
}
// Get some data, make some decisions
Future<void> _getName() async {
print('getName: before getData');
String data = await _getData();
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: setting name = body.data');
name = data;
} else {
print('getName: setting name = Bob');
name = 'Bob';
}
return;
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
final data = await http.get(Uri.parse('http://www.google.co.uk'));
print('getData: After http get.');
return data.body;
}
The output from this code is shown below. Note that the build() method completed before _getData and _getName and the name printed in build() is empty in the 5th row.
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
build: After getName. Name is:
Main: end.
getData: After http get.
getName: after getData
getName: setting name = body.data
In the third example below, I have tried using .then to ensure that the code in each function only executes after the await command. I didn't think this would work (and it didn't) because I think I have a problem controlling the flow between functions, not a problem controlling the flow within functions, but I thought I should give it a go and I was clutching at straws by this point.
// Use global variable to avoid using await in build() method
// Use .then to ensure that method actions follow await command
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
String name = ""; // Global variable for async data return
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() {
print('build: Before getName.');
_getName();
print('build: After getName. Name is: $name');
}
// Get some data, make some decisions
Future<void> _getName() async {
print('getName: before getData');
await _getData().then((data) {
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: setting name = body.data');
name = data;
} else {
print('getName: setting name = Bob');
name = 'Bob';
}
});
return;
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
String value = "";
await http.get(Uri.parse('http://www.google.co.uk')).then((data) {
print('getData: After http get.');
value = data.body;
});
return value;
}
The output from this code is shown below. As with the second example, the execution is not in the correct order and the name printed in the build() method is empty.
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
build: After getName. Name is:
Main: end.
getData: After http get.
getName: after getData
getName: setting name = Bob
Ideally, the output from the programme should be:
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
getData: After http get.
getName: after getData
getName: setting name = Bob
build: After getName. Name is: Bob
Main: end.
How do I write my code so that I can use the http.get method and ensure that my code executes in the order that I want? I'll just add that I have read A LOT of stackoverflow questions, flutter documentation and general help online, but not found anything that answers my question so far.
Or nothing that I understand. :D Apologies if this is a stupid question. I am a noob at this.
I should have added that this example is an simplification of the problem in a Flutter app I am writing (noughts and crosses). This is checking for a win/draw after each move, then reading data from a DB, updating the results and writing them back to the DB. It also updates the game state to show that the game is over. The problem caused by async/await is that gamestate isn't being updated whilst the functions await the data and the game continues in the "playing" state even though the game is over. Pseudo code of the programme below (this is a bit scrappy, but hopefully it illustrates the problem).
build() {
checkState(win, draw or continue?);
if (continue){
_humanMove();
_computerMove();
drawTheWidgets;
} else {
drawDifferentWidgets; // New game
}
}
void _humanMove() async {
processMove;
if (win/draw) {
await _updateResults;
updateGameState(game over);
}
}
void _computerMove() async {
processMove;
if (win/draw) {
await _updateResults;
updateGameState(game over);
}
}
results _updateResults() async {
await http.get data results in database;
updateWithResult;
await http.put data results in database;
}
You need to wait ("await") for all the function calls, see:
void main(List<String> arguments) async {
print('Main: start.');
await _build();
print('Main: end.');
}
// Draw some stuff, make some decisions
Future<void> _build() async {
print('build: Before getName.');
await _getName();
print('build: After getName. Name is: $name');
}
// Get some data, make some decisions
Future<void> _getName() async {
print('getName: before getData');
String data = await Future.delayed(const Duration(milliseconds: 500), () {
return 'x';
});
print('getName: after getData');
double val = 5;
if (val < 0.5) {
print('getName: setting name = body.data');
name = data;
} else {
print('getName: setting name = Bob');
name = 'Bob';
}
return;
}
One comment: I believe it's easier for you to learn to do this right, if you try to learn this not based on console output, but with a Flutter app. You would see that your program goes through different states that actually need time - before, during and after your http request.
In your build method of a widget, you would need to provide something to show for for each state of your program. So you actually do not wait (await) for results, but update the state based on the results. And depending on that, your build method is prepared to take the different states and show something adequate. Then async/await is quite nice.
--- adding the following in response to the clarifying comment ---
The build method of a widget is not where you put business logic of your app. Build is called whenever a widget is rebuild (if you have a http request writing in a database, it would be triggered a whole number of times!). Trying to apply the steps you wanted your app to do in your first description with the intended output on the console, I tried to write this clarifying app (and to have different states that your app passes through I included that you have to click the icon button / FloatingActionButton to trigger the process). Please have a look how the build method of the second widget deals with all the different states (it prints them to the console and shows a text - you could do more fancy stuff there of course, based on what the state of your app implies). The real "action" (changes of the state of your app) is happening elsewhere:
import 'package:flutter/material.dart';
class ExampleWidget extends StatefulWidget {
const ExampleWidget({Key? key}) : super(key: key);
#override
_ExampleWidgetState createState() => _ExampleWidgetState();
}
class _ExampleWidgetState extends State<ExampleWidget> {
String _showWhatsHappening = 'Before getName.';
Future<String> _getData() async {
setState(() {
_showWhatsHappening = 'getData: Before http get.';
});
final data = await Future.delayed(const Duration(milliseconds: 500), () {
return 'Bob';
});
setState(() {
_showWhatsHappening = 'getData: After http get.';
});
await Future.delayed(const Duration(milliseconds: 300));
return data;
}
#override
void initState() {
print('not main, but initstate: start.');
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
theme: ThemeData(brightness: Brightness.light, fontFamily: 'Example'),
home: Scaffold(
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.star_outline),
onPressed: () async {
setState(() {
_showWhatsHappening = 'before getData ';
});
final x = await _getData();
await Future.delayed(const Duration(milliseconds: 300));
setState(() {
_showWhatsHappening = 'after getData ';
});
await Future.delayed(const Duration(milliseconds: 300));
setState(() {
_showWhatsHappening = 'setting name = $x';
});
},
),
body: Center(child: ShowItWidget(showIt: _showWhatsHappening)),
),
);
}
}
class ShowItWidget extends StatelessWidget {
final String showIt;
const ShowItWidget({Key? key, required this.showIt}) : super(key: key);
#override
Widget build(BuildContext context) {
print(showIt);
return Text(showIt);
}
}
void main() {
runApp(const ExampleWidget());
}
For completeness, here is my Dart-specific solution using Streams.
// Use global variable to receive awaited data
// Use StreamController to wait for async methods to finish
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
String name = "";
StreamController streamController = StreamController.broadcast();
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() {
print('build: Before getName.');
_getName();
streamController.stream.listen((args) {
print('build: After getName. Name is: $name');
});
}
Future<void> _getName() async {
print('getName: before getData');
String data = await _getData();
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: setting name = body.data');
name = data.length.toString();
} else {
print('getName: setting name = Bob');
name = 'Bob ${data.length.toString()}';
}
print('gateName. Name is $name');
streamController.add(name);
return;
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
final data = await http.get(Uri.parse('http://www.google.co.uk'));
print('getData: After http get.');
return data.body;
}

Print and save data of list of installed apps in flutter

I am trying to get list of appName for all Apps installed and using package: https://pub.dev/packages/device_apps . How to run this in initstate so I can run it in background and save data in backend.
Below code prints all information while I am only looking for specific fields as list.
void initState() {
super.initState();
getinstalledAppList();
}
Future<void> getinstalledAppList() async{
List<Application> apps = await DeviceApps.getInstalledApplications();
print(apps);
}
chetan suri you can map your apps list to new one or use foreach statement. Here is example:
void initState() {
super.initState();
getinstalledAppList();
}
Future<void> getinstalledAppList() async{
List<Application> apps = await DeviceApps.getInstalledApplications();
print(apps);
// Using foreach statement
apps.forEach((app) {
print(app.appName);
// TODO Backend operation
});
}
Map apps list to new:
Class model:
class AppInfo {
String appName, packageName, versionName;
AppInfo({
this.appName,
this.packageName,
this.versionName,
});
static List<AppInfo> retrieveSomeFields(List<Application> data) {
return data
.map(
(app) => AppInfo(
appName: app.appName,
packageName: app.packageName,
versionName: app.versionName,
),
)
.toList();
}
}
Call:
Future<void> getinstalledAppList() async{
List<Application> apps = await DeviceApps.getInstalledApplications();
print(apps);
var data = AppInfo.retrieveSomeFields(apps);
// TODO Backend operation
}
You can write a work manager and callbackDispatcher for background processes. Here is a good explanation. It will look like this:
const myTask = "syncWithTheBackEnd";
void main() {
Workmanager.initialize(callbackDispatcher);
Workmanager.registerOneOffTask(
"1",
myTask, //This is the value that will be returned in the callbackDispatcher
// Set Your Delay!
initialDelay: Duration(minutes: 5),
constraints: WorkManagerConstraintConfig(
requiresCharging: true,
networkType: NetworkType.connected,
),
);
runApp(MyApp());
}
void callbackDispatcher() {
Workmanager.executeTask((task) {
switch (task) {
case myTask:
print("this method was called from native!");
// Call your own method for Android.
getinstalledAppList();
break;
case Workmanager.iOSBackgroundTask:
print("iOS background fetch delegate ran");
// Call your own method for iOS.
getinstalledAppList();
break;
}
//Return true when the task executed successfully or not
return Future.value(true);
});
}