Convert Future<List> of custom object in list of object - flutter

i need support on a problem.
I have a method of my DatabaseHelper that takes care of recovering users from my database created with SQLite.
So I created a class called DatabaseUserHelper that takes care of retrieving information.
Here is the method of my class:
Future<List> getAllUser() async {
var dbClient = await db;
var result = await dbClient.query(tableUser, columns: [columnId, columnNome, columnCognome, columnUsername]);
return result.toList();
}
My requirement is to convert the result of this method into a normal list.
Here is an example of what I would like to do with Java.
List <User> listUser = getAllUser ()
Unfortunately, however, every time I try to make this conversion I always get an error.
Is there anything I can do ?
Thank you all

You should use await keyword before calling the function getAllUser like below :
List<User> listUser = await getAllUser();
It is because your function has 'Future' keyword which means the result will be available sometime in the future. So, you should wait a bit.

Related

Dart: how do I initialize a class such that one of its properties is filled upon initialization via a function that returns a future?

I have a file called database.dart. In it, I have a string property called currentUsername. This is the currently logged in user's username. I want this class to call the getUsernameFS() function only once and then be able to reuse this string for the rest of the class's existence inside its other functions. How do I accomplish this?
The code below gives an error: Error: A value of type 'Future<String>' can't be assigned to a variable of type 'String'.
class Database {
late String currentUsername = getUsernameFS(); //ERROR IS HERE
Future<String> getUsernameFS() async {...}
String someFunction() {...//some function that uses currentUsername//...}
}
A direct (and perhaps naive) approach would be to add an asynchronous initialization step to initialize an instance of your class:
class Database {
late String currentUsername;
Future<void> initialize() async {
currentUsername = await getUsernameFS();
}
}
However, that's potentially error-prone since it creates more work for callers, and callers could accidentally neglect to call (or neglect to wait for) initialize, and there's no way to enforce that at compile-time.
Instead, I'd recommend a couple of other options:
Make your member variable a Future instead:
late Future<String> currentUsername = getUsernameFS();
This has the advantage of safely avoiding accidental errors from callers who neglect to explicitly call an asynchronous initialization method first. However, this has the disadvantage of forcing all callers to await the result, making them also asynchronous.
If possible, make your class constructor private and force callers to obtain instances with an asynchronous, factory-like static method:
class Database {
late String currentUsername;
Database._();
static Future<Database> create() async {
var db = Database._();
db.currentUsername = await db.getUsernameFS();
return db;
}
...
}
This also has the advantage of safely avoiding accidental errors, and it avoids forcing all consumers of currentUsername to be asynchronous. A disadvantage is that a private constructor would prevent your class from being extended.
If possible, I'd also make getUsernameFS a static method and pass the username to the private constructor. Then currentUsername wouldn't need to be late, and you would avoid any risk of accidentally using a late variable before it's initialized.

How to test a void function in Dart?

I am new to unit tests in Dart/Flutter and I would like to write a test for a void function. When functions return something I am writing a test like this:
test('Gets user save', () async {
final userSave = await mockSource!.getUserSave();
expect(userSave!.age, equals(20));
});
In such a scenario like above expect can be used since getUserSave function returns a user model.
How about checking if test passes of fails for a void/Future function like below? I can not use expect because it does not return a value.
Future<void> clearUserSave() async {
DatabaseClient mockDBClient = MockDatabaseClientImpl();
mockDBClient.clear();
}
I use flutter_test and mockito for testing.
Typically a void function will produce a side effect of some sort. When writing a test for a void function, I would check whatever state is effected by the void function before and after calling the function to be sure that the desired side effect has occurred.
In this specific case, you are calling clear on a DatabaseClient. I don't know the specifics of the DatabaseClient api, but I would construct a test where the client contains some data before calling clear, and then check that the data is no longer there after calling clear.
Something along the lines of this:
Future<void> clearUserSave() async {
DatabaseClient mockDBClient = MockDatabaseClientImpl();
mockDBClient.add(SOMEDATA);
expect(mockDBClient.hasData, true);
mockDBClient.clear();
expect(mockDBClient.hasData, false);
}
you can add nullable variable and assign variable value in method, after call method check if this varaible isNotNull
like this:
test('Gets user save', () async {
await mockSource?.getUserSave();
final userSave=mockSource.user;
expect(userSave,isNotNull );
});
testing a function that return void :
expect(
() async => await functionThatReturnsVoid(),
isA<void>(),
);

Does a function that calls a Future needs to be a Future too?

I have a function that calls a Future in it. Now I am not sure if the first function needs to be a future too to await for the data. Here is my code:
FireBaseHandler handler = FireBaseHandler();
saveRows() {
handler.saveRows(plan.planId, plan.rows); ///this is a future
}
In my FireBaseHandler class I have this Future:
final CollectionReference usersCol =
FirebaseFirestore.instance.collection('users');
Future saveRows(String id, data) async {
return await usersCol.doc(myUser.uid).collection('plans').doc(id)
.update({'rows': data});
}
So does the first function needs to be a Future too?
You can have async function inside a sync function. But this way you lose ability to await for the result. And await is allowed only in functions marked as async, which leads us to Future as a the result of that function. So, yes, if you need to wait for the result, both functions have to be Future functions.
Edit:
If you need your wrapper function to be sync, but still be able to retrieve the result from inner async function, you can use a callback:
saveRows(Function(dynamic) callback) {
handler.saveRows(plan.planId, plan.rows).then((result){
callback(result);
});
}
This way the result will be retrieved at any point of time after calling the function (the code is not awaited)

Do these two approaches of await future in dart, return the same thing

var id;
Future load() async {
//Approach #1
id = await getUserId();
//Approach #2
await getUserId().then((id) {
setState((){
id = id;
});
});
}
If there is any difference in the two approaches I took, I would be happy to know :)
If you use async/await, you should use the async keyword in every method that relies upon asynchronous data; just like in JavaScript. In any case, it's better to properly loose couple your code, in a way that a function depends only on its own context, and is used without accessing to other scopes.
Therefore, the first example should be such as:
var id;
Future load() async {
// you should put the "await" when you consume "load()", rather than here.
return getUserId();
}
Have in mind that this is a blocking operation, which often is nice, but sometimes you may need a more asynchronous approach. Per example, when you want to load different stuff and you don't really care about when the responses arrive. Then, the second example is fine to use like this in the method you desire:
var id;
// here you don't need await at all.
getUserId().then((id) {
setState((){
id = id;
}
});
}
This second example will not fire the .then() anonymous function until the request has finished, which permits the program to do more stuff while it loads. Sometimes that's a perk, sometimes that's a problem.
Use the first when you depend on that data, use the second for assets that you don't immediatly need.

