retrieving firebase data in init state - flutter

I want to fetch a simple document from firebase and store it in a map as soon as the screen loads,
I'm trying to do this through the init state, i.e. calling the fetching function in the init state, but my map is still null.
Here is some code :
Map<String, int> minDelivery;
void minimumDelivery() async{
await FirebaseFirestore.instance
.collection("minimumDelivery")
.limit(1)
.get()
.then((QuerySnapshot qs) {
qs.docs.forEach((doc) {
int amount = doc["min_amount"];
int charge = doc["charge"];
minDelivery = {
"amount":amount,
"charge":charge
};
});
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
minimumDelivery();
}
But the map is null when I am trying to access its data.
I don't see where the problem is.I just want the map to have the required data at the start of screen.

Related

flutter X firebase: how to correctly initialize fields with data in different branches in firebase realtime database

I have a piece of code trying to initialize the fields with data from two different users in the firebase realtime database, I tried various ways but all of them don't work and the field is not initialized error keeps popping up.
Here is the code:
class _PartnerProfilePageState extends State<PartnerProfilePage> {
final userUID = FirebaseAuth.instance.currentUser!.uid;
final database = FirebaseDatabase.instance.ref();
late final partnerUID;
late final profilePath;
late final partnerName;
late final birthday;
late final en;
#override
initState() {
super.initState();
initInfo();
}
initInfo() async {
database.child(userUID).onValue.listen((event) {
final data = Map<dynamic, dynamic>.from(event.snapshot.value as Map);
setState(() {
partnerUID = data['partner'];
en = data['language'].toString().startsWith('en');
initPartnerInfo();
});
});
}
Future initPartnerInfo() async {
final ppsnapshot =
await database.child(partnerUID).child('profilePath').get();
profilePath = ppsnapshot.value.toString();
final nsnapshot = await database.child(partnerUID).child('username').get();
partnerName = nsnapshot.value.toString();
final bsnapshot = await database.child(partnerUID).child('birtday').get();
birthday = bsnapshot.value.toString();
}
//rest of other unrelated stuff like build down there
}
(My firebase realtime database has no 'user' branch but directly save every user in the root with their userid).
I think there is a problem with the async initializing. The build method can try to build before you initialize the last variable(Because initState method can not async). You can easily check my theory.Delete the 'method call'(the initInfo) inside the initState(). Just create a button in the screen, give it a anonymous async function, inside the function call your init method and try to call your variables like this:
() async{
await initInfo();
print('partnerUID');
}
Easy way to checking out initialization process. Hope it helps.

Future<dynamic> is not a subtype of List<dynamic>

So I am trying to pass a list of String values from firestore table, but I am getting an exception type 'Future<dynamic>' is not a subtype of type 'List<dynamic>'
This is the function
getLectureList(String userId) async {
var collection = FirebaseFirestore.instance.collection('students');
var docSnapshot = await collection.doc(userId).get();
Map<String, dynamic>? data = docSnapshot.data();
List<String> _lectureList =
await data!['attendance']; //This line is kinda giving me trouble
userInfo = FirestoreWrapper()
.getStudentFromData(docId: currentUser(), rawData: data);
return _lectureList;
}
And this is the function where I am getting the exception thrown
#override
void initState() {
lectureList = getLectureList(currentUser()); // Getting an exception here
NearbyConn(context).searchDevices(devices: deviceList);
super.initState();
}
tried using await in the getLectureList() method but still getting the same problem
Why do you await your data? You already got it.
List<String> _lectureList = data!['attendance'];
Please note that I don't know what your data structure looks like, so I cannot tell you if this is correct, I can only tell you that it is more correct than before, because the await did not belong there.
You are getting an exception here lectureList = getLectureList(currentUser()); because the the parameter required by the getLectureList() method is the userId which is a string. I do not know what currentUser() return but I'm assuming it's the userId that you need when calling the getLectureList() method. Based on the error, it looks like currentUser() is an async method that returns a future after some time.
You're not awaiting that future. You shouldn't make the initState() method async so move the code block out of it into a separate method and then call it from initState().
Something like this,
#override
void initState() {
super.initState();
_getData();
}
void _getData() async {
lectureList =
getLectureList(await currentUser());
NearbyConn(context).searchDevices(devices: deviceList);
}
or
#override
void initState() {
super.initState();
_getData();
}
void _getData() async {
String _userID = await currentUser();
lectureList = getLectureList(_userID);
NearbyConn(context).searchDevices(devices: deviceList);
}
Which I recommend so you can see all the parts.
Making your method parameters required named parameters also help you to easily see what is needed to pass to a function/class/.
Eg.
getLectureList({required String userId}){
...
}
Your IDE will alert you on the type of object the function requires and it makes things clearer.
Ultimately, I think typing your classes makes it so much more easier to fetch data from fireStore Typing CollectionReference and DocumentReference
This way you can easily do this,
final moviesRef = FirebaseFirestore.instance.collection('movies').withConverter<Movie>(
fromFirestore: (snapshot, _) => Movie.fromJson(snapshot.data()!),
toFirestore: (movie, _) => movie.toJson(),
);
and get your data this way,
Future<void> main() async {
// Obtain science-fiction movies
List<QueryDocumentSnapshot<Movie>> movies = await moviesRef
.where('genre', isEqualTo: 'Sci-fi')
.get()
.then((snapshot) => snapshot.docs);
// Add a movie
await moviesRef.add(
Movie(
title: 'Star Wars: A New Hope (Episode IV)',
genre: 'Sci-fi'
),
);
// Get a movie with the id 42
Movie movie42 = await moviesRef.doc('42').get().then((snapshot) => snapshot.data()!);
}
Keeps everything dry and tidy.
< The data comes to list format thats why showing the exception of datatype >
List<String> lectureList = await getLectureList(currentUser()); // use
Future<List<String>> getLectureList(String userId) async {
- your code -
}
Instead of
List _lectureList =
await data!['attendance'];
Try this
_lectureList = await data![] As List

Why am I getting 'Future<dynamic>' instead of the return value in the function?

I'm trying to get the return value in my function but the output is 'Instance of Future' instead of the value of school field name in the database
#override
void initState() {
userId = _auth.currentUser!.uid;
publisherSchool =
getName(widget.postInfo['publisher-Id'], 'school').toString();
super.initState();
}
Future getName(String publisherUid, String fieldname) async {
DocumentSnapshot publisherSnapshot = await FirebaseFirestore.instance
.collection('users')
.doc(publisherUid)
.get();
print(publisherSnapshot.get(fieldname));
return publisherSnapshot.get(fieldname);
}
but whenever i'm printing the publisherSnapshop.get(fieldname) i'm getting the correct value from the database
There are 2 ways to do it, you can create a Future method and call it inside the initState like below:
#override
void initState() {
initial();
super.initState();
}
Future<void> initial() async {
userId = _auth.currentUser!.uid;
// Remember using `()` to wrap the `await` to get it result
publisherSchool = (await getName(widget.postInfo['publisher-Id'], 'school')).toString();
}
Or you can use .then to call it directly inside the initState:
#override
void initState() {
userId = _auth.currentUser!.uid;
getName(widget.postInfo['publisher-Id'], 'school').then((value) {
publisherSchool = value.toString();
});
super.initState();
}
When you declare the getName() function, specify the return type as Future<String>, and then when you call getName(), you need to await the result e.g. publisherSchool = await getName(widget.postInfo['publisher-Id'], 'school').toString();
The reason why you are not getting the correct response is because whenever you are working with Futures it takes some time to finish and return the results. Meanwhile it is fetching the result you have to make it await so that the program will continue once that future function is complete since await/then is nowhere to be found in your code hence the issues.
To solve this make this change:
Change
publisherSchool =
getName(widget.postInfo['publisher-Id'], 'school').toString();
To
getName(widget.postInfo['publisher-Id'],
'school').then((value){
publisherSchool=value.toString()});

Get Shared Preferences Value using Provider State Management

I try to Get Value from SharedPrefereces but I get a null value if I try to get Data in Main Page, I try to get the data when the state on the main page is created but I sometimes get null like this Available URL: http://169.172.70.108:8008/api/v1/iksk/self?idtraining=null
but after hot reload I managed to get the result like this
Available URL: http://169.172.70.208:8008/api/v1/iksk/self?idtraining=2021-01-21
this is my code
#override
void initState() {
// get pelatihan
MySharedPreferences.instance
.getStringValue(key: 'namaPelatihan')
.then((value) {
namaPelatihan = value;
// get nama Peserta
MySharedPreferences.instance
.getStringValue(key: 'namaPeserta')
.then((value) {
namaPeserta = value;
});
});
how do I get real-time results (get results when redirecting to the main page) using provider state management?
first create a function then use async and await with it
then fellow this code
_transitionToNextPageAfterSplash() async {
final auth = await SharedPreferences.getInstance()
.then((value) => value.getBool('auth') ?? false);
}

Returning a single Firestore field value from a known documentID

I am wanting to do a simple lookup in Firestore to get a 'propertyName' for a given 'propertyID' (the 'properties' documentID) from the collection 'properties' and assign this to the variable propertyName.
The print() gives 'from initstate propertyName = Instance of 'Future' and not the actual value in Firestore. How do I extract the actual value?
I have tried also using StreamBuilder but keep having the same issue.
#override
void initState() {
super.initState();
var propertyName = _getPropertyNameFromPropertyID(
widget.propertyID); // to get propertyName from Firestore
print('from initstate propertyName = $propertyName');
}
Future _getPropertyNameFromPropertyID(propertyID) async {
DocumentSnapshot snapshot = await Firestore.instance
.collection('properties')
.document(propertyID)
.get();
String result = snapshot['propertyName'].toString();
return result;
}
That's because your method returns a Future so you will need to use async/await or just get the result directly from the Future.
Option 1
#override
void initState() {
super.initState();
_getPropertyNameFromPropertyID(widget.propertyID).then ((propertyName){
print('from initstate propertyName = $propertyName');
});
}
Option 2
#override
void initState() {
super.initState();
_loadAsyncData();
}
_loadAsyncData() async {
var propertyName = await _getPropertyNameFromPropertyID(
widget.propertyID); // to get propertyName from Firestore
print('from initstate propertyName = $propertyName');
}
Future _getPropertyNameFromPropertyID(propertyID) async {
DocumentSnapshot snapshot = await Firestore.instance
.collection('properties')
.document(propertyID)
.get();
String result = snapshot['propertyName'].toString();
return result;
}