Receiving these errors in Future builder and Future List - flutter

I am facing this error, even though I have written the 'if statement'.
I also tried adding an 'else statement' like else{return Text(" "); } but then it completely ignores the 'if statement' and shows only the else statement in the Output, which is a Text widget.
I am also facing this error in the file 'db_helper.dart' when writing await dbClient!.query('cart');

The solution to the first error is return something both in if and else condition like this:
if(snapshot.hasData){
return Expanded();
}else{
return Text("");
}
and for the 2nd error make the code like this:
final List<Map<String, Object?>> queryResult=await dbClient!.query("cart");

The issue with your first snippet is that you return a Widget only when snapshot.hasData, to solve this after that return outside of the if case, either return a SizedBox or a CircularProgressIndicator that will show loading until the Future yilds data.
The second snippet indicates that your return type does not match with the type of queryResult, so you can change the type of queryResult to List<Map<String, Object?>> and this will most probably fix all your errors.

Related

Change a dropdown's items when another dropdown's value is chosen in flutter (UI wont update)

I have two drop downs, and I want to do things when they get values selected. One of those is to change the second buttondrop items based on what's selected in the first dropdown.
For example:
Dropdown1 is a list of car manufactuers
Dropdown2 is a list of their models
Dropdown1 selects mercedes
Dropdown2 gets "E Class, S Class" etc
Dropdown1 selects lexus
Dropdown2 gets "ES, LS", etc
(Eventually the second drop down will update a listview as well, but haven't gotten to that yet.)
Data wise, it works, I update the list. The problem is the UI won't update unless I do a hot reload
Currently I am just having the dropdowns fetch their data and using Future builders
Future? data1;
Future? data2;
void initState(){
super.initState();
data1 = _data1AsyncMethod();
data2 = _data2AsyncMethod();
}
_data2AsyncMethod([int? item1_id]) async{
if(item1_id == null){
item2Classes = await DefaultItems().getAllItem2Classes();
listOfItem2ClassNames = DefaultItems().returnListOfItemClassNames(item2Classes);
}
else{
// The methods below calls the DefaultItems methods which have Futures all in them.
// The getAllItems calls a network file with GET methods of future type to get data and decodes them, etc.
// They build a list of the object type, ex List<Item2>
item2Classes = await DefaultItems().getAllItem2Classes(item1_id);
listOfItem2ClassNames = DefaultItems().returnListOfItemClassNames(item2Classes);
}
}
I have this Future Builder nested in some containers and paddings
FutureBuilder{
future: data2,
builder: (context, snapshot){
if(snapshot.connectionState != done...)
// return a circle progress indictator here
else{
return CustomDropDown{
hintText: 'example hint'
dropDownType: 'name'
dropDownList: listOfItem2ClassNames
dropDownCallback: whichDropDown,
}
The onChanged in CustomDropDown passes the dropDownType and the dropDownValue
The callback
whichDropDown(String dropDownType, String dropDownValue){
if(dropDownType == 'item1'){
//so if the first dropdown was used
// some code to get item_1's id and I call the data2 method
_data2AsyncMethod(item1_id);
}
Again the data updates (listOfItem2ClassNames) BUT the UI won't update unless I hot reload. I've even called just setState without any inputs to refresh but doesn't work
So how do I get the UI to update with the data, and is my solution too convoluted in the first place? How should I solve? StreamBuilders? I was having trouble using them.
Thanks
If you do a setState in the whichDropDown function, it will rebuild the UI. Although I'm not exactly sure what you want, your question is really ambiguous.
whichDropDown(String dropDownType, String dropDownValue){
if(dropDownType == 'item1'){
//so if the first dropdown was used
// some code to get item_1's id and I call the data2 method
_data2AsyncMethod(item1_id).then((_) {
setState(() {});
});
}
}
I notice a couple things:
nothing is causing the state to update, which is what causes a rebuild. Usually this is done explicitly with a call to setState()
in whichDropdown(), you call _data2AsyncMethod(item1_id), but that is returning a new Future, not updating data2, which means your FutureBuilder has no reason to update. Future's only go from un-completed to completed once, so once the Future in the FutureBuilder has been completed, there's no reason the widget will update again.
You may want to think about redesigning this widget a bit, perhaps rather than relying on FutureBuilder, instead call setState to react to the completion of the Futures (which can be done repeatedly, as opposed to how FutureBuilder works)

Flutter SQFLITE: Please explain what factory and this data type Future<List<Trail>> means

I'm trying to get SQFlite to work in a flutter.
I'm new to flutter and there are some nagging problems with null safety and new types I'm not familiar with.
On top of the problems.. sometimes it says "List is not a type" which it certainly is.
My own code is located here
https://github.com/bksubhuti/places
I would like to connect it to a futurebuilder or listbuilder UI.
This is a learning app but also one for production later on.
Eventually I want to rewrite book reading app (Tipitaka Pali Projector) based on SQFlite and flutter.
follow a tutorial on trails. The code is on github.
https://github.com/nhandrew/sqflite_app/blob/main/lib/main.dart
It does not work with null safety
There is another code for a dog tutorial which is the official tutorial for flutter SQLite I don't understand this code
The code is located at this link here
The dog tutorial also has a similar syntax which is new to me.
https://github.com/flutter/website/blob/master/src/docs/cookbook/persistence/sqlite.md
I get confused about the factory and this datatype Future<List>
I have never seen this stuff before.
It also has to work with null safety.
It is This part here.. that is confusion
factory Trail.fromJson(Map<String,dynamic> json){
return Trail(
name: json['name'],
difficulty: json['difficulty'],
distance: json['distance']
);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: FutureBuilder<List<Trail>>(
future: dbService.getTrails(),
builder: (context, snapshot) {
if (!snapshot.hasData) return Center(child: CircularProgressIndicator(),);
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index){
return ListTile(
title: Text(snapshot.data[index].name),
leading: Text(snapshot.data[index].difficulty),
trailing: Text(snapshot.data[index].distance.toString()),
);
});
}
)
);
}
Future<List<Trail>> getTrails() async {
await initDatabase();
List<Map> list = await _db.rawQuery('SELECT * FROM Trails');
return list.map((trail) => Trail.fromJson(trail)).toList();
}
I suggest you read Dart language tour:
https://dart.dev/guides/language/language-tour
You can find details on your questions here:
https://dart.dev/guides/language/language-tour#factory-constructors
https://dart.dev/guides/language/language-tour#generics
https://dart.dev/guides/language/language-tour#asynchrony-support
https://dart.dev/guides/language/language-tour#lists
I'll edit my answer to give answers you asked in a comment.
So, let's unpack this part:
Future<List<Trail>> getTrails() async {
await initDatabase();
List<Map> list = await _db.rawQuery('SELECT * FROM Trails');
return list.map((trail) => Trail.fromJson(trail)).toList();
}
First of all - the function is asynchronous (as indicated by async keyword). This means a few things: this function will be able to run in parallel with other things. This is not technically 100% correct, but let's leave it at this level now.
This parallel run is achieved by the function immediately returning a Future object, and later returning the function value into this Future object. (Actually, Future is returned when it first hits await statement but ignore it for now).
Think of it as ordering food in a restaurant: you place the order, and the waiter immediately puts an empty plate in front of you (the plate is the Future object). While waiting for your dinner, you get to do other things - go to the restroom, have some drinks, etc. Eventually, the food will appear in your plate. Food is the object that the Future will eventually hold.
I'm trying here to simply explain a very complex topic - you should learn about this separately, it will really help you a lot. A LOT.
So this kinda explains what this means: Future<List<Trail>>. Your function will immediately return a Future object. But not any Future object, but the one that will hold a specific return value once the function completes. This value will be of List type: but not any List type - it will hold a list that will have only Trail object values. Hence List is defined as List<Trail> (List of Trail objects), and Future is Future<List<Trail>> - a Future that will hold a List of Trail objects.
Next, you are calling await initDatabase();. your initDatabase is async itself, which gives you an option to run some other things while you are waiting for the database to initialise. You could have for example:
var i=initDatabase();
print('Doing some work while the database is being initialized');
await i; // now I'm done, nothing for me to do until the database is initialized, so I'll just wait for that
List<Map> list =...
Again - here you really need to understand your async/await and Future (or Promises as they are called in JavaScript).
The rest should be easy:
List<Map> list = await _db.rawQuery('SELECT * FROM Trails');
Your rawQuery is async - and you decided to wait for it, since there is nothing else for you to do until query is done. The query will return a List - assuming here it will be a List of database Rows. Each Row is presented as a Map object, so List will not be any list - but a List of Map: List<Map>.
Finally - you convert List<Map> that is returned by query into List<Trail> - since this is the value you should return. Remember - function returns Future<List<Trail>>, so your final return statement needs to return List<Trail>. Remember: Future was returned already when you called the function.

FutureProvider using .whenData() shorthand

Riverpod provides a shorthand using .whenData() where you do not need to supply loading and error parameters. But I can't find example of how this code can be used to return widget in build() function.
Widget build(BuildContext context, ScopedReader watch) {
final cityListFuture = watch(cityListFutureProvider);
// This one is working fine
return cityListFuture.when(
data: (value) {
return Text("Data goes here");
},
loading: () => CircularProgressIndicator(),
error: (error, stack) {
return Container();
});
// This is shorthand for .when() without the need of loading and error
// ERROR: The return type 'AsyncValue<Text>' isn't a 'Widget', as required by the closure's context.
return cityListFuture.whenData((value) => Text("Data goes here"));
}
Anyone knows how can we use .whenData() to return a widget?
Quite an interesting documentation I had to go through for this.
It seems like the whenData is probably not exactly what is sounds like it should do.
Because all it does is that it return an AsyncValue<Widget> instead of directly returning Widget like the when function does.
So one way to use it would be,
return cityListFuture.whenData((val) => Text(val)).data!.value;
Here, the value would be your Text(value) itself.
One thing to be careful of here is the ! symbol since the data can be null. So you would have to manually have if checks.
Other thing to note here is that, you might as well achieve the same thing with something like this,
return Text(watch(cityListFutureProvider).data?.value);
Assumptions have been made that your value is a String.

FutureBuilder Flutter returning no data from my calculation

I can't get FutureBuilder to display any data from my calculation. I know the calculation is definitely correct because I can get the correct answer to be printed into the console, but just can't seem to get it to display in the app itself.
Here's the code for the code:
Future<String> getBeerDrinksTarget() async {
final user = await _auth.currentUser();
loggedInUser = user;
double beerNumber;
var query = _firestore
.collection('goals')
.where('email', isEqualTo: '${loggedInUser.email}');
query.getDocuments(source: Source.cache).then((data) {
beerPercentage = data.documents[0].data['beerPercentage'];
var y = double.parse(beerPercentage);
double beerNumber = (y / 100) * 21;
print('${beerNumber.toStringAsFixed(1)} target number of weekly beers');
print('$beerPercentage% weekly beer target as a percentage of total weekly drinks');
});
return beerNumber.toStringAsFixed(1);
}
And here's the code for the FutureBuilder widget:
FutureBuilder<String>(
future: getBeerDrinksTarget(),
builder: (BuildContext context,
AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return Center(
child: Text(
'${snapshot.data}',
),
);
} else
return Text('no data yet');
}),
The strange part about this is, if I swap return beerNumber.toStringAsFixed(1); to return beerPercentage;, it displays the beerPercentage fine in the app. This would suggest that FutureBuilder code is ok. But why does it not work when I use return beerNumber.toStringAsFixed(1);? That is the part I need to display in the app. It's weird because the calculation is being printed perfectly fine into the console, so that would suggest I must have made a mistake in the FutureBuilder() code, not the getBeerDrinksTarget() code.
Can anyone spot where I've gone wrong?
Thanks in advance for your help!
Jason
final response = await query.getDocuments(source: Source.cache);
beerPercentage = response.documents[0].data['beerPercentage'];
var y = double.parse(beerPercentage);
double beerNumber = (y / 100) * 21;
print('${beerNumber.toStringAsFixed(1)} target number of weekly beers');
print('$beerPercentage% weekly beer target as a percentage of total weekly drinks');
return beerNumber.toStringAsFixed(1);
This would work. Try it and let us know so I can add an explanation.
EDITED: Added Explanation.
The reason why .then didn't work and await worked is that await suspends the execution of current function while .then continues with executing the rest of the function after adding the callback to the callback chain. As pointed by #pskink since you are returning the value outside the .then method, it is returned before .then completely executes. And that is why, your beerPercentage was returned successfully but BEFORE the code execution reached to calculate beerNumber, a null value was already returned.
It would work fine if the return statement was also inside the then callback. Otherwise await is a much easier approach. On the other side, await stops everything else and waits for the future to resolve first and then carry on with the rest of the code.
Your print statements were also running after returning the value so you were able to see both beerNumber and beerPercentage in print.
I hope that clears the air!
Thank you for the edit #pskink

Autocomplete places widget returning error on place tap

Places Autocomplete widget returns this exception error when a place is tapped in prediction list
getDetailsByPlaceId returns a dynamic List. Change the return type to List<PlaceDetails>.
List<PlaceDetails> getDetailsByPlaceId(String id) { //Or whatever parameter you use
//Your code
}
From your question it seems that when you call the getPlacesbyId method so there is the return value it is expecting the list but it is getting the list of dynamic, So you need to make a return type of your List so the error might be resolved.