Flutter and Sembast - Method returns null - flutter

I try to combine flutter with the sembast nosql database. I created a method to get all db documents and edit the given data.
My method looks like this:
Future<List<Minute>> getAll() async {
final finder = Finder(sortOrders: [SortOrder('timestamp')]);
final recordSnapshots = await store.find(await _db, finder: finder);
return recordSnapshots.map((snapshot) {
final minute = Minute.fromMap(snapshot.value);
minute.id = snapshot.key;
return minute;
}).toList();
}
get getSum {
getAll().then((value) {
int total = value.fold(
0, (previousValue, element) => previousValue + element.value);
print(total); // The correct value
return total; // Here I dont retrieve the value
});
}
print(total) shows me the right value, but in the Widget I don´t retrieve it. I retrieve just null. Whats the issue here?

I think you should not return a value inside the then(...) maybe return just the future and use a FutureBuilder as widget

You should use pedantic/strong mode as it should show lint warnings (missing return type and missing return value) that could help solving your issue without running it.
Unoptimized solution based on your code:
Future<int> getSum() async {
var minutes = await getAll();
var total = minutes.fold<int>(
0, (previousValue, element) => previousValue + element.value);
return total;
}
Possible optimizations:
avoid the sort order when computing the sum
only read the value field instead of converting the whole object
Example:
Future<int> getSum() async {
var total = 0;
(await store.find(await _db)).forEach((snapshot) {
total += snapshot['value'] as int;
});
return total;
}
Unfortunately the return value cannot be a String, it could be a Future<String> though (i.e. db calls are async).
as Julian2611 noted you then need to use a FutureBuilder and for example .toString() to convert the value to a string.

Related

Return multiple value from function in dart

