Before posting I took at look at previous questions (because there are many) but I didn't find something that suited my needs.
I have a function that checks if a document exists or not on Firestore, then if the document exists the function must return false, otherwise if not exists, true.
The problem is that the return of the function is always null and also compiler told me that the function doesn't have a return statement but I don't understand why.
This is the code, the important function is checkMissingId the other one just checks if the string id has a valid format or not.
Code :
bool checkStr(String id, String letter, String str) {
if (id.length < 1) {
print("Id is too short");
return false;
} else {
if ('a'.codeUnitAt(0) > letter.codeUnitAt(0) ||
'z'.codeUnitAt(0) < letter.codeUnitAt(0)) {
print("User name begins with bad word!");
return false;
}
print("ids/tabs/" + letter);
return true;
}
}
Future<bool> checkMissingId(String id, context) async {
String str = id.toLowerCase();
String letter = str[0];
if (checkStr(id, letter, str) == false)
return false; //checks some rules on strings
else {
try {
await FirebaseFirestore.instance.collection("ids/tabs/" + letter).doc(str).get()
.then((DocumentSnapshot documentSnapshot) { //Maybe here!(??)
if (documentSnapshot.exists) {
print("Document exists!");
return false;
} else {
print('Document does not exist on the database');
return true;
}
});
} catch (e) {
await showErrDialog(context, e.code);
return false;
}
}
}
The problem is that you are using both await and .then() for getting data from Firestore. Replace your function with this to get desired result:
Future<bool> checkMissingId(String id, context) async {
String str = id.toLowerCase();
String letter = str[0];
if (checkStr(id, letter, str) == false) return false; //checks some rules on strings
else {
try {
DocumentSnapshot documentSnapshot = await FirebaseFirestore.instance.collection("ids/tabs/" + letter).doc(str).get();
if (documentSnapshot.exists) {
print("Document exists!");
return false;
} else {
print('Document does not exist on the database');
return true;
}
} catch (e) {
await showErrDialog(context, e.code);
return false;
}
}
}
Try this:
Future<bool> checkMissingId(String id, context) async {
String str = id.toLowerCase();
String letter = str[0];
if (checkStr(id, letter, str) == false)
return false; //checks some rules on strings
else {
try {
var data = await FirebaseFirestore.instance.collection("ids/tabs/" + letter).doc(str).get()
if (data.exists) {
print("Document exists!");
return false;
} else {
print('Document does not exist on the database');
return true;
}
} catch (e) {
await showErrDialog(context, e.code);
return false;
}
}
}
The problem was that in the .then(...) function, it takes a function as input. So, you wouldn't be able to return anything. Because it doesn't return data to your function.
Related
I have this function here it gets data from API and stores the data locally and I want to display a percent value like 20% ..... 100% in the splash screen while this function is being executed how can I do that?
_getCleaningDate(
{required AppGetCleaningDateEvent event,
required Emitter<AppState> emit}) async {
userDataModel = await SQLService.getUserData();
if (userDataModel == null) {
print('No User Data');
}
if (userDataModel != null) {
try {
emit(AppGetCleaningDateLoadingState());
propertiesModel = null;
offlineProperties = null;
bool hasNetwork = await Services.hasNetwork();
this.hasNetwork = hasNetwork;
if (hasNetwork) {
propertiesModel = await Services.getCleaningDate(
token: userDataModel!.token!,
cleanday: AvadaTheme.formateCleaningDate(event.dateTime));
await _syncAllData(event: AppSyncAllDataEvent(), emit: emit);
offlineProperties = await SQLService.getProperties(
AvadaTheme.formateCleaningDateFromStored(event.dateTime));
emit(AppGetCleaningDateSuccessState());
} else {
offlineProperties = await SQLService.getProperties(
AvadaTheme.formateCleaningDateFromStored(event.dateTime));
emit(AppGetCleaningDateSuccessState());
}
} on DioError catch (error) {
emit(AppGetCleaningDateErrorState(error.message));
}
}
}
I have a simple flutter code to retrieve some data from Firestore. the data is retireved correctly, however passing the data from the future function making the result always null. can you advise how to adapt the code to return the list?
that is the class where the actual query is happening:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
List businessprofileslist = [];
try {
await BusinessProfilesCollection.get().then((QuerySnapshot) {
QuerySnapshot.docs.forEach((element) {
businessprofileslist.add(element.data());
print(businessprofileslist[0]);
});
});
} catch (e) {
print(e.toString());
return null;
}
}
}
here is the page where I am calling the function: (however the result is always null)
class _ProfilesListPageState extends State<ProfilesListPage> {
List businessprofileslist = [];
#override
void initState() {
super.initState();
fetchBusinessProfilesList();
}
fetchBusinessProfilesList() async {
dynamic result = await DatabaseManager().GetBusinessProfilesCollection();
print(result.toString());
if (result == null) {
print('enable to retieve');
} else {
print('success');
setState(() {
businessprofileslist = result;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold();
}
}
You're not returning anything from GetBusinessProfilesCollection but null, so the result seems somewhat expected.
I guess you want to do:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
List businessprofileslist = [];
try {
var QuerySnapshot = await BusinessProfilesCollection.get();
querySnapshot.docs.forEach((element) {
businessprofileslist.add(element.data());
});
return businessprofileslist;
} catch (e) {
print(e.toString());
return null;
}
}
}
Btw: returning null when the load fails, is just going to lead to a null pointer exception when you then do print(result.toString());. So I recommend not catching the error and just letting it bubble up. With that your code can be simplified to:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
var QuerySnapshot = await BusinessProfilesCollection.get();
return querySnapshot.docs.map((element) => element.data());
}
}
You just need to return the list
return businessprofileslist;
CODE :
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
List businessprofileslist = [];
try {
await BusinessProfilesCollection.get().then((QuerySnapshot) {
QuerySnapshot.docs.forEach((element) {
businessprofileslist.add(element.data());
print(businessprofileslist[0]);
});
// you just need to return the list here after filling it up
return businessprofileslist;
});
} catch (e) {
print(e.toString());
return null;
}
}
}
Code with a little improvement:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
await BusinessProfilesCollection.get().then((QuerySnapshot) {
QuerySnapshot.docs.map((doc) => doc.data()).toList();
});
}
}
Try that with calling the function in feching
fetchBusinessProfilesList()
async {
dynamic result ;
await DatabaseManager().GetBusinessProfilesCollection().then((value){
result=value;
print(result.toString());
if (result == null) {
print('enable to retieve');
} else {
print('success');
setState(() {
businessprofileslist = result;
});
}
});
}
I am trying to build a URL from a Firebase Storage file but the Future<String> I have built always seems to return null. This is the Future I am calling:
Future<String> getUrlFromStorageRefFromDocumentRef(
DocumentReference docRef) async {
try {
docRef.get().then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
String filename = documentSnapshot.get('file');
firebase_storage.Reference ref = firebase_storage
.FirebaseStorage.instance
.ref()
.child('/flamelink/media/$filename');
if (ref == null) {
return Future.error("Storage Reference is null");
} else {
print(ref.fullPath);
return Future.value(
'https://storage.googleapis.com/xxxxxxxxx.appspot.com/${ref.fullPath}');
}
} else {
return Future.error('No Snapshot for DocumentReference ${docRef.id}');
}
});
} catch (e) {
print(e);
return Future.error('No DocumentReference for ID ${docRef.id}');
}
}
The line in question is :
return Future.value(
'https://storage.googleapis.com/xxxxxxxxx.appspot.com/${ref.fullPath}');
It's worth noting that the String is generated from the Firebase Storage path and everything looks perfect until it comes to return the value.
It should return the String value back to my calling code which at the moment looks like this:
DocButtonCallback docCallback = () async {
bool isKidsDoc = item.screenId == StringsManager.instance.screenIdKids;
try {
// first we need to get the URL for the document ...
var url = await AssetManager.instance
.getUrlFromStorageRefFromDocumentRef(isKidsDoc
? feature.relatedDocumentKidsRef
: feature.relatedDocumentRef);
String urlString = url.toString();
canLaunch(urlString).then((value) {
launch(urlString);
}).catchError((error) {
// TODO: open alert to tell user
});
} catch (error) {
print(error);
}
};
I have tried many different ways to get that String including:
DocButtonCallback docCallback = () async {
bool isKidsDoc = item.screenId == StringsManager.instance.screenIdKids;
await AssetManager.instance
.getUrlFromStorageRefFromDocumentRef(isKidsDoc
? feature.relatedDocumentKidsRef
: feature.relatedDocumentRef)
.then((urlString) {
canLaunch(urlString).then((value) {
launch(urlString);
}).catchError((error) {
// TODO: open alert to tell user
});
}).catchError((error) {
// TODO: open alert to tell user
});
};
For some reason, the Future always returns null. What am I doing wrong here?
You are returning the Future value inside the then() callback, which essentially returns this value from the callback itself rather than from your getUrlFromStorageRefFromDocumentRef() function. There you should only need to add a return statement before that:
Current:
docRef.get().then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
...
After:
/// Adding the return statement here to return the actual value
/// returned internally by the then callback
return docRef.get().then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
...
If you hover over the then() callback, your IDE should show you that this callback will return Future<T> (or whatever generic type placeholder) which need to be returned as well in order to make it available
I have this function that calls a Future<bool> function :
bool checkId(String id, context) {
bool ret;
checkMissingId(id, context).then((value) => ret = value);
return ret;
That calls :
Future<bool> checkMissingId(String id, context) async {
String str = id.toLowerCase();
String letter = str[0];
if (checkStr(id, letter, str) == false)
return false; //checks some rules on strings
else {
try {
var data = await FirebaseFirestore.instance
.collection("ids/tabs/" + letter)
.doc(str)
.get();
if (data.exists) {
return false;
} else
return true;
} catch (e) {
await showErrDialog(context, e.code);
return false;
}
}
}
ret returns null, not a bool value.
Edit : checkId must be of type bool, not Future<bool>
Because it is null when the checkId function returns. You should await the operation, like this:
Future<bool> checkId(String id, context) async {
bool ret = await checkMissingId(id, context);
return ret;
}
You need to pause the execution of the program for the checkMissingId method to complete before return the ret variable. You do this by using the await keyword and marking the function as async.
You should change the code to:
Future<bool> checkId(String id, context) async {
bool ret = await checkMissingId(id, context);
return ret;
}
In my Flutter app, I have a function returning Future, but I wanna get result as Stream. Here is the function :
Future<bool> isGpsOn() async {
if (await Geolocator().isLocationServiceEnabled()) {
return true;
} else {
return false;
}
}
How to do that?
Read the manual and check my answer:
Stream<bool> gpsStatusStream() async* {
bool enabled;
while (true) {
try {
bool isEnabled = await Geolocator().isLocationServiceEnabled();
if (enabled != isEnabled) {
enabled = isEnabled;
yield enabled;
}
}
catch (error) {}
await Future.delayed(Duration(seconds: 5));
}
}
gpsStatusStream().listen((enabled) {
print(enabled ? 'enabled' : 'disabled');
});
or create convertor:
Stream futureToStream(fn, defaultValue, Duration duration) async* {
var result;
while (true) {
try {
result = await fn();
}
catch (error) {
result = defaultValue;
}
finally {
yield result;
}
await Future.delayed(duration);
}
}
Future<bool> isGpsOn() async {
return await Geolocator().isLocationServiceEnabled();
}
final gpsStatusStream = futureToStream(isGpsOn, false, Duration(seconds: 5));
gpsStatusStream.listen((enabled) {
print(enabled ? 'enabled' : 'disabled');
});
If you don't want to change the return type of your function, you could make callers convert the Future<T> to a Stream<T> by simply calling asStream() on the returned Future.