Flutter: How to Queue multiple streams - flutter

I have stream like the below-mentioned code, here want to merge streams and wanted to call it one by one whenever it required.
main() {
Stream<String> stream = new Stream.fromFuture(getData());
stream.listen((data) {
print("DataReceived: "+data);
}, onDone: () {
print("Task Done");
});
}
Future<String> getData() async {
await Future.delayed(Duration(seconds: 5)); //Mock delay
print("Fetched Data");
return "This a test data";
}
onDone will be called after 5 seconds.

For it, StreamQueue can be used to combine multiple streams
Add Dependency:
async: ^2.4.1
Use StreamQueue:
void main() async {
Stream<String> stream1 = new Stream.fromFuture(getData(2));
Stream<String> stream2 = new Stream.fromFuture(getData(4));
Stream<String> stream3 = new Stream.fromFuture(getData(6));
var streams = StreamGroup.merge([stream1, stream2, stream3]);
var data = StreamQueue(streams);
var first = await data.next;
print(first);
var second = await data.next;
print(second);
var third = await data.next;
print(third);
var third1 = await data.next;
print(third1);
}
Future<String> getData(int duration) async {
await Future.delayed(Duration(seconds: duration)); //Mock delay
return "This a test data for duration $duration";
}
Output:
I/flutter ( 7950): This a test data for duration 2
I/flutter ( 7950): This a test data for duration 4
I/flutter ( 7950): This a test data for duration 6
data.next will provide you the first stream in Queue which is in our
case is stream1, here we can take the future value and use. stream.next gives next stream in the queue if nothing exists in queue & stream.next is performed then it will throw an exception.

Related

Flutter - an async function returns before really finishing?

I have a function scanAndConnect() that should scan for BLE devices and connect to the device with the specified service ID. This function should be async and should return Future.
The problem is that scanAndConnect() prints 99999 and returns without waiting for flutterReactiveBle.statusStream.listen() to finish although I use await before it.
Future scanAndConnect(Uuid serviceId, Uuid charctId) async {
StreamSubscription<BleStatus>? bleStatusStreamSubscription;
StreamSubscription<DiscoveredDevice>? deviceStreamSubscription;
Stream<DiscoveredDevice> stream;
bleStatusStreamSubscription =
await flutterReactiveBle.statusStream.listen((bleStatus) async {
print("new listen ${bleStatus.toString()}");
if (bleStatus == BleStatus.ready) {
await bleStatusStreamSubscription!.cancel();
connectionStatus = BLEConnectionStatus.Connecting;
stream = await flutterReactiveBle.scanForDevices(
withServices: [serviceId],
scanMode: ScanMode.lowLatency,
);
}
});
print("9999999");
}
....
Future connectToDevice() async {
await ble.scanAndConnect(BLE_SERVICE_UUID, BLE_CHAR_UUID)
print("Statement after await in main");
setState(() {
loading = false;
print("Changing state to ${loading.toString()}");
});
}
This is the output I get in Xcode:
flutter: 9999999
flutter: Statement after await in main
flutter: Changing state to false
flutter: new listen BleStatus.unknown
flutter: new listen BleStatus.ready
How can I make scanAndConnect doesn't return before really finishing?
According to the documentation, FlutterReactiveBle.scanForDevices() returns a Stream, not a Future, so await will not work here. You can use
await for
listen()
await stream.first()
to wait for data from a Stream.

Exit an infinite stream

