Describe the bug
I am trying to make something similar to flutter_wtih_streams. I have made my own stream connected to a gRPC server and I want to be able to show the changes in data in realtime on the UI, the problem is that even though when I print the values in console, it shows the change but the same is not reflected in the UI.
Code sample
main.dart
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:ventilator_software_flutter/monitor_bloc/monitor_bloc.dart';
import 'package:ventilator_software_flutter/grpc_client/client.dart';
import 'package:ventilator_software_flutter/models/DataModel.dart';
import 'package:ventilator_software_flutter/screens/chart.dart';
void main() {
EquatableConfig.stringify = kDebugMode;
runApp(VentMonitorApp());
}
class VentMonitorApp extends MaterialApp {
VentMonitorApp({Key key})
: super(
key: key,
home: BlocProvider(
create: (_) => MonitorBloc(Client('10.0.2.2', 8080)),
child: MainPage(),
),
);
}
class MainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Vent Monitor'),
),
body: BlocBuilder<MonitorBloc, MonitorState>(
builder: (context, state) {
if (state is MonitorDataPresent) {
// var toPlot = convertPoints(state.ventDataPoints);
// return SeriesTimeChart(toPlot);
return Center(
child: Text("${state.ventDataPoints.length}"),
);
}
return const Center(
child: Text('The ventilator is offline Press button to start receiving data'),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.bloc<MonitorBloc>().add(MonitorStarted()),
tooltip: 'Start',
child: const Icon(Icons.timer),
),
);
}
}
The stream
Stream<VentData> runMonitor() async* {
final monitorRequest = MonitorRequest()..id = this.ventilatorId;
print("Executing stream listening");
await for (var dataPoint in this.stub.monitor(monitorRequest)) {
print("executing this thing");
ParamConfig data = dataPoint.status;
yield new VentData(
tidalVolume: data.tidalVolume,
respiratoryRate: data.respiratoryRate,
modeOfVentilation: data.modeOfVentilation.toString(),
fio2: data.fio2,
inspiratoryTime: data.inspiratoryTime,
peep: data.peep,
timestamp: dataPoint.timestamp
);
}
}
The monitor_bloc.dart
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:ventilator_software_flutter/models/DataModel.dart';
import 'package:ventilator_software_flutter/grpc_client/client.dart';
part 'monitor_event.dart';
part 'monitor_state.dart';
class MonitorBloc extends Bloc<MonitorEvent, MonitorState> {
MonitorBloc(this._grpcClient) : super(MonitorInitial());
final Client _grpcClient;
StreamSubscription _subscription;
@override
Stream<MonitorState> mapEventToState(
MonitorEvent event,
) async* {
if (event is MonitorStarted) {
await _subscription?.cancel();
_subscription = _grpcClient
.runMonitor()
.listen((dataPoint) => add(_NewVentData(dataPoint)));
}
if (event is _NewVentData) {
final currentState = state;
List<VentData> ventDataPoints = [];
if (currentState is MonitorDataPresent) {
ventDataPoints = currentState.ventDataPoints;
}
print(event.dataPoint.peep);
ventDataPoints.add(event.dataPoint);
yield MonitorDataPresent(ventDataPoints);
}
}
@override
Future<void> close() {
print("calling close");
_subscription?.cancel();
return super.close();
}
}
When I print the data using print(event.dataPoint.peep);, I get the following lines in the console:
I/flutter ( 7928): 48
I/flutter ( 7928): executing this thing
I/flutter ( 7928): 35
I/flutter ( 7928): executing this thing
I/flutter ( 7928): 11
I/flutter ( 7928): executing this thing
I/flutter ( 7928): 37
I/flutter ( 7928): executing this thing
I/flutter ( 7928): 26
I/flutter ( 7928): executing this thing
I/flutter ( 7928): 41
I/flutter ( 7928): executing this thing
I/flutter ( 7928): 8
But the UI is constant on length 1. Earlier versions used to have a BlocDelegate which we could override to easily debug, I do not know how to do that anymore..... Please help.
If anyone wants to have a look at the full code, they can go and see https://github.com/amartya-dev/ventilator_software_flutter
Hi @amartya-dev 馃憢
Thanks for opening an issue!
I believe the issue is you are mutating the state rather than emitted a new instance of the state each time.
If you change
if (currentState is MonitorDataPresent) {
ventDataPoints = currentState.ventDataPoints;
}
to
if (currentState is MonitorDataPresent) {
ventDataPoints = List.of(currentState.ventDataPoints);
}
I believe that should solve the issue. Let me know if that helps 馃憤
Please refer to the FAQs for more information.
Closing for now but if you have additional questions let me know and I'm happy to continue the conversation 馃槃
Yes you are right, I was resetting the state every time and basically returning an empty list there, I changed the code to actually copy all the points and then add a new one, you think there can be optimization on that?
List<VentData> ventDataPoints = [];
if (currentState is MonitorDataPresent) {
if (currentState.ventDataPoints.length < 20) {
ventDataPoints.addAll(currentState.ventDataPoints);
} else {
ventDataPoints.addAll(currentState.ventDataPoints
.sublist(1, currentState.ventDataPoints.length));
}
}
ventDataPoints.add(event.dataPoint);
yield MonitorDataPresent(ventDataPoints);
Also, I have tried to maintain the length at 20, if you think there is a better way to do this, please let me know :) Thank you very much for your help
No problem!
You should be able to do something like:
if (currentState is MonitorDataPresent) {
yield MonitorDataPresent(
currentState.ventDataPoints.length < 20
? List.of(currentState.ventDataPoints)
: currentState.ventDataPoints.take(20)
);
return;
}
yield MonitorDataPresent([event.dataPoint]);
Hope that helps 馃憤
Yeah I get your point, the thing is I wanted to be able to add the datapoint to the current list and remove the first one, though it is fine, I get the thing. Thank you very much for your help :)