Reputation: 407
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')
Upvotes: 5
Views: 1671
Reputation: 5595
That StreamController
you're creating in the Chat
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
class Chat extends Bloc<ChatEvent, ChatState> {
Chat(this.chatUseCases, this.chat) : super(const SingleChatState()) {
on<ChatEmitUploadList>(_emitUploadList);
on<ChatInitStreamListener>(_onChatInitStreamListener); // adding this event
add(ChatInitStreamListener()); // call this event from constructor
}
FutureOr<void> _onChatInitStreamListener(event, Emitter<ChatState> emit) {
final fileStream = // stream from the Dio method
emit.forEach(fileStream, onData: (FileDetails fileDetails) {
// handle each stream update here and return a new ChatState
// if no changes in state are required then `return state;`
});
}
}
Upvotes: 3
Reputation: 56
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.
Upvotes: 1