Im using StreamController
to track the progress of uploading an image using Dio
, so when i sink a value in my stream i trigger an event to emit new state to rebuild my widget, the problem is it does not emit new state.
This is the code from my ChatBloc
class Chat extends Bloc<ChatEvent, ChatState> {
final StreamController<FileDetails> uploadStream =
StreamController<FileDetails>();
Chat(this.chatUseCases, this.chat)
: super(const SingleChatState()) {
on<ChatEmitUploadList>(_emitUploadList);
/// listen to changes form upload stream
uploadStream.stream.listen((event) {
List<FileDetails> details = state.fileDetailsList;
details
.firstWhereOrNull((element) => element.id == event.id)!
.percentage = event.percentage;
add(SingleChatEvent.emitUploadList(fileDetails: event));
});
}
}
FutureOr<void> _emitUploadList(
ChatEmitUploadList event, Emitter<SingleChatState> emit) {
print('emit once');
List<FileDetails> details = List.from(state.fileDetailsList);
details
.firstWhereOrNull((element) => element.id == event.fileDetails.id)!
.percentage = event.fileDetails.percentage;
emit(state.copyWith(fileDetailsList: details));
}
This is my BlocBuilder
class UploadingWidget extends StatelessWidget {
const UploadingWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocBuilder<ChatBloc, ChatState>(
builder: (context, state) {
if (state.status == SingleChatStatus.uploadingFiles) {
return Container(
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
margin: const EdgeInsets.all(8),
padding: const EdgeInsets.all(8),
child: CustomScrollView(
shrinkWrap: true,
slivers: [
SliverGrid.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 100),
itemBuilder: (context, index) => Material(
child: SizedBox(
height: 100,
width: 100,
child: Center(
child: Text(
state.fileDetailsList[index].percentage.toString()),
),
),
),
itemCount: state.fileDetailsList.length,
)
],
),
);
}
return const SizedBox.shrink();
},
);
}
}
This is my method in Dio
final response = await dio.post(BASE_TEST+sendMessageEndPoint,data: data,
onSendProgress: (count, total) {
if(file!=null){
String fileName = file.path.toString().split('/').last;
double percentage = (count / total * 100).toDouble();
uploadStream.sink.add(FileDetails(id: file.path!, percentage: percentage,file: file));
}
},);
This is my debug console
[log] onEvent -- ChatBloc, ChatEvent.emitUploadList(fileDetails: Instance of 'FileDetails')
[log] onEvent -- ChatBloc, ChatEvent.emitUploadList(fileDetails: Instance of 'FileDetails')
[log] onEvent -- ChatBloc, ChatEvent.emitUploadList(fileDetails: Instance of 'FileDetails')
[log] onEvent -- ChatBloc, ChatEvent.emitUploadList(fileDetails: Instance of 'FileDetails')
[log] onEvent -- ChatBloc, ChatEvent.emitUploadList(fileDetails: Instance of 'FileDetails')
2
Answers
I think the problem i when you add the event add(SingleChatEvent.emitUploadList(fileDetails: event)).on the construct of chat there is not register of a method which implements SingleChatEvent. , there should have a method in the chat construct that implements the SingleChatEvent event.
That
StreamController
you’re creating in theChat
class is not the same instance as the one you’re adding to in the Dio method. You’re creating a stream, then listening to it, but never adding to that actual stream.You need to listen to the stream that the Dio is actually updating.
Below is an incomplete example but should get you going in the right direction. The preferred way to listen to a Stream in bloc is to use
emit.forEach
then return a new state on each Stream update.This requires that your Dio method returns a Stream or if there is a repository class in between, then have that return a Stream, that then gets passed into the stream argument of
emit.forEach