Reputation: 530
I have 2 instances of my app running on 2 devices.
Both devices are listening to the same DB node changes using onChildChanged
method.
When new child node is added to the listened node the onChildChanged
event only triggered on the same device that did this new node adding.
On the second device the onChildChanged
event is not triggered for some reason.
But if I use onChildAdded
method to be listened then this event onChildAdded
is properly triggered on both devices when the node which changes we are listening gets updated by newly added child node.
I'm OK to accept that onChildChanged
only triggered when any of descendant nodes actually get changed (not new child node added) on all devices. But why onChildChanged
is only triggered on that device which performed new node adding?
So in short the question is:
Why when on device A new child node added into DB the onChildAdded
triggered on both A and B devices and onChildChanged
only triggered on device A (and not on B)?
Related docs: https://firebase.google.com/docs/database/admin/retrieve-data https://firebase.google.com/docs/reference/android/com/google/firebase/database/ChildEventListener
Update:
I discovered that ServerValue.timestamp causes this behavior. Without ServerValue.timestamp when we add new child node and listening for parent node changes the onChildChanged event has never triggered. Only if we use ServerValue.timestamp as one of the fields of newly added child node the onChildChanged event happens BUT only on the device which added this new child node with server timestamp value. I understand it most likely triggered because first the new child node is created with timestamp field placeholder and then this node is updated by server when real timestamp is written into required field. So now the question is: why not all devices with this application instances get the onChildChanged event in case we use ServerValue.timestamp?
class _MyHomePageState extends State<MyHomePage> {
final String dbDataPath = 'root/chatroom0001/messages';
DatabaseReference dbRef = FirebaseDatabase.instance.reference ();
StreamSubscription <Event> _messagesChangedSubscription;
List <String> _lMessages = List <String> ();
Future <List <String>> fetchMessages (String dbDataPath) async {
List <String> lMessages = List <String> ();
final DataSnapshot dataMessages = await dbRef.child (dbDataPath).once ();
dataMessages.value.forEach ((k, v) => {
lMessages.add (v ['message'])
});
return lMessages;
}
@override
void initState () {
super.initState ();
DatabaseReference messagesRef = dbRef.child (dbDataPath);
fetchMessages (dbDataPath).then ((lMessages) {
_lMessages = lMessages;
setState (() {});
});
_messagesChangedSubscription = dbRef.child (dbDataPath).onChildChanged.listen ((event) {
fetchMessages (dbDataPath).then ((lMessages) {
_lMessages = lMessages;
setState (() {});
});
});
}
@override
void dispose () {
super.dispose ();
_messagesChangedSubscription.cancel ();
}
@override
Widget build (BuildContext context) {
return Scaffold (
body: Container (
color: Colors.red, child: ListView.builder (
itemCount: _lMessages.length,
itemBuilder: (context, i) {
return Container (padding: EdgeInsets.all (5), child: Text (_lMessages [i], style: TextStyle (fontWeight: FontWeight.bold, fontSize: 20.0)));
}
)
),
floatingActionButton: FloatingActionButton (
onPressed: () {
DatabaseReference messagesRef = dbRef.child (dbDataPath);
DatabaseReference newChatMessageRef = messagesRef.push ();
newChatMessageRef.set ({
'message': 'Hello Google!',
'date': ServerValue.timestamp
});
},
),
);
}
}
Here is test project source code: https://drive.google.com/file/d/1j3eHbD_3UxOhWWmZRfSoKqa-r9dyEwbV/view?usp=sharing
Upvotes: 1
Views: 1071
Reputation: 599876
This code:
DatabaseReference newChatMessageRef = messagesRef.push ();
newChatMessageRef.set ({
'message': 'Hello Google!',
'date': ServerValue.timestamp
});
Will lead to two write events on the client that makes the change:
Remote clients listening to this same node, will only see the second event. That can either be a onValue
event, or an onChildAdded
or onChildChanged
event, depending on the type of listener used and the local state.
So on the client making the change: if you use a onValue
listener, you'll see two value events, while if you use onChild
listeners on the parent node, you'll see an onChildAdded
for the local event, and then a onChildChanged
for the final value.
Upvotes: 2