Problem while reading document from firestrore in flutter - flutter

I am new to Firebase I want to read all data of document here is how i am trying to read
This is my function to get data.
Future<List<AgencyModel>> getAgencyData() async {
List<AgencyModel> agencyListModel = [];
try {
agencyListModel = await _db.collection('colelctionName')
.doc('myDoc')
.snapshots()
.map((doc)=> AgencyModel.fromJson(doc.data()!)).toList();
print('List : ${agencyListModel.length}');
return agencyListModel;
} catch (e) {
debugPrint('Exception : $e');
rethrow;
}
}
This is how i am calling the above function
getAgencyDetails() async {
List<AgencyModel> data = await fireStoreService.getAgencyData();
print('Data : ${data.first}');}
and this is my models class fromjson function
factory AgencyModel.fromJson(Map<String, dynamic> json) {
return AgencyModel(
agencyName: json['agencyName'],
agencyContact: json['agencyContact'],
agencyAddress: json['agencyAddress'],
cnic: json['cnic'],
agencyContactDetails: json['agencyContactDetails'],
certificatesUrl: json['certificatesUrl'],
locationUrl: json['locationUrl'],
earning: json['earning'],
processing: json['processing'],
onHold: json['onHold']);}
I am not getting any error or exception, Also these two print statements does not work not display anything not even the Strings List : and Data : i.e
print('List : ${agencyListModel.length}');
print('Data : ${data.first}');}

UPDATE:
According to the documentation:
https://firebase.google.com/docs/firestore/query-data/get-data#dart_1
It is necessary to distinguish whether you want to retrieve the data only once or listen to changes over the document in real time.
It seems to me like you want to accomplish 1. case that you only want to retrieve data once. In that case.
You should change:
agencyListModel = await _db.collection('collectionName')
.doc('myDoc')
.snapshots()
agencyListModel = await _db.collection('collectionName')
.doc('myDoc')
.get()

Related

Return multiple value from function in dart

