Flutter dart async await not working as expected - flutter

I am trying to check the internet connection of the mobile device. I am using below code to check the connectivity.
import 'package:flutter/material.dart';
import 'package:internet_connection_checker/internet_connection_checker.dart';
class RedirectPage extends StatelessWidget {
final int? status;
#override
Widget build(BuildContext context) {
bool? isDeviceConnected;
() async {
print("a");
print(123);
isDeviceConnected = await checkConnection();
print(888);
};
if (isDeviceConnected != null && isDeviceConnected == false) {
return AppNetworkConnectivityHome();
} else{
return HomePage();
}
}
}
print(isDeviceConnected); //giving null for the first time and true or false on the second time.
Future<bool?> checkConnection() async {
bool a = false;
a = await InternetConnectionChecker().hasConnection;
print(a);
return a;
}
how to force wait for the await function to complete

You'd have to await the method call. You've currently defined it as an anonymous function, so depending on where and how you execute it there will be some differences. But it will work if you instead do something like this:
Future<bool?> myMethod() async {
return await InternetConnectionChecker().hasConnection;
}
...
print(await myMethod());

You can't call async function in build method, you need to use FutureBuilder like this:
return FutureBuilder<bool>(
future: checkConnection(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
bool data = snapshot.data ?? true;
if (!data) {
return AppNetworkConnectivityHome();
} else{
return HomePage();
}
}
}
},
)

Related

Firestore Image Reference returning error, even though image exists

I have a Wrapper for my Signup flow, in order to show the right screen, depending on if the user did finish the corresponding signup step. Unfortunately, it does not work properly: Even though the image exists in the "userImages" folder for the corresponding user id, my userImageExists variable is returning "false". Does anybody know what is wrong with my code?
class Wrapper extends StatelessWidget {
static String id = 'wrapper';
#override
Widget build(BuildContext context) {
final user = Provider.of<User?>(context);
if (user == null) {
return SignupScreen();
} else {
return FutureBuilder(
future: _checkUserData(user),
builder: (context, snapshot) {
if (snapshot.hasData) {
switch (snapshot.data) {
case UserDataStatus.imageNotUploaded:
return ImageUploadScreen();
case UserDataStatus.verificationImageNotUploaded:
return SignupVerificationScreen();
case UserDataStatus.interestsNotPopulated:
return InterestsScreen();
default:
return LoggedInScreenWrapper();
}
} else {
return Container(
color: Colors.white,
child: Center(child: CircularProgressIndicator()));
}
},
);
}
}
Future<UserDataStatus> _checkUserData(User user) async {
final userImageRef =
FirebaseStorage.instance.ref().child('userImages').child(user.uid);
final verificationImageRef = FirebaseStorage.instance
.ref()
.child('userImages')
.child(user.uid)
.child('verification');
final userDoc =
FirebaseFirestore.instance.collection('users').doc(user.uid);
final userImageExists = await userImageRef
.getData(1)
.then((value) => true, onError: (error) => false);
final verificationImageExists = await verificationImageRef
.getData(1)
.then((value) => true, onError: (error) => false);
final interestsExist = await userDoc
.get()
.then((value) => value['interests'] != null, onError: (error) => false);
print("userImageExists: $userImageExists");
print("verificationImageExists: $verificationImageExists");
print("interestsExist: $interestsExist");
if (!userImageExists) {
return UserDataStatus.imageNotUploaded;
} else if (!verificationImageExists) {
return UserDataStatus.verificationImageNotUploaded;
} else if (!interestsExist) {
return UserDataStatus.interestsNotPopulated;
} else {
return UserDataStatus.allDataPresent;
}
}
}
enum UserDataStatus {
imageNotUploaded,
verificationImageNotUploaded,
interestsNotPopulated,
allDataPresent,
}

how to get the future value in build widget

i made a future function which return bool value, then i used it in the build widget on if condition but the code is always returning the sign in.
can any one help me solving this issue.
Future isExist(uid) async{
var isExist = await Database().checkIfUserExist(uid);
if(isExist == true){
return true;
}
else{
return false;
}
}
#override
Widget build(BuildContext context) {
final user = Provider.of<UserDashboard?>(context);
// return either Home or Authenticate widget
if(user == null){
print("user null");
return SignIn();
}else{
print("user not null");
if(isExist(user.uid) == Future.value(true)){
return Home();
}
}
return SignIn();
}
user is not null and is exist but not taking the correct result.
Future<bool> checkIfUserExist(String adminID) async{
bool isExist = false;
try{
await admin.doc(adminID).get()
.then((doc) => isExist = doc.exists);
return isExist;
}catch(e){
print(e);
return isExist;
}
}
the above function is safe and good because i used it in another screen and worked.
Try using FutureBuilder for future method.
FutureBuilder<bool>(
future: isExist(uid), // use a state variable for statefulwidget
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
if(snapshot.data) return Home();
else return SignIn();
}
return CircularProgressIndicator();
}
More about using FutureBuilder

Issue with Asynchronous execution in flutter

