Flutter Riverpod Future provider - requires async and await? - flutter

I've been reviewing the RiverPod 2 tutorial at https://codewithandrea.com/articles/flutter-state-management-riverpod/
In the section dealing with Future providers there is a code snippet as shown below...
final weatherFutureProvider = FutureProvider.autoDispose<Weather>((ref) {
// get repository from the provider below
final weatherRepository = ref.watch(weatherRepositoryProvider);
// call method that returns a Future<Weather>
return weatherRepository.getWeather(city: 'London');
});
I can't understand why this code snippet is missing the 'async' and 'await' syntax as shown below...
final weatherFutureProvider = FutureProvider.autoDispose<Weather>((ref) async {
// get repository from the provider below
final weatherRepository = ref.watch(weatherRepositoryProvider);
// call method that returns a Future<Weather>
return await weatherRepository.getWeather(city: 'London');
});
Is my version correct or what?

Think of it as doing:
Future<int> example() {
return Future.value(42);
}
instead of:
Future<int> example() async {
return await Future.value(42);
}
Sure, you can use async/await. But it is technically optional here.
Doing return future vs return await future doesn't change anything. In fact, there's a lint for removing the unnecessary await: unnecessary_await_in_return
The async keyword is generally helpful. It catches exceptions in the function and converts them into a Future.error.
But FutureProvider already takes care of that. So async could also be omitted

Related

Most elegant way to wait for async initialization in Dart

