how to use values from async functions in dart - android

I want to use string value in my code but I am unable to do so. Please help, I am new to flutter.
// Database (db) is database sqlite
// Dog class has a String field name
Text func() async{
var dog = await db.firstDog();
return Text(dog.name);
}
The return type Text isn't a Text, as defined by the method func.dart(return_of_invalid_type).

use Future
ForExample
Future<Text> func() async{
String d = await getTest();
return Text(d);
}

Prerequisites
Do tell us where this function call is placed (eg. in repository or screen layer)
What you can do
Instead of returning the Text as Widget, you can just return a Future<String>
Future<String> func() async {
var dog = await db.firstDog();
return dog.name;
}
Assuming that you are utilizing this on your screen or widget class directly, you can do this
Database db = new Database();
String _dogName;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(_dogName),
),
);
}
void func() async {
setState(() async {
var dog = await db.firstDog();
_dogName = dog.name;
});
}

Check this article on medium. You can get a good understanding.
For asynchronous functions you should use Future class for returning the values.
Future<String> asyncFunc() async {
d = await db.firstDog();
return d.name;
}
Then use this string to set the text for a TextView

Related

How Print('Object') is executing before getData() function gets finished in Dart

I was Learning Async and Future Functions in Dart but I got confused because I still not get it like how print("object") is compiled before getData() function because traditionally next Line is read by compiler once the before line or Function is fully compiled/Executed . If I am making a Mistake Please correct me out , I am noob tbh
import 'package:flutter/material.dart';
class Loading extends StatefulWidget {
const Loading({super.key});
#override
State\<Loading\> createState() =\> \_LoadingState();
}
class \_LoadingState extends State\<Loading\> {
void getdata() async {
String parth = await Future.delayed(Duration(seconds: 3), () {
return 'parth';
});
print('Hey');
print(parth);
}
#override
int count = 0;
void initState() {
// TODO: implement initState
super.initState();
getdata();
print('object');
}
#override
Widget build(BuildContext context) {
// print(' Setstae vala + $count');
return Scaffold(
appBar: AppBar(
title: Text('Loading'),
),
body: ElevatedButton(
onPressed: () {
setState(() {
count++;
});
},
child: Text('$count')),
);
}
}
Your output should be like this:
object
Hey
parth
because traditionally next Line is read by compiler once the before
line or Function is fully compiled/Executed
Normaly, yes. But since you are using the async keyword in this case, it works a little differently.
What happens here:
You call getData first in your initState. In getData you have a future delayed in it, which you wait for with keywoard await. So it waits 3 seconds until you return 'parth' as a string. At the same time, however, it continues to run in your initState to return 'object'.
getdata is a async method, you need to return Future<void> to await.
Now the initState() cant be async, you can create another method to await and place it here.
void newMethod() async {
await getdata();
print('object');
}
And place newMethod() inside initState.
Or you can use .then
getdata().then((_) {
print('object');
});

How to refresh a widget with stream builder in flutter

