Flutter snapshot.hasError is always false - flutter

I'm pretty new to flutter. I can't understand why in my code I cannot trigger the snapshot.hasError property. Here is my code, it's basically the same as in the Flutter examples:
Inside the column builder:
...
RoundedButton(
text: "SIGNUP",
press: () {
setState(() {
_signupRequested = Auth().signup(_emailController.text,
_passwordController.text, _usernameController.text);
});
},
),
...
The signup function:
class Auth extends BaseLogic {
Future<bool> signup(String email, String password, String username) async {
BasicResponse response = await AuthRepo().signup(email, password, username);
if (response.success) {
await this.setValue('signup_requested', 'y');
return true;
} else {
throw Exception(response.reason!);
}
}
...
Future builder:
FutureBuilder<bool> buildFutureBuilder() {
return FutureBuilder<bool>(
future: _signupRequested,
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data!) {
return buildWait();
} else {
return buildColumn(null);
}
} else if (snapshot.hasError) {
return buildColumn(snapshot.error.toString());
}
return const CircularProgressIndicator();
},
);
}
If signup function throws the explicit exception or another exception (from the repo) the snapshot.hasError property is always false...

Ok I found the problem, I was using setState inside the builder to initialize the variable, somehow removing it made it work

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,
}

flutter futureBuilder not working with bool return type

I am trying to get the value from firestore and use it on my Switch widget. The indicator that I am trying to read on this switch is bool (true or false only). However it is not working, getting this error: type 'Null' is not a subtype of 'bool'
Here is my FetureBuilder code wrapped on my Switch:
FutureBuilder<bool>(
future: getShipValue(),
builder: (context, snapshot) {
if (snapshot.hasData == true) {
setState(() {
isSwitch = true;
});
}
return CupertinoSwitch(
value: isSwitch,
onChanged: (val) {
handleSwitch(val);
},
);
}),
Here is my Function that I am referencing:
Future<bool> getShipValue() async {
return await FirebaseFirestore.instance
.collection('OrderDetails')
.doc(order.orderId)
.get()
.then((value) {
return value.data()!['ShippedInd'];
});
}
Please let me know what am I doing wrong. Many thanks!
if (snapshot.hasData) {
if(snapshot.data){
setState(() {
isSwitch = true;
});
}
} else{
// Do sth else
}
The issue is solved now. As many pointed, I needed to handle the bool(true/false) and such is my FutureBuilder code that worked.
Thank you all!
Future<bool> getSwitchState() async {
isSwitch = await FirebaseFirestore.instance
.collection('OrderDetails')
.doc(order.docid)
.get()
.then((value) {
//return value.data()!['ShippedInd'] == true;
// print(value.data()!['ShippedInd']);
return (value.data()!['ShippedInd'] == true) ? true : false;
});
return isSwitch;
}

Flutter futurebuilder nested futures returning null

I am trying to work with different user roles and after logging in they should be directed to different pages however the futurebuilder I am trying to use to do so returns null. The element.data['role'] is correct and is being printed out before the future is returned but is not returned to the futurebuilder.
Future<String> getUserRole() async {
final result = await FirebaseAuth.instance.currentUser().then(
(user) async {
await Firestore.instance.collection("Users").getDocuments().then(
(docs) {
docs.documents.forEach((element) {
print(element.documentID);
print(user.uid);
if (element.documentID == user.uid) {
print(element.data['role']);
return element.data['role'];
}
});
},
);
},
);
return result;
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getUserRole(),
builder: (ctx, AsyncSnapshot<String> futureResult) {
if (futureResult.connectionState == ConnectionState.waiting) {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
} else if (futureResult.hasError) {
Center(
child: Text("Error"),
);
} else if (futureResult.hasData) {
print("BUILDER");
print(futureResult.data);
if (futureResult.data == "Employer") {
return EmployerTabsScreen();
} else {
return EmployeeTabsScreen();
}
} else {
return Text("NONE");
}
},
);
}
Try to call return at all iterations and add a null (or other value) for handling situation where you are not returning a data:
Future<String> getUserRole() {
return FirebaseAuth.instance.currentUser().then((user) {
return Firestore.instance.collection("Users").getDocuments().then((docs) {
var role = null;
docs.documents.forEach((element) {
print(element.documentID);
print(user.uid);
if (element.documentID == user.uid) {
print(element.data['role']);
role = element.data['role'];
}
});
return role;
});
});
}
Because you mix randomly "async/await" with "then"-callbacks.
Guess its not wrong to do that, but like in your example, its totally anreadable and makes everything harder than it could be.
At First:
final result = await FirebaseAuth.instance.currentUser().then(
(user) async {
If you declare something as "await", you dont need a callback. You should declare the result right. If you want to do something with the result, make the new request as an await aswell. Thats it. If you have this response, take it and lambda or foreach it.
The last step is, check what you got and return it.
It makes no sense to "await" something when you make "promises" (said in javascript)

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.