In the below code while execution at line 18 calls a future function readData() and before its complete execution its move to the line 19 i.e. print statement. I want after the readData() function is called wait for the function to complete its execution and update the flag variable and then after move with further execution
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/cupertino.dart';
import 'package:udharibook/Screens/SignInPage.dart';
import 'package:udharibook/Screens/UserProfile.dart';
import 'package:udharibook/Screens/dashboard.dart';
class AuthService {
bool flag = false;
final FirebaseAuth _auth = FirebaseAuth.instance;
final DBRef = FirebaseDatabase.instance.reference().child('Users');
handleAuth(){
return StreamBuilder(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext, snapshot) {
if(snapshot.hasData) {
readData();
print(flag);
if(flag ==true)
return DashboardPage();
else
return UserProfile();
}
else {
return SignIn();
}
},
);
}
Future<void> readData() async {
final FirebaseUser user = await _auth.currentUser();
final userid = user.uid;
DBRef.child(userid).once().then((DataSnapshot data){
print(userid);
if(data.value!=null)
{
flag = true;
print(data.key);
print(data.value);
}
else{
print('User not found');
flag = false;
}
});
}
signOut(){
FirebaseAuth.instance.signOut();
}
signIn(AuthCredential authCreds){
FirebaseAuth.instance.signInWithCredential(authCreds);
}
signInWithOTP(smsCode,verId){
AuthCredential authCreds = PhoneAuthProvider.getCredential(
verificationId: verId,
smsCode: smsCode
);
signIn(authCreds);
}
}
Use a FutureBuilder and let your readData return true or false instead of setting a variable.
return StreamBuilder(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext, snapshot) {
if(snapshot.hasData) {
return FutureBuilder<String>(
future: readData(),
builder: (BuildContext context, AsyncSnapshot<String> readDataSnapshot) {
//readDataSnapshot.data will be your true/false from readData()
if(readDataSnapshot.hasData){
if(readDataSnapshot.data == true)
return DashboardPage();
else
return UserProfile();
} else {
return CircularProgressIndicator(); // or something else while waiting for the future to complete.
}
}
);
} else {
return SignIn();
}
},
);
Future<bool> readData() async {
final FirebaseUser user = await _auth.currentUser();
final userid = user.uid;
DBRef.child(userid).once().then((DataSnapshot data){
print(userid);
if(data.value!=null)
{
print(data.key);
print(data.value);
return true;
}
else{
print('User not found');
return false;
}
});
}

Chained async methods don't show result in Widget with FutureBuilder

I am having a problem displaying asynchronous data in a text widget.
I'm doing two chained asynchronous methods to search for the coordinates and then search for the city from the smartphone:
Future<String> _getCity() async {
Future<Position> pos = Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.low);
String city = "";
pos.then((result) {
return result;
})
.then((result) async {
List<Placemark> listPlacemark = await Geolocator().placemarkFromPosition(result);
return listPlacemark;
})
.then((result) {
return result.first;
})
.then((result) {
city = result.subAdministrativeArea;
// print( city ); -> Here it's showing correct data in console
});
return city;
}
The city appears on the console in that print command that is commented out.
To fill the text widget I'm doing this:
Padding(
padding: EdgeInsets.all(12),
child: FutureBuilder<String>(
future: _getCity(), // a Future<String> or null
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Error loading location');
case ConnectionState.waiting:
return Text('Waiting...');
default:
if (snapshot.hasError) {
return Text('Error loading location');
} else {
return Text(snapshot.data);
}
}
},
),
),
I called the _getCity method inside the initState method and it also worked.
The waiting message has already appeared but now everything is blank, what is missing?
I thank you for your attention!
Even if you return a value from the callback of then(), the value is not returned from _getCity(). You need to return Future.
Future<String> _getCity() async {
Future<Position> pos = Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.low);
return pos.then((result) {
return result;
}).then((result) async {
List<Placemark> listPlacemark = await Geolocator().placemarkFromPosition(result);
return listPlacemark;
}).then((result) {
return result.first;
}).then((result) {
return result.subAdministrativeArea;
});
}
By the way, do you need so many thens?
I've never used Geolocator(), so this is just a guess, but some of them may be removed because it looks like listPlacemark, result.first and result.subAdministrativeArea are not Future and you just want to extract a value from List<Position>. If my guess is right, the following will do.
Future<String> _getCity() async {
Future<Position> pos = Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.low);
return pos.then((result) async {
List<Placemark> listPlacemark = await Geolocator().placemarkFromPosition(result);
return listPlacemark.first.subAdministrativeArea;
});
}

FutureBuilder doesnt work when used with SharedPreferences

I'm getting a type 'Future<dynamic>' is not a subtype of type 'Future<String>'
I am simply trying to use a FutureBuilder in conjunction with SharedPreferences to return a string that I have previously stored using SharedPreferences.
Flutter : 'Future <dynamic>' is not a subtype of type bool
This stack overflow answer is doing the exact same thing as what I'm doing yet I have an error?
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: getRoleFuture(),
builder: (context, snapshot) {
if(snapshot.data == false) {
return Text("No data");
}
else {
return Text(snapshot.data);
}
}
);
}
getRoleFuture() async {
var sp = await SharedPreferences.getInstance();
return sp.getString("role");
}
The reason is that you used snapshot.data as bool in this line
if(snapshot.data == false) {
return Text("No data");
}
where as you set the Future return type as string
maybe if you do this:
if(snapshot.data == null || snapshot.data == '') {
return Text("No data");
}
else {
return Text(snapshot.data);
}
Check the snapshot with hasData.
if(snapshot.hasData == false)
Gives the return type.
Future<String> getRoleFuture() async {
var sp = await SharedPreferences.getInstance();
return sp.getString("role");
}
In you FutureBuilder future pass a refrence to your function:
future: getRoleFuture, (without the parentheses)
so your code should be something like this:
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: getRoleFuture,
builder: (context, snapshot) {
if(!snapshot.hasData) {
return Text("No data");
}
else {
return Text(snapshot.data);
}
}
);
}
Future<String> getRoleFuture() async {
var sp = await SharedPreferences.getInstance();
return sp.getString("role");
}
We don't use the parentheses in that code because we don't want the function to be called at the point where that code is encountered. Instead, we want to pass a reference to our function into the future.