skip to Main Content

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


  1. 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.

    Login or Signup to reply.
  2. 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;`
        });
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search