Hi so i'm new to dart and I'm having an issue with returning 2 value from a dart function.
Currently I have this function :
Future LoadAllData({required Map data, required String detailId}) async {
loadUserData(data: data);
powData = await Database.getPowDataActive(detailId: detailId);
return powData;
}
so getPowDataActive is a function that will fetch a data from my database and it will return some map data, load user data will also fetch data and will also return some map. I wanted to use the loadAllData function for my futureBuilder and use the snapshot data from this 2 function for different purposes, can I do so ? or I have to first combine the return from both function into 1 variable and access it differently ?
Thanks before
You can create a model like this:
class LoadDataResult {
final Map userData;
final Map powData;
LoadDataResult({#requierd this.userData, #requierd this.powData, });
}
and then use it like this:
Future<LoadDataResult> LoadAllData({required Map data, required String detailId}) async {
var userData = await loadUserData(data: data);
powData = await Database.getPowDataActive(detailId: detailId);
return LoadDataResult(userData:userData, powData: powData);
}
and then use it like this in futureBuilder:
LoadDataResult data = snapshot.data;
print('${data. userData}');

Unexpected text "return" inside function

int FriendsNum() {
_firestore.collection('Friends').doc(User.userID).collection("Friends").where("Status",isEqualTo: 2)
.get()
.then((res)=> return{res.size});
return 0;
}
I am basically trying to get the number of documents that are inside the collection that has the status of 2, and the value should be returned so it can be displayed for the user, what can be changed in the syntax to make this work? with many thanks!
You have to change your function return type to a Future and use the await keyword to get the result from the firestore collection as it is an asynchronous operation itself.
The updated code will be the following:
Future<int> FriendsNum() async {
final count = await _firestore
.collection('Friends')
.doc(User.userID)
.collection("Friends")
.where("Status",isEqualTo: 2)
.get()
.then((res) => res.size);
return count;
}

Flutter - await/async on a List - why does this only work when not using declarations?

Still new to Flutter :(. Can anyone help...
I have a class that stores a bunch of project information. One part of this is a list of topics (for push notification), which it grabs from a JSON file.
I apply a getter for the list of topics, and when getting it it calls an async function which will return a List
Future<List<String>> _pntopics() async{
final _json = await http.get(Uri.parse(_topicsUrl));
if (_json.statusCode == 200) {
return (jsonDecode(_json.body));
}else {
return [""];
}
}
Future<List<String>> get topics => _pntopics();
In my main.dart file, it calls this value like so...
Future<List<String>> _topiclist = await projectvalues.topics;
The response is however empty, pressumably because it is a Future - so it is grabbing the empty value before it is filled.
But I can't remove the "Future" part from the async method, because asnc methods require a Future definition.
Then I decided to remove the declarations entirely:
_pntopics() async{
final _json = await http.get(Uri.parse(_topicsUrl));
if (_json.statusCode == 200) {
return (jsonDecode(_json.body));
}else {
return [""];
}
}
get topics => _pntopics();
and in main.dart, a general declaration...
var _topiclist = await projectvalues.topics;
...and this works!
So what declaration should I actually be using for this to work? I'm happy to not use declarations but we're always to declare everthing.
You should return back Future<List<String>> return types to the function and the getter but for _topicslist you must use var, final or List<String> declaration because:
(await Future<T>) == T
i.e.
var _topiclist = await projectvalues.topics; // The type of _topiclist is List<String>
final _topiclist = await projectvalues.topics; // The type of _topiclist is List<String>
UPDATE
Your code should be:
Future<List<String>> _pntopics() async{
final _json = await http.get(Uri.parse(_topicsUrl));
if (_json.statusCode == 200) {
return List<String>.from(jsonDecode(_json.body));
}else {
return <String>[""];
}
}
Doing this you force _pnptopics returns List<String> as jsonDecode returns List<dynamic>.
P.S. It is good practice do not use dynamic types where they can be changed to specified types.

streaming and transforming a Firestore document snapshots() stream

I am trying to get a document stream from Firestore, to extract one of its fields in my repository and to stream the field to my bloc. I use return orderBriefDoc.snapshots().map((snapshot) { for this.
However, upon first call, no internal map instruction becomes executed and instead I receive a type mismatch type '_MapStream<DocumentSnapshot, dynamic>' is not a subtype of type 'Stream<List<OrderBrief>>'. I do not understand why the return type of the .map() method does not depend on what I return within its return statement and why this internal code is not executed.
First of all, I used the repository function of Felix Angelov's Firebase authentication ant of the todo list as a blueprint:
Stream<User> get user {
return _firebaseAuth.authStateChanges().map((firebaseUser) {
return firebaseUser == null ? User.empty : firebaseUser.toUser;
});
}
Stream<List<Todo>> todos() {
return todoCollection.snapshots().map((snapshot) {
return snapshot.documents
.map((doc) => Todo.fromEntity(TodoEntity.fromSnapshot(doc)))
.toList();
});
}
My adaption looks like this
#override
Stream<List<OrderBrief>> orderBriefs() {
if (orderBriefDoc == null)
getOrderCollection();
return orderBriefDoc.snapshots().map((snapshot) {
final tmp = snapshot;
print ("2");
final tmp2 = snapshot.data()['orderBriefs'];
print("3");
return snapshot.data()['orderBriefs'].map((orderBrief) {
final tmp=orderBrief;
final tmp2 = OrderBriefEntity.fromMap(orderBrief);
final tmp3 = OrderBrief.fromEntity(tmp2);
return tmp3;
}).toList();
});
}
For some reason "2" and "3" are not printed upon first call, and due to the type mismatch the app execution fails. So in my function orderBriefs() I return a .map() of a snapshots() stream. The mapped value, so the single document snapshot is mapped again to extract the orderBriefs field. This field is transformed from an storage entity class OrderBriefEntity to my business logic class OrderBrief. The transformation result is the final return value. Hence I would expect the function orderBriefs() to return a list stream of this transformation result. However, a _MapStream<DocumentSnapshot, dynamic> is returned. Why?
PS: This refers to my question, but with a slightly different angle
So, finally I found a method to stream a single document of Firestore. I finally had the idea to look up the documentation of Stream and found a working example there. Why it does not work with only the map method like Felix did it, no idea. That being said, I still follow his pattern to transform the snapshot to an "entity" and then further to the data structure used by the bloc and the ui.
I finally needed to flavors, stream a single field (nested array) of a document and stream a whole document
(1) Streaming a field of a document.
#override
Stream<List<OrderBrief>> orderBriefs() async* {
if (orderBriefDoc == null)
getOrderCollection();
Stream<DocumentSnapshot> source = orderBriefDoc.snapshots();
await for (var snapshot in source) {
final List<OrderBrief> returnVal = snapshot.data()['orderBriefs'].map<OrderBrief>((orderBrief) {
return OrderBrief.fromEntity(OrderBriefEntity.fromMap(orderBrief));
}).toList();
yield returnVal;
}
}
(2) Streaming a document with all of its fields
#override
Stream<Order> orderStream(String orderId) async* {
Stream<DocumentSnapshot> source = orderBriefDoc.collection('orders').doc(orderId).snapshots();
await for (var snapshot in source) {
final Order returnVal = Order.fromEntity(OrderEntity.fromSnapshot(snapshot));
yield returnVal;
}
}
How to neatly integrate this with a listener in the bloc, you will find in the Firestore ToDos example at bloclibrary

Flutter - Retrieve value in getter outside of a .then() function

I´ve created a get method outside the Widget tree to retrieve a value from a database provider. But the problem: Because it is a Future type I have to get this data with .then() ..and outside of .then() my return does not know this value.
Example:
String get _plannedHours {
final calendarEntriesData =
Provider.of<CalendarEntries>(context, listen: false);
calendarEntriesData.getPlannedHoursFromMonth(_currentDate).then((value) {
print(value.length); // I need this value
});
return "Value: 20"; // ....here!
}
How I get this value outside of .then() to return a value to the Widget three?
You can use async/await to access the value:
Future<String> get _plannedHours async {
final calendarEntriesData =
Provider.of<CalendarEntries>(context, listen: false);
var value = await calendarEntriesData.getPlannedHoursFromMonth(_currentDate);
return "Value: ${value.length}";
}
Then you need to do:
await _plannedHours