I've written this piece of code in order to scan Bluetooth devices in my Flutter app with the help of flutter_blue package.
Stream<List<ScanResult>> scannedDevices(int durationSeconds) async* {
Stream<List<ScanResult>> stream = _flutterBlue.scanResults;
List<ScanResult> payload = List<ScanResult>.empty(growable: true);
_flutterBlue.startScan(timeout: Duration(seconds: durationSeconds));
await for (List<ScanResult> results in stream) {
payload.clear();
for (ScanResult result in results) {
payload.add(result);
}
yield payload;
}
The problem is that scanResults Stream is infitnite (see here) and because of that the await for loop runs foreverer, leaving my scannedDevices Stream running forever too.
Is there a way to break the await for loop after scanDuration has passed? Thanks
Simply add an timeout to the stream that you are listening.
Another option is to use the StreamTransformer instead of what you did with await for.
Stream<List<ScanResult>> scannedDevices(int durationSeconds) async* {
Stream<List<ScanResult>> stream = _flutterBlue.scanResults.timeout(Duration(seconds: durationSeconds));
List<ScanResult> payload = List<ScanResult>.empty(growable: true);
_flutterBlue.startScan(timeout: Duration(seconds: durationSeconds));
await for (List<ScanResult> results in stream) {
payload.clear();
for (ScanResult result in results) {
payload.add(result);
}
yield payload;
}
}
Yes, you can simply return null for breaking loop
Stream<List<ScanResult>> scannedDevices(int durationSeconds)
async* {
Stream<List<ScanResult>> stream = _flutterBlue.scanResults;
List<ScanResult> payload = List<ScanResult>.empty(growable:
true);
_flutterBlue.startScan(timeout: Duration(seconds:
durationSeconds));
await for (List<ScanResult> results in stream) {
payload.clear();
for (ScanResult result in results) {
payload.add(result);
}
return payload;
}

Out of memory, flutter stream

the below stream method is triggered every frame
stream() async {
_isStreaming=true;
var counter=0;
channel = WebSocketChannel.connect(
Uri.parse('ws://192.168.11.8:8000/real-time/')
) as IOWebSocketChannel?;
_cameraController?.startImageStream((CameraImage img) async{
final imageBytes= await convertYUV420toImageColor(img);
if(_isStreaming) {
channel?.sink.add(imageBytes);
}
});
channel?.stream.listen((message) {
print(message);
var jsonDecodex = jsonDecode(message);
var prediction = jsonDecodex['prediction'];
var predictProba = jsonDecodex['predict_proba'];
_prediction = prediction;
_accuracy =predictProba+"%";
if(mounted) {
setState(() {});
}
},
);}
I'm working on a flutter app that opens a stream and sends every frame to a Django server via WebSockets, However, nearly after 1 minute of streaming 'out of memory' error happens.

Is there a way to get notified when a dart stream gets its first result?

I currently have an async function that does the following:
Initializes the stream
Call stream.listen() and provide a function to listen to the stream.
await for the stream to get its first result.
The following is some pseudo code of my function:
Future<void> initStream() async {
// initialize stream
var stream = getStream();
// listen
stream.listen((result) {
// do some stuff here
});
// await until first result
await stream.first; // gives warning
}
Unfortunately it seems that calling stream.first counts as listening to the stream, and streams are not allowed to be listened by multiple...listeners?
I tried a different approach by using await Future.doWhile()
Something like the following:
bool gotFirstResult = false;
Future<void> initStream() async {
var stream = getStream();
stream.listen((result) {
// do some stuff here
gotFirstResult = true;
});
await Future.doWhile(() => !gotFirstResult);
}
This didn't work for me, and I still don't know why. Future.doWhile() was successfully called, but then the function provided to stream.listen() was never called in this case.
Is there a way to wait for the first result of a stream?
(I'm sorry if I didn't describe my question well enough. I'll definitely add other details if needed.)
Thanks in advance!
One way is converting your stream to broadcast one:
var stream = getStream().asBroadcastStream();
stream.listen((result) {
// do some stuff here
});
await stream.first;
Another way, without creating new stream, is to use Completer. It allows you to return a Future which you can complete (send value) later. Caller will be able to await this Future as usual.
Simple example:
Future<int> getValueAsync() {
var completer = Completer<int>();
Future.delayed(Duration(seconds: 1))
.then((_) {
completer.complete(42);
});
return completer.future;
}
is equivalent of
Future<int> getValueAsync() async {
await Future.delayed(Duration(seconds: 1));
return 42;
}
In your case:
Future<void> initStream() {
var stream = getStream();
var firstValueReceived = Completer<void>();
stream.listen((val) {
if (!firstValueReceived.isCompleted) {
firstValueReceived.complete();
}
// do some stuff here
});
return firstValueReceived.future;
}

Flutter: Combine Multiple Future<T> Tasks

How we can merge multiple FutureTask so that we can get a callback for all at the same response.
We use Future<T> like
Future<String> getData(int duration) async {
await Future.delayed(Duration(seconds: duration)); //Mock delay
return "This a test data for duration $duration";
}
Call above method like getData(2).then((value) => print(value));
If we want to call multiple Future Task, then how can we do that?
To execute all futures concurrently, use Future.wait This takes a list of futures and returns a future of lists:
Suppose you have these futures.
class CovidAPI {
Future<int> getCases() => Future.value(1000);
Future<int> getRecovered() => Future.value(100);
Future<int> getDeaths() => Future.value(10);
}
You can get all the futures together using Future.wait([list of futures])
final api = CovidAPI();
final values = await Future.wait([
api.getCases(),
api.getRecovered(),
api.getDeaths(),
]);
print(values); // [1000, 100, 10]
This is ideal when the futures are independent, and they don't need to execute sequentially.
Source : https://codewithandrea.com/videos/top-dart-tips-and-tricks-for-flutter-devs/
For it, FutureGroup can be used to combine multiple streams
FutureGroup provides us the functionality of combining multiple
futures into one single group, which will give the callback at the end
when all future tasks work gets completed.
Dependency:
dependencies:
async: ^2.4.1
How to implement FutureGroup?
FutureGroup futureGroup = FutureGroup();
futureGroup.add(future1);
futureGroup.add(future2);
futureGroup.add(future3);
Use:
void main() {
Future<String> future1 = getData(2);
Future<String> future2 = getData(4);
Future<String> future3 = getData(6);
FutureGroup futureGroup = FutureGroup();
futureGroup.add(future1);
futureGroup.add(future2);
futureGroup.add(future3);
futureGroup.close();
futureGroup.future.then((value) => {print(value)});
}
Future<String> getData(int duration) async {
await Future.delayed(Duration(seconds: duration)); //Mock delay
return "This a test data";
}
Output:
I/flutter ( 5866): [This a test data, This a test data, This a test
data] // Called after 6 seconds.
Note: This will be called only once when all the Future Task gets completed, here it will run after 6 seconds.