Don't assign to void - flutter

I tried to figure out the reason, but it seems there is only one issue linked to github, it keeps displaying this warning, plz let me know the reason, thanks a lot!
onPressed: () async {
print('now trying to purchase');
_purchaserInfo = await Purchases.purchasePackage(widget.package);
print('purchase completed');
appData.isGoldWorm = _purchaserInfo.entitlements.all["all_features"].isActive;
print('is user pro? ${appData.isGoldWorm}');
}

Without seeing the entire code and warning it is hard. I see you are defining the _purchaseInfo with the await but you aren't calling it so it may seem to the function that you are using async but not await, and so you cant assign it correctly.

Related

What is the purpose of awaiting the empty Future in this animation code?

I was going though GSkinner's flutter_vignattes codebase, in one of the functions there was an empty await for a Future
Future<void> _reset() async {
// Wait until next event loop to advance animation and call setState or flutter will yell at you
await Future<void>.value();
_controller.forward(from: 1.0 - _percentage * 0.83);
if (_isLoading) {
setState(() {
_model = BasketballGameModel.randomize();
});
}
_isLoading = false;
}
I understand how promises are sent to micro-task queue in JS (assuming same happens in Dart), but not quite able to understand the reason provided in the comment here i.e.,
// Wait until next event loop to advance animation and call setState or flutter will yell at you
Really appreciate if someone can provide a deeper insight into this. This is the particular line in codebase i am referring to.
https://github.com/gskinnerTeam/flutter_vignettes/blob/0ccc72c5b87b5ab6ba2dee9eff76f48ce2fadec8/vignettes/basketball_ptr/lib/demo.dart#L149
Future<void> function() {}
Defines an asynchronous function that ultimately returns nothing but can notify callers when it eventually completes. Also see: What's the difference between returning void vs returning Future?
Or You can learn from this https://github.com/dart-lang/sdk/issues/33415

Future<void> async vs simply void async in Dart