Hi so i'm new to dart and I'm having an issue with returning 2 value from a dart function.
Currently I have this function :
Future LoadAllData({required Map data, required String detailId}) async {
loadUserData(data: data);
powData = await Database.getPowDataActive(detailId: detailId);
return powData;
}
so getPowDataActive is a function that will fetch a data from my database and it will return some map data, load user data will also fetch data and will also return some map. I wanted to use the loadAllData function for my futureBuilder and use the snapshot data from this 2 function for different purposes, can I do so ? or I have to first combine the return from both function into 1 variable and access it differently ?
Thanks before
You can create a model like this:
class LoadDataResult {
final Map userData;
final Map powData;
LoadDataResult({#requierd this.userData, #requierd this.powData, });
}
and then use it like this:
Future<LoadDataResult> LoadAllData({required Map data, required String detailId}) async {
var userData = await loadUserData(data: data);
powData = await Database.getPowDataActive(detailId: detailId);
return LoadDataResult(userData:userData, powData: powData);
}
and then use it like this in futureBuilder:
LoadDataResult data = snapshot.data;
print('${data. userData}');

Flutter pagination with firestore stream

How to properly implement pagination with firestore stream on flutter (in this case flutter web) ?
my current approach with bloc which is most likely wrong is like this
function called on bloc when load next page, notice that i increased the lastPage variable of the state by 1 each time the function is called:
Stream<JobPostingState> _loadNextPage() async* {
yield state.copyWith(isLoading: true);
try {
service
.getAllDataByClassPage(state.lastPage+1)
.listen((List<Future<DataJob>> listDataJob) async {
List<DataJob?> listData = [];
await Future.forEach(listDataJob, (dynamic element) async {
DataJob data= await element;
listData.add(data);
});
bool isHasMoreData = state.listJobPostBlock.length!=listData.length;
//Update data on state here
});
} on Exception catch (e, s) {
yield StateFailure(error: e.toString());
}}
function called to get the stream data
Stream<List<Future<DataJob>>> getAllDataByClassPage(
String className, int page) {
Stream<QuerySnapshot> stream;
if (className.isNotEmpty)
stream = collection
.orderBy('timestamp', "desc")
.where('class', "==", className).limit(page*20)
.onSnapshot;
else
stream = collection.onSnapshot;
return stream.map((QuerySnapshot query) {
return query.docs.map((e) async {
return DataJob.fromMap(e.data());
}).toList();
});
}
With this approach it works as intended where the data loaded increased when i load next page and still listening to the stream, but i dont know if this is proper approach since it replace the stream could it possibly read the data twice and end up making my read count on firestore much more than without using pagination. Any advice is really appreciated, thanks.
Your approach is not very the best possible indeed, and as you scale you going to be more costly. What I would do in your shoes would be to create a global variable that represents your stream so you can manipulate it. I can't see all of your code so I am going to be as generic as possible so you can apply this to your code.
First let's declare the stream controller as a global variable that can hold the value of your stream:
StreamController<List<DocumentSnapshot>> streamController =
StreamController<List<DocumentSnapshot>>();
After that we need to change your getAllDataByClassPage function to the following:
async getAllDataByClassPage(String className) {
Stream stream = streamController.stream;
//taking out of the code your className logic
...
if(stream.isEmpty){
QuerySnapshot snap = await collection.orderBy('timestamp', "desc")
.where('class', "==", className)
.limit(20)
.onSnapshot
streamController.add(snap.docs);
}else{
DocumentSnapshot lastDoc = stream.last;
QuerySnapshot snap = await collection.orderBy('timestamp', "desc")
.where('class', "==", className)
.startAfterDocument(lastDoc)
.limit(20)
.onSnapshot;
streamController.add(snap.docs);
}
}
After that all you need to do in order to get the stream is invoke streamController.stream;
NOTE: I did not test this code but this is the general ideal of what you should try to do.
You can keep track of last document and if has more data on the list using startAfterDocument method. something like this
final data = await db
.collection(collection)
.where(field, arrayContains: value)
.limit(limit)
.startAfterDocument(lastDoc)
.get()
.then((snapshots) => {
'lastDoc': snapshots.docs[snapshots.size - 1],
'docs': snapshots.docs.map((e) => e.data()).toList(),
'hasMore': snapshots.docs.length == limit,
});

Unable to use a Future value - Flutter/Dart

I've fetched a json object and deserialized it and then returned it too.
I want to use this in another file.
I'm unable to assign the values that I'm getting in the first step.
Here are all the codes...
Service
Future getGeoPoints(String accessToken, String tripId) async {
String requestUrl;
var response = await get(
Uri.parse(requestUrl),
headers: {
'Authorization': "Bearer $accessToken",
},
);
if (response.statusCode == 200) {
Map<String, dynamic> responseBody = json.decode(response.body);
GetGeoPoints geoPoints = GetGeoPoints.fromJson(responseBody);
List listOfGeoPoints = [];
for (var geoPoint in geoPoints.geoPoints) {
listOfGeoPoints.add(
{
'latitude': geoPoint.latitude,
'longitude': geoPoint.longitude,
'timestamp': geoPoint.timeStamp,
},
);
}
// print('List of geo points: ' + '$listOfGeoPoints');
return listOfGeoPoints;
} else {
throw Exception('Failed to load data from server');
}
}
File where I need the above values
List routeCoordinates;
Future<void> getValues() async {
getGeoPoints(widget.accessToken, widget.tripId)
.then((value) => routeCoordinates = value);
}
When I run the app, routeCoordinates is null but when I hotreload, it contains the value.
I want to have the values as soon as the screen starts. What is the right way to assign the values here?
I've also tried this:
routeCoordinates = getGeoPoints...
It throws error..
Please help.. Thanks..
The function getGeoPoints() is an asynchronous one. But on the other file, you are not using the await keyword, instead you are using then(). So your code is not waiting for that function to return value.
Try using below code,
List routeCoordinates;
Future<void> getValues() async {
routeCoordinates = await getGeoPoints(widget.accessToken, widget.tripId);
}
Let us know how it went.
You need to use a FutureBuilder to define a behaviour depending on the state of the request. You'll be able to tell the widget what to return while your app is waiting for the response to your request. You can also return a specific widget if you get an error(if your user is offline, for example).
Edit: I've linked the official docs but give this article a read if it's not clear enough.

How to send Map<String,bool> to cloud Firestore in Flutter

We have a Map declared as follows:
Map<String, bool> _selection = {};
It contains data as follows:
key = 'Messi'
value = 'true'
We wanted to send the data containing in the Map _selection.
Right now we are trying using a method defined as given below:
Future<Map<String, dynamic>> votedown() async {
_selection.forEach((key, value) {
Map<dynamic, dynamic> comdata = <dynamic, dynamic>{
'criteriaName': key,
'isChecked': value,
};
return comdata;
});
}
We are sending data to firestore as:
DocumentReference ref =
FirebaseFirestore.instance.collection('posts').doc();
await ref.set({
'Players': {
'Football': {
await votedown(),
}
}
});
But it is giving error as:
ArgumentError (Invalid argument: Instance of '_CompactLinkedHashSet<Map<String, dynamic>>')
Please help me to solve this?
Desired Output in Firestore in 'posts' collection:
--> Players
--> Football
--> PlayerName : Messi
isStriker : true
It seems the problem is in brackets {} surrounding the await downvote(). As the method is returning a map object already, if you put into the brackets you will have set of map nested object. Such objects are not in types available in the Firestore (reference) and this is reason of the error.
I do not have a playground to test it, but please try bellow:
DocumentReference ref =
FirebaseFirestore.instance.collection('posts').doc();
await ref.set({
'Players': {
'Football': await votedown(),
}
});

How to fetch data from Firestore and display them in Flutter?

I'm a beginner in Flutter and firestore. I have a collection in firestore with following order:
event->'a user specific id'->post->'a post id->'post details'. you can see hereFirestore1 and hereFirestore2
When I try to fetch the 'postdetails', only thing I get is 'Instance of 'DocumentSnapshot',see hereResponse
What i tried:
getEvents() async {
setState(() {
_isLoading = true;
});
DocumentSnapshot snapshot = await eventref
.doc(uid)
.collection('post')
//.orderBy('Date', descending: true)
.doc()
.get();
print('Snapshot : ${snapshot}');
return snapshot;
// setState(() {
// _isLoading = false;
// print(event);
// });
}
I have also made a model for events. See hereEvent Model
I want to fetch data and display them as card.
Any help?
Thanks in Advance
print('Snapshot : ${snapshot.data()["Date"]}');
Using data() you can access the fields value.
snapshot.get("field_name")
You need to ask flutter to retrieve the fields inside the document snapshot.
Will need a more detailed order of your firestore collection and document to provide the exact code. You may follow the sample below:
On firestore:
-Collection: "users"
---Document: "userId"
-------Field: "username"
-------Field: "birthdate"
Code:
DocumentSnapshot doc =await _firestore.collection("users").doc(userId).get();
print(doc.get('username'));
print(doc.get('birthdate'));