convert Stream<List<String>> to List<String> in flutter

I am trying to convert a Stream<List<String>> to List<String> in flutter
here is my code
Stream<List<String>> _currentEntries;
/// A stream of entries that should be displayed on the home screen.
Stream<List<String>> get categoryEntries => _currentEntries;
_currentEntries is getting populated with data from a database.
I want to convert _currentEntries into List<String>
I tried the following code but doesn't work:
List<List<String>> categoryList () async {
return await _currentEntries.toList();
}
I get the following error:
A value of type List<List<String>> can't be returned from method categoryList because it has a return type of List<List<String>>
Can someone help how to solve this issues and convert a Stream<List<String> to List<String>?
The issue seems to be with your return type for categoryList. You're returning as List of Lists when the Stream only contains a single layer of List. The return type should be Future<List<String>>.
Use .first, .last, or .single in addition to await to get just a single element, and toList() should be removed.
Future<List<String>> categoryList () async {
return await _currentEntries.first;
}
Also a quick tip: Dart automatically generates getters and setters for all fields so the getter method you show isn't necessary.
As title said, question is how to convert stream of some items to item. So what Christopher answered it is ok but only if you want to take the first value from the stream. As streams are asynchronous, they can provide you a value in any point of a time, you should handle all events from the stream (not only the first one).
Let's say you are watching on a stream from database. You will receive new values from database on each database data modification, and by that you can automatically update GUI according to newly received values. But not if you are taking just first value from stream, it will be updated only the first time.
You can take any value and handle it ("convert it") by using listen() method on a stream. Also you can check this nicely written tutorial on Medium. Cheers!
Stream<List<String>> _currentEntries = watchForSomeStream();
_currentEntries.listen((listOfStrings) {
// From this point you can use listOfStrings as List<String> object
// and do all other business logic you want
for (String myString in listOfStrings) {
print(myString);
}
});
I have no idea that Stream can await for the API call from the server, in my case I'm using BLOC pattern and using Future<List<String>> getCategoryList async () {...} and to get the List I going to use like this:
Future<List<String>> getCategory() async {
var result = await http.get();
//Some format and casting code for the String type here
return result;
}
Hope this help