Flutter + Firebase: How to set state on bool value in documents - firebase

I am trying to set the value of a variable based on the return value of a field (bool) in Firestore.
So far, this is what I have come up with;
First I call the method here;
#override
void initState() {
super.initState();
getAdventureStatus();
}
And this is the method.
Future getAdventureStatus() async {
Firestore.instance
.collection('adventures')
.document(widget.currentUser.id)
.collection('user_adventures')
.where('adventure_active', isEqualTo: 'false');
setState(() {
adventureActive = true;
print('${adventureActive.toString()}');
});}
What am I doing wrong and what is the most pragmatic way of doing this?

I think you just remove the set state call and set the bool to true direct.
Future getAdventureStatus() async {
Firebase.instance.document()
...
adventureActive = true;
}

Related

How to sequentially call the Getx controllers

My Task is :
I have an list of orders in firebase in users collection ,
I want to get information of those orders which is in orders collection,
And moment a list of order is updated in the firebase users collection.
The orders list should be automatically updated to get the latest information from the orders collection.
But for that i have to wait until list of orders is fetched from the users collection and only then i can query from orders collection about those orders..
I am stuck here,
And i want to actually understand Getx's bindStream , ever(), and observable variables,and Obx() is used in widget , But what if it is normal variable and i want to listen to it's changes ,how to do that, because Obx() can be only used while you use Widget
So far my code:
controllers.dart
UtilityController utilityController = UtilityController.instance;
CartController cartController = CartController.instance;
OrderController orderController = OrderController.instance;
UserModel.dart
class UserModel {
String? uid;
String? email;
String? name;
bool? isAdmin;
String? password;
List<CartItemModel>? cart;
String? token;
List<String>? orders;
UserModel({this.uid, this.email, this.name, this.isAdmin, this.password, this.cart, this.token, this.orders});
UserModel.fromSnapshot(DocumentSnapshot snapshot) {
uid = snapshot.id;
name = snapshot['name'];
token = snapshot['token'];
cart = _convertCartItems(snapshot['cart'] ?? []);
orders = new List<String>.from(snapshot['orders']);
}
List<CartItemModel> _convertCartItems(List cartFomDb) {
List<CartItemModel> _result = [];
if (cartFomDb.length > 0) {
cartFomDb.forEach((element) {
_result.add(CartItemModel.fromMap(element));
});
}
return _result;
}
}
UtilityController.dart
class UtilityController extends GetxController {
static UtilityController instance = Get.find();
Rx<UserModel> userModel = UserModel().obs;
#override
void onReady() {
super.onReady();
getUserType();
userModel.bindStream(listenToUser());
}
Stream<UserModel> listenToUser() {
return FirebaseFirestore.instance
.collection("users")
.doc(FirebaseAuth.instance.currentUser?.uid)
.snapshots()
.map((snapshot) => UserModel.fromSnapshot(snapshot));
}
OrderController.dart
class OrderController extends GetxController {
static OrderController instance = Get.find();
RxList<OrderModel> orders = RxList<OrderModel>([]);
#override
void onReady() {
super.onReady();
orders.bindStream(getAllOrders());
ever(utilityController.userModel, function); --> I am using this , but i know this is not the correct way
}
function(UserModel userModel) {
getAllOrders();
}
Stream<List<OrderModel>> getAllOrders() {
return FirebaseFirestore.instance
.collection("orders")
.where(FieldPath.documentId, whereIn: utilityController.userModel.value.orders)
.snapshots()
.map((query) => query.docs.map((item) => OrderModel.fromMap(item.data(), item.id)).toList());
}
}
The utilityController.userModel.value.orders is null !!! it's not yet loaded, so all the orders are fetched :(
And even if the orders are changed... But new orders are not fetched from the orders collection
How to get over this?
Consider using like this.
class OrderController extends GetxController {
static OrderController instance = Get.find();
final isLoading = true.obs;
final isDone = false.obs;
var orders = OrderModel([]).obs;
#override
void onInit() async {
await listenForOrders();
super.onInit();
}
Future listenForOrders() async {
isLoading.value = true;
isDone.value = false;
Stream<QuerySnapshot> _userOrders;
_userOrders = getAllOrders();
_userOrders.listen((QuerySnapshot query) {
if (query.docs.isNotEmpty) {
query.docs.forEach((element) {
orders.addIf(!orders.contains(element), OrderModel.fromDocumentSnapshot(element));
});
} else {
isDone.value = true;
}
isLoading.value = false;
});
}
Stream<QuerySnapshot> getAllOrders() {
return FirebaseFirestore.instance
.collection("orders")
.where(FieldPath.documentId, whereIn: utilityController.userModel.value.orders)
.snapshots();
}
}
The best approach is to use the worker functions provided by getx controller like:
ever - is called every time the Rx variable emits a new value.
everAll - Much like ever , but it takes a List of Rx values Called every time its variable is changed. That's it.
once - is called only the first time the variable has been changed.

How can I wait for my future function in map function? (Flutter)

So, I'm building my app in Flutter and unfortunately, I have recently come across an error. So what I want to do in my TestProvider class is to get data from firestore (what getQuestionFromFirebase() function is doing), and after that happens, I want to create a map from DocumentSnapshot (what questionMapFromFirebase() function is doing). And there comes an error, because I can't async in map function so my function doesn't wait for the result from previous function, and returns null. Any solutions? *I tried to return map from getQuestionFromFirebase() - Future, but later I can't use value from it because, my function wants pure map.
class TestProvider {
FirebaseFirestore _firestore = FirebaseFirestore.instance;
Future<DocumentSnapshot> getQuestionFromFirebase(String documentId) async {
return await _firestore.collection('questions').doc(documentId).get();
}
Map questionMapFromFirebase(String documentId) {
Map questionMapFromFirebase;
getQuestionFromFirebase(documentId).then((DocumentSnapshot carSnapshot) => {
questionMapFromFirebase = carSnapshot.data(),
});
return questionMapFromFirebase;
}
}
Later I'm using this function there:
I'm using this function later there
List<Question> listOfQuestions() {
List<int> range = numberInRange(amountOfQuestions);
List<Question> listOfQuestions;
for (int i = 1; i <= amountOfQuestions; i++) {
listOfQuestions.add(Question.fromMap(
_testProvider.questionMapFromFirebase(range[1].toString())));
}
return listOfQuestions;
}
And that's creating error when Future occurs.
The argument type 'Future<Map<dynamic, dynamic>>' can't be assigned to the parameter type 'Map<String, dynamic>'.
Edit:
So recently I've made some changes to my code and now it looks like that
class TestProvider {
FirebaseFirestore _firestore = FirebaseFirestore.instance;
Future<DocumentSnapshot> getQuestionFromFirebase(String documentId) async {
return await _firestore.collection('questions').doc(documentId).get();
}
Future<Map> questionMapFromFirebase(String documentId) async {
DocumentSnapshot ds = await getQuestionFromFirebase(documentId);
return ds.data();
}
}
and repository
class TestRepository {
final int amountOfQuestions;
TestRepository({
#required this.amountOfQuestions,
});
TestProvider _testProvider;
Future listOfQuestions() async {
List<int> range = numberInRange(amountOfQuestions);
List<Question> listOfQuestions;
for (int i = 1; i <= amountOfQuestions; i++) {
listOfQuestions.add(Question.fromMap(
await _testProvider.questionMapFromFirebase(range[i].toString())));
}
return listOfQuestions;
}
}
The problem I started to see that is that every time i tried to call function questionMapFromFirebase from TestProvider, it has been working just fine. But when i tried to call it from TestRepository it throw the error:
E/flutter (13348): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: NoSuchMethodError: The method 'questionMapFromFirebase' was called on null.
E/flutter (13348): Receiver: null
E/flutter (13348): Tried calling: questionMapFromFirebase("2")
Any other sugestions how can I handle it?
Future<Map> questionMapFromFirebase(String documentId) async {
DocumentSnapshot ds = await getQuestionFromFirebase(documentId);
return ds.data();
}
Edit
check FutureBuilder class
example, it will be inside your widget tree where the list need to be shown.
return FutureBuilder(
future: _loadQuestions(),
builder: (context, snapshot) {
if(snapshot.connectionState == ConnectionState.done){
return widgetForListing(snapshot.data);
}
return Center(child: Text('Loading...'));
},
);
And your _loadQuestions function will be as
_loadQuestions() async {
List<int> range = numberInRange(amountOfQuestions);
List<Question> listOfQuestions = [];
for (int i = 1; i <= amountOfQuestions; i++) {
listOfQuestions.add(Question.fromMap(
await _testProvider.questionMapFromFirebase(range[1].toString())));
}
return listOfQuestions; //you can get this list in **snapshot.data** of future builder
}

Wait for stream inside a Future: Flutter

I want to check if the Firebase DB is connected or not, so I have to use a Future to return a boolean
Have a check at my code..
#override
Future<bool> isAvailable() async {
bool ret = false;
await firebaseInstance.reference().child('.info/connected').onValue.listen((event) {
ret = event.snapshot.value;
});
return ret;
}
the firebaseInstace.reference is a StreamSubscription type and does not wait for the future to return me a result.
please help.
If you only need to know the current value, use once().then instead of onValue.listen
#override
Future<bool> isAvailable() async {
var snapshot = await firebaseInstance.reference().child('.info/connected').once();
return snapshot.value;
}
Instead of awaiting the end of the stream subscription (it never ends), just take the first value:
#override
Future<bool> isAvailable() => firebaseInstance.reference().child('.info/connected').onValue.first;
You can put the StreamSubcription in a variable
StreamSubscription subscription = someDOMElement.onSubmit.listen((data) {
// you code here
if (someCondition == true) {
subscription.cancel();
}
});
More can be found here is there any way to cancel a dart Future?
You can do the following:
#override
Future<bool> isAvailable() async {
bool ret = false;
Stream<Event> events =
FirebaseDatabase.instance.reference().child('.info/connected').onValue;
await for (var value in events) {
ret = value.snapshot.value;
}
return ret;
}
onValue returns a Stream<Event> and then you can use await for to iterate inside the Stream and get the data, and then it will return.

Flutter Getting a specific Firestore document data and store it into a String variable

I am trying to get data from my Firestore and store the data into a String variable named time. Then put the variable into _event.
However, when I run the code, I got null, and I don't know why.
StateFulWidget...
String event;
Map<DateTime, List> _events;
#override
void initState() {
super.initState();
Firestore.instance.collection('events').document('2019-07-
30').get().then((
DocumentSnapshot ds) {
event = ds['time'];
});
_events = {
DateTime.parse("2019-08-01"): [event]
};
}
This is my Firestore document
I am new to Flutter and I have struggled for this question for a long time. Hope someone can help me
Thank you so much
UPDATE
I change my code.
However, I am still getting errors: The method [](Intance of 'DateTime') was called on null.
String event;
Map<DateTime, List> _events;
Future<String> getData()async{
final ds = await
Firestore.instance.collection('events').document('2019-07-30').get();
return ds['time'];
}
#override
void initState() {
super.initState();
getData().then((s) {
event = s;
//when I print(s), it showed the data correctly
});
_events = {
DateTime.parse("2019-08-01"): [event]
};
Can someone shows me where I got wrong?
Thank you!
You are not waiting for the result, ie _events = .. will run before .then() clause and that is why event variable is still null;
.get() is a Future so you need to await the result.
final ds = await Firestore.instance.collection('events').document('2019-07-
30').get();
event = ds['time'];
_events = {
DateTime.parse("2019-08-01"): [event]
};
This works for me :
Firestore().instance.collection('Collection Name')
.document('Document Name').get().then((data) async {
var dataReceive = data['property Name'];
}

How to know whether actual current value is null in Firebase Realtime DB Transactions?

A firebase transaction handler should be able to handle null values, as well as it can be called multiple times. What happens if it is called on a location with a null value. (If there is no existing data) Is there a way to know it from the proceeding promise. How about following code?
let isNull = false;
db.ref('/entry').transaction( currentValue => {
if(!currentValue) {
isNull = true;
// Do the updates
} else {
// Do the update
}
return currentValue;
}).then(() => {
console.log("IsNull", isNull);
})
This example is to check whether a particular user Id is under a node
Use something on the lines of:
//Add this before OnCreate while declaring all variables
FirebaseUser firebaseUserId = FirebaseAuth.getInstance().getCurrentUser();
yourDatabaseSource.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final String thisFirebaseUser = firebaseUserId.getUid();
if (dataSnapshot.hasChild(thisFirebaseUser)) {
//check your condition
} else {
//do something
}
}
Hope this helps.

Resources