I am trying to show data from the text file as per the data stored in shared preference i have another screen to save data in the text file i have a stream builder earlier it was future builder So i am trying to refresh the screen when coming back from second screen i tried to call a method when pop the method is getting called in the viewmodel calss of provider but the streambuilder is not getting updated
this is the code
to fetch data
Future<List<String>> fetchdata() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? category = prefs.getString('category');
if (category != null) {
lines = await locator<JsonAPI>().fetchquotes(category);
} else {
lines = await locator<JsonAPI>().fetchquotes('quotes');
}
// data = lines as Future<List<String>>;
notifyListeners();
return lines;
}
stream builder
var quotesdata = Provider.of<HomeViewModel>(context, listen: false);
StreamBuilder(
stream: quotesdata.fetchdata().asStream(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
List<String> lines = quotesdata.lines;
// List<String>? lines = snapshot.data as List<String>?;
return ScreenShotWidget(
homeViewModel: quotesdata,
list: lines,
);
} else {
return Container();
}
}),
method that i call when pop
function(data) {
category = data.toString();
fetchdata();
notifyListeners();
setState() {}
}
any idea how to update the screen
Every time your widget rebuilds, you get a new stream. This is a mistake. You should obtain the stream only once (for example, in initState)
#override
void initState() {
_stream = quotesdata.fetchdata().asStream();
}
and use that stream variable with StreamBuilder
StreamBuilder(
stream: _stream,
Later, when you want to update the stream, you can do
setState(() {
_stream = quotesdata.fetchdata().asStream();
})
to change the stream and force a refresh.
Please go over your code and change all such usages
StreamBuilder(
stream: quotesdata.fetchdata().asStream(),
to this kind of usage.
StreamBuilder(
stream: _stream,
Otherwise you may get a high backend bill someday. Right now every screen refresh does a new query to the backend.

Async await does not wait for the output to return

I am making an bungalow reservation system with spring rest back end and flutter front end.
In this I want to get a list of bungalows.
So I decided to make a method to get the list of bungalows in a method using HttpService class that I made to handle the rest end points, That method is getBungalows() method.
Then I called this method by overriding initstate().
But the problem is that before my initstate() is completed. my build method starts.
To prove this I printed two lines 'print' and 'print build' as I thought I get 'print build' first. what am I doing wrong here. Please help.
Method to retrieve data from rest back end
When this happened I first checked this method but this works fine and return the desired result.
Future<List<Bungalow>> getBungalows() async {
Uri uri = Uri.parse('$url/bungalows/');
http.Response response = await http.get(uri);
if (response.statusCode == 200) {
List<Bungalow> bungalows = List<Bungalow>.from(
json.decode(response.body).map((x) => Bungalow.fromJson(x)));
// print(bungalows.first.address + 'asafafasfafdfgfgarfgargafvfrvaerg');
return bungalows;
} else {
throw 'Unable to retrieve data';
}
}
Code of the HomeScreen
class _HomeScreenState extends State<HomeScreen> {
HttpService httpService = HttpService();
late List<Bungalow> bungalows;
bool isLoggedIn = false;
User? user;
void getBungalows() async {
bungalows = await httpService.getBungalows();
print('done');
}
#override
Widget build(BuildContext context) {
if (widget.user != null) {
isLoggedIn = true;
user = widget.user;
}
print('done build');
return Scaffold(
backgroundColor: Colors.white,
body: Column(
children: [
Text(isLoggedIn ? user!.userDetail.username : 'No login'),
// TextButton(
// onPressed: () {
// setState(() {
// getBungalows();
// print(bungalows.first.address);
// });
// },
// child: Text('click'))
],
),
);
}
#override
void initState() {
getBungalows();
}
}
Console Output
I/flutter (22248): done build
I/flutter (22248): done
It is behaving correctly, initState function is not async and method getBungalows() is called in parallel.
You should either use setState in getBungalows, or add a listener, or use the then keyword, or use StreamBuilder.
Check this: https://stackoverflow.com/a/54647682/305135

Passing Null Value when loading an async map

I'm trying to pass this map as a value for the next page route, but the value in jsonResult inside MaterialApp goes as a null.
Debugging, it is possible to see that the map contains information about the models. However, in the MaterialApp no, it only appears as a null:
JjModel jmodel = JjModel();
dynamic jsonResult;
loadJson() async {
Map<String, dynamic> mapInicial = jmodel.informacoesIniciais();
final Directory _appDocDir = await getApplicationDocumentsDirectory();
final Directory _appDocDirFolder =
Directory('${_appDocDir.path}/fileSettings');
File jsonFile = File('${_appDocDirFolder.path}/Preferences.json');
if (await jsonFile.exists()) {
String data = jsonFile.readAsStringSync();
jsonResult = json.decode(data);
} else {
final Directory _appDocDirNewFolder =
await _appDocDirFolder.create(recursive: true);
File jsonFile = File('${_appDocDirNewFolder.path}/Preferences.json');
jsonResult = mapInicial;
}
return jsonResult;
}
#override
void initState() {
loadJson();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Menu de Serviços',
debugShowCheckedModeBanner: false,
theme: androidTheme(),
home: ServiceList(jsonResult: jsonResult),
);
}
}
You marked loadJson as async so it completes after MaterialApp creation. Wrap MaterialApp with FutureBuilder, refactor loadJson to return value and set it as future prop. Then use snapshot.dataas input for ServiceList.
P.S. It is recommended to read more about asyncronicy. Read this for start https://www.woolha.com/articles/dart-event-loop-microtask-event-queue

How to assign value of Text Asset to String and display it in Text()?

I want to modify data from a text asset (.txt file) with a function and display it in a Text() widget. myFunction Takes datatype String as parameter and return datatype String.
I've read the documentation. Loading Asset from Image, this didn't worked. I've also tried solution from Suragch's Answer.
This maybe is a case where I should use FutureBuilder but I'm not able to understand how to make it work here (I'm new). I'm going to use another function to modify the data from file and then display.
This is one of the things I tried:
Widget build(BuildContext context) {
Future<String> aloadAsset(BuildContext context) async {
return await DefaultAssetBundle.of(context).loadString('assets/help.txt');
}
String helps = myFunction(await aloadAsset(context));
return Scaffold(
body: Text(helps)
);
}
When assigning value from await aloadAsset(context) to String, I get these errors: Unexpected text 'await'. & A value of type 'Future<String>' can't be assigned to a variable of type 'String'.
This is how to read the text from your file, you have to modify your Build function to include a FutureBuilder. Then you have to move the aloadAsset function out of the build:
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: aloadAsset(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if(if(snapshot.hasData)){
return Scaffold(
body: Text(snapshot.data)
);
}
return Scaffold(
body: Text('No Available data') //This will be returned in case you didn't receive data yet or in the case of a file error.
);
}
}
Future<String> aloadAsset(BuildContext context) async {
return await DefaultAssetBundle.of(context).loadString('assets/help.txt');
}
await keyword can only be used in an async function. What you can do here is:
Make the widget as StatefulWidget and then in the state class:
String helps = "";
in the initState() method:
aloadAsset();
and change your function to:
aloadAsset() async {
helps = myFunction(await rootBundle.loadString('assets/help.txt'));
setState((){});
}
Don't forget to add the import
import 'package:flutter/services.dart' show rootBundle;

Categories

Resources