In Dart,
what is the difference between saying
Future<void> doStuff() async { ...
and
void doStuff() async { ...
I know what a Future<T> is and how async/await work generally, but I never realized Future<void> was a thing. I have some code that has the Future<void> all over the place and I want to replace it with my normal way of doing things, but I don't want to break anything.
Notice that both functions use async. The question is NOT 'what is the difference between async and non-async functions?' or 'can you give a brief attempt at explaining asynchronous programming in Dart, please?'
I'm aware that there is a pretty much identical question already, but if you look closely at the answers you will see nobody actually answered the question in a clear way -- what is the difference? Is there any difference? Is there no difference?
To elaborate, consider the following two functions:
// notice there is no warning about not returning anything
Future<void> futureVoid() async {
await Future.delayed(Duration(seconds: 2), () {
var time = DateTime.now().toString();
print('$time : delay elapsed');
});
}
void nonFutureVoid() async {
await Future.delayed(Duration(seconds: 2), () {
var time = DateTime.now().toString();
print('$time : delay elapsed');
});
}
Then test them with a button whose onPressed() function is:
onPressed: () async {
await nonFutureVoid(); // notce that this await *DOES* delay execution of the proceeding lines.
var time = DateTime.now().toString();
print('$time : <-- executed after await statement');
}
Log result:
flutter: 2021-02-23 21:46:07.436496 : delay elapsed
flutter: 2021-02-23 21:46:07.437278 : <-- executed after await statement
As you can see, they both behave exactly the same way -- the simple void async version IS awaited. So what is the difference?
With your edit, your question makes more sense. Your question is really about principle vs. practice.
In principle, a function that returns void is different from a function that returns Future<void>. One conceptually represents something that does not return anything, and the other represents an asynchronous computation that can fire callbacks when it completes.
In principle, you should never attempt to use the value returned from a void function. It doesn't make sense. If you run the Dart analyzer against code that does await nonFutureVoid(); you'll get a warning if the await_only_futures lint is enabled.
In practice, there are cases where attempting to use a void return value happens to not generate an error. Those are quirks in the language (or bugs in the implementation); you should not rely on them. (Dart didn't originally have a void return type. When it was added later, it wasn't implemented to mean "no value" but instead to mean "a value that you aren't allowed to use". See Dart 2: Legacy of the void. Normally that subtle difference shouldn't matter.)
Being able to do await nonFutureVoid(); is a bug1, and it seems that that bug is now fixed: await nonFutureVoid(); is an error if you use Dart 2.12 or later and enable null-safety and the stricter type-checking that comes with it.
You can observe the old and new behaviors with DartPad by toggling the "Null Safety" button: https://dartpad.dartlang.org/b0dae0d5a50e302d26d93f2db3fa6207
1 There are a lot of issues filed on GitHub with a lot of back-and-forth discussion, so yes, it is rather confusing. Most people seemed to agree that allowing await void was undesirable, however.
A void function indicates that the function returns nothing, which means you can not act on the result of that Function (and cannot await on it's result).
Meanwhile, the Future<void> function returns a Future object (that has value of void). If you don't have a return statement, a missing_return warning will show up (it can still be compiled). You can still act on that result by awaiting it, but cannot actually use the value because it's void.
While it seems like it'd be just fine with whatever you are using, I think it's better to use Future for every async function for type-safety and better maintenance.

Flutter Navigator use in async function issue

I'm writing an app where the login screen asks the user for various permissions and to login using Facebook.
Once all the permissions have been requested, the app moves to the next screen to ask further information and then returns to the calling screen to complete its tasks.
It seems that Navigator.push doesn't work as expected in async functions however.
void function() async {
<...do something 1...>
Navigator.pushNamed(context, screenName);
<...do something 2...>
}
Expected behaviour
would be that <...do something 1...> would run, then Navigator would call the next screen, and once Navigator.pop(context); was called by the second screen, the app would return to the first screen and execute <...do something 2...>. This is the way it works in every other screen in the app.
Actual behaviour
<...do something 1...> called, then <...do something 2...> and then at some point the Navigator is triggered.
This is causing me to have to do all sorts to try to block the flow of the application through this, but it seems very peculiar behaviour.
Has anyone else experienced this, or can give me a way around it? This doesn't even involve the await parts of the function, and to say it's driving me nuts is somewhat of an understatement.
Any help much appreciated!
I believe Navigator.pushNamed returns a Future. As such, perhaps if you just add the await like so:
void function() async {
<...do something 1...>
await Navigator.pushNamed(context, screenName);
<...do something 2...>
}
It can solve the issue, although I did not try it.
You can use .then() function to achieve what you want, example,
(Using email and password SignIn.)
onPressed: () {
authHandler.handleSignInEmail(emailController.text, passwordController.text)
.then((FirebaseUser user) {
Navigator.push(context, new MaterialPageRoute(builder: (context) => new HomePage()));
}).catchError((e) => print(e));
}

Firebase Cloud Messaging onLaunch callback

My app structure is a little bit mess, but I have to add this patch first and then I'll restructure the entire logic. The thing is I first check if there's a firebase user, then if there is one I use StreamBuilder to get the current user profile from Firestore, then I have the _firebaseMessaging.configure method because onLaunch and onResume I use this callback:
void _navigateToGestorResevas(Map<String, dynamic> message, User currentUser) {
Navigator.push(context,
MaterialPageRoute(builder: (context) =>
GestorScreen(user: currentUser)));
}
Because I need to send the User to this screen where he fetch the message from firebase.
onResume this works fine, but onLaunch it goes to the screen and fetch the data but there are like 20 seconds where there are some kind of glitch. It switch like 20-30 times between two states where I have and no have snapshot data in this _initState func:
final snapshot = await _dbRef.child('mensajes').child(widget.user.id).once();
if (snapshot.value != null) {
setState(() {
hayMensajes = true;
});
final data = snapshot.value;
for (var entry in data.entries) {
Message message = Message.fromJson(entry.value);
setState(() {
message.add(message);
});
}
} else {
setState(() {
hayMensajes = false;
});
}
Anyone have an idea what am I doing wrong?
If I am not mistaken, there are some active issues about FCM onLaunch callback with flutter. Some of them are still not fixed. One of the problems most people had to face was that onLaunch callback being called multiple times. I don't know why it happened, but as in your case, you can possibly get rid of the issue by some temporary fixes.
If the same screen is getting pushed over and over again, and glitching, you can pop the stack until it reaches the one you meant to open and set a condition to push navigator only if the new route is different from the old one. Using the named routes,
Navigator.popUntil(context, ModalRoute.withName(routeName));
if (ModalRoute.of(context).settings.name != routeName) {
Navigator.pushNamed(context, routeName);
}
I am not sure if that was the problem you asked, but I hope at least my answer helps somehow.

How to use Future<bool> in if statement condition?

In Flutter, am using "url_launcher" package which uses Future<bool> to check if the app can launch other apps. Within my code I have phone numbers listed, and I want to place an Icon for WhatsApp only if it is installed on the user's device.
bool hasWhatsApp() {
var whatsExists;
canLaunch('https://api.whatsapp.com/').then((val) => whatsExists = val);
return whatsExists;
}
am using this function to check inside an if statement to show the icon on the screen or not, however, it keeps returning 'null'.
if (phoneNumber.substring(0, 1) != '2' && hasWhatsApp())
IconButton(
icon: Image.asset('assets/icons/whatsapp.png'),
iconSize: Theme.of(context).iconTheme.size,
onPressed: () async {
await launch(whatsUrl);
},
),
how can I fix this please?
What you are trying to do cannot work.
Your function hasWhatsApp is synchronous, so it returns a value immediately when you call it.
Inside that function, you start an asynchronous computation, and when that computation finishes (at a later time), it overwrites a local variable. The function has long returned by then.
There is no way you can immediately return a value which is not available until later. It's simply not there.
So, you need to await the future so you can delay making the decision until the value is available.
For example, change your if to:
if (phoneNumber.substring(0, 1) != '2' &&
await canLaunch('https://api.whatsapp.com/')) {
...
}
That does mean that you have to make the function containing that if asynchronous.
I'm not a Flutter expert, I guess you might need to use a FutureBuilder.
There is no work-around for asynchrony.
You can await canLaunch method and convert your function as async. Since you make your function async, your return type needs to be Future Try this:
Future<bool> hasWhatsapp() async {
return await canLaunch('https://api.whatsapp.com/');
}
And if you use https://api.whatsapp.com/ as url it will always return true because it can launch it via browser. If you want to check if app is installed I guess you need to use canLaunch('whatsapp://') instead. Check https://stackoverflow.com/a/50672986/12709039 this answer for more