Cannot read data after updating it in firestore from flutter app - firebase

I am working with Arduino and flutter app and the communication between them is done through firebase. So basically, the data is sent from Arduino to my laptop via pyserial, and then the update is performed on my computer using python.
For eg, when the user presses a button on the circuit, the python code will edit '1' to the field in firestore. The flutter app will notice the change in the database and after updating it back to 0, it will perform a particular action.
class _MyHomePageState extends State<MyHomePage> {
var callval;
var x1;
var f;
var call;
#override
Widget build(BuildContext context) {
FirebaseFirestore.instance
.collection('joystick')
.snapshots()
.listen((QuerySnapshot querySnapshot) {
firestoreList = querySnapshot.docs;
setState(() {
callval = firestoreList.first.get('call');
});
print("call value while listening ${callval}");
if (x1 == 1 && callval == 0 && f == 0) {
print("Updated");
setState(() {
f = 1;
});
//await FlutterPhoneDirectCaller.callNumber(num1);
}
if (callval == 1) {
print("call value is 1 ");
//makecall();
FirebaseFirestore.instance.collection('joystick').doc('data').update(
{'call': 0});
setState(() {
call = firestoreList.first.get('call');
});
print("Next call value is ${call}");
setState(() {
f = 0;
});
setState(() {
x1 = 1;
});
if (call == 0) {
print("updated!");
callval = 0;
}
}
}).onError((e) => print(e));
However, when the user pushes the button a second time, the flutter app does update the database but does not show the value being changed from 0 to 1 and then back to 0 (the database value remains 0), on the console of the app. The first time I push the button, the update is performed within some seconds along with the value being changed from 0 to 1 to 0 in the console while the second time, the database is updated within milliseconds but the database value remains 0 on the console and no output message ("updated") is displayed. Hence, the app will neither be able to perform any operation the second time the button is pushed.
When I tried to remove the update and just press some buttons, it reads the different values, but it won't read any updated value while the values are being updated.
Please advise me on how can I debug the issue. I really need help, please.

It looks like you are not using setState() to inform the flutter widget tree that the value of callval has changed and the screen needs to be rebuilt. Try wrapping your assignment of callval in your firebase callback as below. This should ensure the widget using this value is rebuilt. (You will also have to be using a statefull widget - I can't see from your code snippet whether you are extending a stateless or statefull widget.)
setState(() {
callval = firestoreList.first.get('call');
});

Related

send data to firebase with flutter

I am trying to send the data to firebase in my project which I was able to , yet I want the data to override the variable. The one I am using is adding at all the time
setState(() {
velocity = newVelocity;
if (velocity > highestVelocity) {
highestVelocity = velocity;
}
});
future= new Future.delayed(const Duration(seconds: 5));
_firestore.collection('collectionName').add({
'velocity' : velocity
});
}
I think there is update function to override the var. Can I someone help me with that?
To update an existing document, you must know the ID of that document. Once you do, you can update it with:
_firestore.collection('collectionName').doc("theIdOfYourDocument").set({
'velocity' : velocity
});
Also see: how can I set my document id as user uid in cloud fire store?

Flutter : how to get newly added record from firebase real time database?

I'm using below code to retrieve all the data from my firebase chat room
void readDataFromFireBase() {
var databaseReference =
FirebaseDatabase.instance.reference().child("messages");
databaseReference
.child(chatroom.chatId)
.once()
.then((DataSnapshot snapshot) {
Map<dynamic, dynamic> values = snapshot.value;
values.forEach((key, value) {
setState(() {
chatMessageList.add(ChatMessage(
value["message"], value["fromUser"], value["timestamp"]));
chatMessageList.sort((a, b) {
var formatter = new DateFormat('MM/dd/yyyy, kk:mm');
var dateOne = formatter.parse(a.timestamp);
var selectedDate = formatter.parse(b.timestamp);
return dateOne.compareTo(selectedDate);
});
});
});
}
now how can i get notify my chat room when the new message has arrived
currently i'm using below code to listen child added event
listenDataFromFireBase() {
var db = FirebaseDatabase.instance.reference().child("messages");
db.child(chatroom.chatId).onChildAdded.listen((data) {
print("GET_NEW_DATA");
print(data.snapshot.value["message"] ?? '');
print(data.snapshot.value["fromUser"] ?? false);
print(data.snapshot.value["timestamp"] ?? '');
});
}
but there is one issue i'm facing this listenDataFromFireBase() load all the the data from particular room
My requirement is whenever new message added in chatroom i want to animate my message layout
How can i get notify my screen whenever new message will add in my chat room.
If need more information please do let me know. Thanks in advance. Your efforts will be appreciated.
As you've discovered onChildAdded fires immediately for any existing data, and after that also when any data is added. If you want to distinguish between these two cases, you'll need to combine an onChild listener and an onValue listener.
In the onValue listener, all you do is flag whether that event has happened:
databaseReference.onValue.listen((event) {
setState(() {
isInitialDataLoaded = true
});
});
Now do all your data processing in the onChildAdded listener, getting the message from the snapshot and adding it to the list. Then use the isInitialDataLoaded to detect whether this is initial data, or an update:
var db = FirebaseDatabase.instance.reference().child("messages");
db.child(chatroom.chatId).onChildAdded.listen((data) {
// TODO: get message from snapshot and add to list
if (isInitialDataLoaded) {
// TODO: alert the view about the new data
}
});
So you'll have two listeners on the same node. The Firebase SDK actually detects this situation and only reads the data from the server once, so there is no extra data being transferred in this case.
You can use onValue:
/// Fires when the data at this location is updated. `previousChildKey` is null.
Stream<Event> get onValue => _observe(_EventType.value);
But if you use onValue or onChildAdded, it will retrieve all the data under this chatroom.chatId, then when you data is added the onValue event will be fired again and will give you the new data.

How to configure Flutter Firebase Database onEntryChanged listener correctly?

I'm building a Stateful Flutter page that allows users for follow each other. Once they both follow the other person I want the state of the page to change and allow the users to message each other. I created a listener that queried the database once upon loading the page correctly. Now I'm trying to update the page for user 1 if user 2 makes a change, instantly using onEntryChange and it's not working. I've gone in and manually deleted entries in my database and the UI state remains the same. Here is my code...
String messageBtn;
FriendsVideoPageState(){
fb.child('friends/$id/friends/$uid').onChildChanged.listen(_onEntryChanged);
}
_onEntryChanged(Event event) {
print('Listener..............${event.snapshot.value.toString()}');
if (event.snapshot.value != null){
setState(() {
messageBtn = 'Message';
});
} else {
setState(() {
messageBtn = 'Invite';
});
}
}
onChildChanged is a stream, when I work with streams I often use StreamBuilder as it makes things straight forward.
So you could use a StreamBuilder with something like this:
Widget build(BuildContext build) {
return StreamBuilder(
stream: fb.child('friends/$id/friends/$uid').onChildChanged,
builder: (context, snapshot) {
return Text(snapshot.data == null ? 'Loading' : snapshot.data.snapshot.value ? 'Message' : 'Invite');
},
);
}
Every time a new value arrives for the stream the builder method is called and therefore the text content edited.

Firestore get value of Field.increment after update without reading the document data

Is there a way to retrieve the updated value of a document field updated using firestore.FieldValue.increment without asking for the document?
var countersRef = db.collection('system').doc('counters');
await countersRef.update({
nextOrderCode: firebase.firestore.FieldValue.increment(1)
});
// Get the updated nextOrderCode without asking for the document data?
This is not cost related, but for reliability. For example if I want to create a code that increases for each order, there is no guaranty that if >= 2 orders happen at the same time, will have different codes if I read the incremental value right after the doc update resolves, because if >= 2 writes happen before the first read, then at least 2 docs will have the same code even if the nextOrderCode will have proper advance increment.
Update
Possible now, check other answer.
It's not possible. You will have to read the document after the update if you want to know the value.
If you need to control the value of the number to prevent it from being invalid, you will have to use a transaction instead to make sure that the increment will not write an invalid value. FieldValue.increment() would not be a good choice for this case.
We can do it by using Firestore Transactions, like incremental worked before Field.increment feature:
try {
const orderCodesRef = admin.firestore().doc('system/counters/order/codes');
let orderCode = null;
await admin.firestore().runTransaction(async transaction => {
const orderCodesDoc = await transaction.get(orderCodesRef);
if(!orderCodesDoc.exists) {
throw { reason: 'no-order-codes-doc' };
}
let { next } = orderCodesDoc.data();
orderCode = next++;
transaction.update(orderCodesRef, { next });
});
if(orderCode !== null) {
newOrder.code = orderCode;
const orderRef = await admin.firestore().collection('orders').add(newOrder);
return success({ orderId: orderRef.id });
} else {
return fail('no-order-code-result');
}
} catch(error) {
console.error('commitOrder::ERROR', error);
throw errors.CantWriteDatabase({ error });
}
Had the same question and looks like Firestore Python client
doc_ref.update() returns WriteResult that has transform_results attribute with the updated field value

Is there a way to prevent having to await an async method returning a stream?

We currently have a method that returns a Future<Stream<Position>> just because internally we have to await the result of a method returning a Future before we can call another method that returns the Stream<Position> which we are actually interested in. Here is the code:
Future<Stream<Position>> getPositionStream(
[LocationOptions locationOptions = const LocationOptions()]) async {
PermissionStatus permission = await _getLocationPermission();
if (permission == PermissionStatus.granted) {
if (_onPositionChanged == null) {
_onPositionChanged = _eventChannel
.receiveBroadcastStream(
Codec.encodeLocationOptions(locationOptions))
.map<Position>(
(element) => Position._fromMap(element.cast<String, double>()));
}
return _onPositionChanged;
} else {
_handleInvalidPermissions(permission);
}
return null;
}
So what happens here is:
We await the _getLocationPermission() method so that we can test if the user grants us permission to access to the location services on their device (Android or iOS);
If the user grants us permission we return a Stream<Position> which will update every time the device registers a location change.
I have the feeling we can also handle this without doing an await and returning a Future. Something along the lines of:
Manually create and return an instance of the Stream<Position> class;
Handle the logic of checking the permissions and calling the _eventChannel.receiveBroadcastStream in the then() method of the Future<PermissionStatus> returned from the _getLocationPermission() method (so we don't have to await it);
Copy the events send on the stream from the _eventChannel.receiveBroadcastStream onto the earlier created (and returned) stream.
Somehow this seems to be possible, but also includes some overhead in managing the stream and make sure it closes and is cleaned up correctly during the live cycle of the plugin or when the user unsubscribes pass through the events to the _eventChannel etc.
So I guess the question would be, what would be the best way to approach this situation?
You can write the code as an async* function, which will return a Stream and still allows await in the body:
Stream<Position> getPositionStream(
[LocationOptions locationOptions = const LocationOptions()]) async* {
PermissionStatus permission = await _getLocationPermission();
if (permission == PermissionStatus.granted) {
if (_onPositionChanged == null) {
_onPositionChanged = _eventChannel
.receiveBroadcastStream(
Codec.encodeLocationOptions(locationOptions))
.map<Position>(
(element) => Position._fromMap(element.cast<String, double>()));
}
yield* _onPositionChanged;
} else {
_handleInvalidPermissions(permission);
}
}
Alternatively, if you are using a non-async function, you can also use StreamCompleter from package:async.
It allows you to return a Stream now, even if you only get the real stream later. When that happens, you "complete" the StreamCompleter with the real stream, and the original stream will behave as if it was the real stream.

Resources