Stream is not updating when push notification is come in flutter - flutter

My stream is not updating when I call the api while I get the push notification.
This is a stream and I declare like this,
var itemController = BehaviorSubject();
Stream get itemStream => itemController.stream;
I add data in stream using sink.add like,
modelData.itemController.sink.add(modelData);
I use stream like,
StreamBuilder(stream: item.itemStream,builder: (context, snapshot) {});
When I call api at that time these stream is not update and I am not getting any updated data from stream.

Related

What is the RiverPod way to Consume a StreamProvider outside of Flutter - Dart only

Working with RiverPod for first time and the Provider concept in general.
Goal: Validate the signed in user has an account info document in my accounts FireStore collection.
My code is based off this flutter/firebase starter architecture referenced on the RiverPod site as an example of how to use RiverPod for state management.
I'm using latest flutter_riverpod package.
My minimized code:
firestore_database.dart - implementing model/service layers away from ui.
Stream<Account> accountStream() => _service.documentStream(
path: FirestorePath.account(uid),
builder: (data, documentId) => Account.fromMap(data, uid),
);
account_setup_service.dart
final accountStreamProvider = StreamProvider.autoDispose((ref) {
final database = ref.watch(databaseProvider);
return database != null ? database.accountStream() : const Stream.empty();
});
This is the part I'm stuck on. Everything else checks out. How do I consume the accountStreamProvider outside of Flutter in Dart only context? Again, my goal is to simply evaluate whether the stream is empty or if it contains a document.
Consumer(builder: (context, watch, _) {
final accountAsyncValue = watch(accountStreamProvider);
return accountAsyncValue.when(
// what do I do here to validate that there is an account
// info document for the currently authorized user
// using the accountStreamProvider?
// I don't want to return a widget...
data: (account) => null,
loading: () => null,
error: (_, __) => null,
);
});
On the RiverPod doc site I've read the "Reading a provider outside of providers using Dart only" reference, however, I'm not sure where to go with it to consume the stream and evaluate data if there is any.
The documentation link you posted shows how to get a StateController from a ProviderContainer.
It looks like the StateController class has a stream property, so you could listen to that:
https://pub.dev/documentation/riverpod/latest/riverpod/StateController-class.html
(I haven't tried that, but that's what I can make out from the docs)

Unable to create a broadcast stream in flutter

Note: Please read edit first.
I want to create a broadcast stream for providing to Stream Builder. And I also have to attach websocket url to it. Using StreamController docs, this is what i've implemented. I have read similar questions but couldn't figure out the problem.
final WebSocketChannel channel = IOWebSocketChannel.connect("ws://192.168.225.220:6969/test");
Class _Sample extends state<Sample>{
#override
void initState() {
super.initState();
mystream = channel.stream;
controller = StreamController<dynamic>.broadcast();
controller.addStream(mystream);
subscription = controller.stream.listen((data) => data, onDone: () => print("Task done") , onError: (error) => error);
}
///widget tree
child: StreamBuilder(
stream: mystream,
builder: (context, snapshot) {
if(snapshot.hasData && !snapshot.hasError){
return Text(snapshot.hasData.toString());
}
return Text("No Data");
}
),
///widget tree
}
This is the error I'm getting
Bad state: Stream has already been listened to.
EDIT:
I realized after #andras pointed out I'm listening to the stream multiple times. I have read the documentation many times and viewed implementations for Stream and Streambuilder but still I am not able to comprehend Streams. For my implementation, I want to send data from a client to server. And multiple clients should be able to listen to this data.Hence, I want to create a broadcast stream so that multiple listeners can listen to a broadcasting client on a websocket channel.
So far this is what I have understood,
I create a websocket channel, this channel provides a single subscription stream where I have provided a url for server communication.
final WebSocketChannel channel = IOWebSocketChannel.connect("ws://192.168.225.220:6969/test");
Now to create a broadcast stream there is a StreamController.broadcast constructor and to attach a source there is addStream() method
void initState(){
controller = StreamController<dynamic>.broadcast();
controller.addStream(channel.stream);
subscription = controller.stream.listen((data) => data, onDone: () => print("Task done") , onError: (error) => error);
}
So I'm listening on controller.stream, now I want to attach this stream to Streambuilder so I can update builder whenever there is data from stream.
I'm echoing the data to same client using
controller.sink.add(data)
Error i get when i try to use the stream.
The following StateError was thrown while handling a gesture:
Bad state: Cannot add new events while doing an addStream
I'm not sure what i'm doing wrong.
The error description seems pretty detailed.
You listen to the stream once in the initState and then StreamBuilder does also listen to the stream you pass into it.
Think about your code a bit and see if broadcast methods fits for your use case if you want to stick to multiple listeners.
Documentation says 'Events must not be added directly to this controller using add, addError, close or addStream, until the returned future is complete.'
https://api.dart.dev/stable/2.12.4/dart-async/StreamController/addStream.html
I think you should use listen to web sockets stream and add events manually instead of using addStream()