I have a class that is responsible for all my API/Database queries. All the calls as well as the initialization of the class are async methods.
The contract I'd like to offer is that the caller has to call [initialize] as early as possible, but they don't have to await for it, and then they can call any of the API methods whenever they need later.
What I have looks roughly like this:
class MyApi {
late final ApiConnection _connection;
late final Future<void> _initialized;
void initialize(...) async {
_initialized = Future<void>(() async {
// expensive initialization that sets _connection
});
await _initialized;
}
Future<bool> someQuery(...) async {
await _initialized;
// expensive async query that uses _connection
}
Future<int> someOtherQuery(...) async {
await _initialized;
// expensive async query that uses _connection
}
}
This satisfies the nice contract I want for the caller, but in the implementation having those repeated await _initialized; lines at the start of every method feel very boilerplate-y. Is there a more elegant way to achieve the same result?
Short of using code-generation, I don't think there's a good way to automatically add boilerplate to all of your methods.
However, depending on how _connection is initialized, you perhaps instead could change:
late final ApiConnection _connection;
late final Future<void> _initialized;
to something like:
late final Future<ApiConnection> _connection = _initializeConnection(...);
and get rid of the _initialized flag. That way, your boilerplate would change from:
Future<bool> someQuery(...) async {
await _initialized;
// expensive async query that uses `_connection`
to:
Future<bool> someQuery(...) async {
var connection = await _connection;
// expensive async query that uses `connection`
This might not look like much of an improvement, but it is significantly less error-prone. With your current approach of using await _initialized;, any method that accidentally omits that could fail at runtime with a LateInitializationError when accessing _connection prematurely. Such a failure also could easily go unnoticed since the failure would depend on the order in which your methods are called. For example, if you had:
Future<bool> goodQuery() async {
await _initialized;
return _connection.doSomething();
}
Future<bool> badQuery() async {
// Oops, forgot `await _initialized;`.
return _connection.doSomething();
}
then calling
var result1 = await goodQuery();
var result2 = await badQuery();
would succeed, but
var result2 = await badQuery();
var result1 = await goodQuery();
would fail.
In contrast, if you can use var connection = await _connection; instead, then callers would be naturally forced to include that boilerplate. Any caller that accidentally omits the boilerplate and attempts to use _connection directly would fail at compilation time by trying to use a Future<ApiConnection> as an ApiConnection.

Flutter: Error: 'await' can only be used in 'async' or 'async*' methods. even thought the method is async

I have this method:
Future<AppUser> _getUser(String documentId) async {
var document = await firestore.collection('customers').doc(documentId);
document.get().then((DocumentSnapshot documentSnapshot) {
print(documentSnapshot.data());
});
AppUser bruceUser = AppUser(userId: 'user006',);
return bruceUser;
}
And below it, I have a variable that uses this method:
AppUser _user = await _getUser(document.id);
However, this returns the following error:
Error: 'await' can only be used in 'async' or 'async*' methods.
What am I doing wrong here? I don't want to change _user to Future, because it will complicate the code further, so why doesn't the await work?
_getUser() is an asnyc function but not the calling function.
The error you are getting states that, await was used in a non-async function.
If you have difficulty in converting the caller function to async, try using then as follows.
snapshot.data.docs.map((DocumentSnapshot document) {
_getUser(document.id).then((user) {
//code here
});
}
In summary, there are two solutions for this
Convert the caller function to async.
Use _getUser().then((user){});

Dart Flutter, help me understand futures

See this code:
class SomeClass{
String someVariable;
SomeClass();
Future<String> getData () async {
Response response = await get('http://somewebsite.com/api/content');
Map map = jsonDecode(response.body); // do not worry about statuscode, trying to keep it minimal
someVariable = map['firstName'];
return 'This is the first name : $someVariable';
}
}
Now look at main:
void main(){
String someFunction() async {
SomeClass instance = SomeClass(); // creating object
String firstNameDeclaration = await instance.getData().then((value) => value);
return firstNameDeclaration;
}
}
When working with Future, like in the case of firstNameDeclaration why do I have to use .then() method to access the string object, since I am waiting for the function to finish?
When searching on the web, some people use .then() others don't, I am confused.
Kindly help me have a clearer understanding of how Futures and async functions overall work.
Background
Asynchronous operations let your program complete work while waiting for another operation to finish. Here are some common asynchronous operations:
Fetching data over a network.
Writing to a database.
Reading data from a file.
To perform asynchronous operations in Dart, you can use the Future class and the async and await keywords.
When an async function invokes "await", it is converted into a Future, and placed into the execution queue. When the awaited future is complete, the calling function is marked as ready for execution and it will be resumed at some later point. The important difference is that no Threads need to be paused in this model.
Futures vs async-await
When an async function invokes "await", it is converted into a Future, and placed into the execution queue. When the awaited future is complete, the calling function is marked as ready for execution and it will be resumed at some later point. The important difference is that no Threads need to be paused in this model.
async-await is just a a declarative way to define asynchronous functions and use their results into Future and it provides syntactic sugar that help you write clean code involving Futures.
Consider this dart code snipped involving Futures -
Future<String> getData(int number) {
return Future.delayed(Duration(seconds: 1), () {
return 'this is a future string $number.';
});
}
main(){
getData(10).then((data) => {
print(data)
});
}
As you can see when you use Futures, you can use then callback when the function return a future value. This is easy to manage if there is single "then" callback but the situation escalates quickly as soon as there are many nested "then" callbacks for example -
Future<String> getProductCostForUser() {
return getUser().then((user) => {
var uid = user.id;
return getOrder(uid).then((order) => {
var pid = order.productId;
return getProduct(pid).then((product) => {
return product.totalCost;
});
});
});
}
main(){
getProductCostForUser().then((cost) => {
print(cost);
});
}
As you can when there multiple chained "then" callback the code become very hard to read and manage. This problem is solved by "async-await". Above chained "then" callbacks can be simplified by using "async-await" like so -
Future<String> getProductCostForUser() async {
var user = await getUser();
var order = await getOrder(user.uid);
var product = await getProduct(order.productId);
return product.totalCost;
}
main() async {
var cost = await getProductCostForUser();
print(cost);
}
As you can above code is much more readable and easy to understand when there are chained "then" callbacks.
I hope this explains some basic concepts and understanding regarding the "async-await" and Futures.
You can further read about topic and examples here
Basically, you should either use await OR then(). However, Dart guidelines advocates that you should prefer use await over then() :
This code :
Future<int> countActivePlayers(String teamName) {
return downloadTeam(teamName).then((team) {
if (team == null) return Future.value(0);
return team.roster.then((players) {
return players.where((player) => player.isActive).length;
});
}).catchError((e) {
log.error(e);
return 0;
});
}
should be replaced by :
Future<int> countActivePlayers(String teamName) async {
try {
var team = await downloadTeam(teamName);
if (team == null) return 0;
var players = await team.roster;
return players.where((player) => player.isActive).length;
} catch (e) {
log.error(e);
return 0;
}
}
In your case, you should write :
void main(){
Future<String> someFunction() async {
SomeClass instance = SomeClass(); // creating object
String firstNameDeclaration = await instance.getData();
return firstNameDeclaration;
// Or directly : return await instance.getData();
// Or : return instance.getData();
}
}

How to convert Future List instance to List String in flutter

I am saving strings list in shared procedure and fetching that like below
Future<List<String>> getList() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getStringList("key");
}
Issue is that I need to send that list to server but facing issue as I need to convert that future list to simple List
How can I do that? or is there any other way as I need to send list of ids save by user to server.
When you mark a function as async it will return a future.
If you dont wait for the future you will get 'Future instance' this means your future(data) is not available yet.
If you want to wait for the future(data) to be resolved you need to use the await keyword.
So in your case you can create a List<String> myList; then create a function to wait for the future and assign the data to the previous List.
List<String> myList;
void getStringList() async {
var tempList = await getList();
// Or use setState to assign the tempList to myList
myList = tempList;
}
Or use Then:
getList().then(List<String> myList {
// TODO: Send myList to server.
});
Hope this helpe!!
When you work with async data you should "wait" while data will not completely loaded. You can use await word in async methods like that:
foo() async {
final Future<List<dynamic>> futureList = fetchSomeFutureList();
final list = await futureList;
}
or use Future's then() method to delegate some work.
You also can wait for futures in widget tree using FutureBuilder.
Check Dart Docs page for see details.

Future<void> vs void

Say I want to create an asynchronous method. I can make its return type either Future void or simply "void" (as in examples below). Both ways seem to do the trick. So what's the difference between the two? When should I use Future void instead of void? Thanks!
Future<void> myMethod() async{
await myOtherMethod(); //myOtherMethod() has return type of Future<void>
print('something');
}
vs
void myMethod() async{
await myOtherMethod(); //myOtherMethod() has return type of Future<void>
print('something');
}
Use Future<void> where you want to
Call future functions:
myMethod().then((_) => ...).catchError((err) => ...);
Use await for more readable async code.
await myMethod();
await anotherMethod();
Use void where you want to fire and forget.
myMethod();
... do other stuff
The Future<void> is a lot more common. If you're not sure which one to use, use Future<void>.
Future<void> myMethod() async{ }
is a asynchronous function which means it let the app to work while waiting for some operation to finish(ex:Network Request).The Future fun(); is used while we need to perform operation that gives result in future such as network call.
void fun(){};
is function that doesnot return anything.