Difference between listening of stream by StreamBuilder and regular listen method

I'm using http library in order to download an image.
final client = http.Client();
final _response = await client.send(http.Request('GET', Uri.parse("my_url")));
Listening way 1: (listen method)
int downloaded = 0;
_response.stream.listen((value) {
// this gets called 82 times and I receive actual image size
downloaded += value.length;
});
Listening way 2: (StreamBuilder widget)
int downloaded = 0;
StreamBuilder<List<int>>(
stream: _response.stream,
builder: (_, snapshot) {
// this gets called 11 times and I receive around 1/10 actual image size
if (snapshot.hasData) downloaded += snapshot.data.length;
return Container();
},
);
The question is why StreamBuilder's build() method isn't getting called that often when new data arrives, it simply defeats the purpose of being used as a widget.
StreamBuilder is basically better optimized to not rebuilt on every new snapshot. As StreamBuilder documentation states:
Widget rebuilding is scheduled by each interaction, using
State.setState, but is otherwise decoupled from the timing of the
stream. The builder is called at the discretion of the Flutter
pipeline, and will thus receive a timing-dependent sub-sequence of the
snapshots that represent the interaction with the stream.
As an example, when interacting with a stream producing the integers 0
through 9, the builder may be called with any ordered sub-sequence of
the following snapshots that includes the last one (the one with
ConnectionState.done):
new AsyncSnapshot<int>.withData(ConnectionState.waiting, null)
new AsyncSnapshot<int>.withData(ConnectionState.active, 0)
new AsyncSnapshot<int>.withData(ConnectionState.active, 1)
...
new AsyncSnapshot<int>.withData(ConnectionState.active, 9)
new AsyncSnapshot<int>.withData(ConnectionState.done, 9)
The actual sequence of invocations of the builder depends on the relative timing of events produced by the stream and the build rate of the Flutter pipeline.

Is there a way to push data to a stream without loosing it's content?

I'm trying to build an infinite scroll with a StreamBuilder on Flutter with Firestore. While I've managed to retrieve everything I needed on the initial load (let's say 10 first posts), I can't manage to add data from the next page to the initial Stream.
I've a Stream called "content", where I've put my initial fetched post but when I'm trying to add some other content to this Stream, it get's erased by the content added when I'm doing mystream.sink.add(data) How can I keep the initial Stream's content and ADD the new content to the Stream?
Here is some code:
In my view:
body: StreamBuilder(
stream: bloc.documentData, // My initial Stream in my view
In my controller / bloc (FeedController):
FeedController() {
loadInitialFeed();
}
final _documentData = StreamController<QuerySnapshot>();
Stream<QuerySnapshot> get documentData => _documentData.stream;
loadInitialFeed() async {
QuerySnapshot test = await _database.test(await _user.getSecuredUserId());
_documentData.sink.add(test);
}
In fact, what I'm looking for is simply a way to merge two Streams. I've been looking since this morning...
Thank you

Flutter Firestore, interacting with user?

Flutter: I'm using Streambuilder to listen to Firestore data which works great.
However, what I would like to do is to notify the user when certain conditions in Firestore changes, let him respond and write that back to Firestore.
Doable (in Flutter/Dart)?
Thanks in advance.
Best,
/j
StreamBuilder(
stream: Firestore.instance.collection('users').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return const Text('Loading...');
List<DocumentSnapshot> documents = snapshot.data.documents;
documents.forEach((doc) {
if (doc['Invited']) {
**// notify current user that he is invited,
// let him reply yes/no, write that value back to Firestore**
}
}
},
);
Is there a point why you dont want to use Firebase Messaging?
1) Firestore triggers a Notification
2) Your App recieve the notification
3) Your app handle the notification, it should be possible that the user dont see the notification in the status bar.
If i understand you correctly you want to update something as soon as the user receives an invitation. You can do this by using code like this:
Future<Null> updateUser{
#required String userId,
bool accepted,
}) {
assert(userId != null && userId.isNotEmpty);
// create map that contains all fields to update
Map<String, dynamic> dataToUpdate = {
"accepted": accepted,
};
// get the reference to the user you want to update
final userRef = references.users(userId);
// update the user
return userRef.updateData(dataToUpdate);
}
You can get the reference from your document by using doc.reference. The Future will complete if the update was successful or terminate with an error if something went wrong.