0

I'm trying to upload a list of attachments and update the status of each element as they are iterated through. If the upload was completed successfully, we emit uploadSucceded = true, else false.

Future<void> uploadAttachments() async {
    int uploadableAttachmentIndex = 0;
    emit(state.copyWith(isUploadingAttachments: true));
    final attachments = generateFormAttachmentList();

    await Future.forEach(
      attachments,
      (UploadableAttachment attachment) async {
        final success = await uploadFile(attachment);
        if (success) {
          debugPrint('SUCCESS!!!!!');
          emit(state.copyWith(
            uploadableAttachments: state.uploadableAttachments!
              ..[uploadableAttachmentIndex].uploadSucceded = true,
          ));
        } else {
          debugPrint('FAILED!!!');
          emit(state.copyWith(
              uploadableAttachments: state.uploadableAttachments!
                ..[uploadableAttachmentIndex].uploadSucceded = false,
              isUpdatingTask: false,
              taskUpdateError: 'Error uploading attachment',
              attachmentUploadsFailed: true));
        }
        uploadableAttachmentIndex++;
      }
    );
    emit(state.copyWith(isUploadingAttachments: false));
  }

While the values get set, a rebuild isnt triggered until we hit the emit at the end of the function, causing the status of the uploads to be re-built all at once. I'm not sure why the rebuild inside the forEach isnt triggering, but I think the problem is with this function. Here is what the bloc builder looks like:

class _UploadAttachmentsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<TaskDetailsCubit, TaskDetailsState>(builder: (context, state) {
      return Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          state.attachmentUploadsFailed
              ? _UploadErrorWidget()
              : _UploadPageHeader(state.isUploadingAttachments),
          SizedBox(height: 40),
          Expanded(
            child: ListView.builder(
              scrollDirection: Axis.vertical,
              shrinkWrap: true,
              itemCount: state.uploadableAttachments!.length,
              itemBuilder: (context, index) {
                return _UploadProgressListItem(file: state.uploadableAttachments![index]);
              },
            ),
          ),
        ],
      );
    });
  }
}

The UploadProgressListItem should be rebuilding when uploadSucceded gets set.

class _UploadProgressListItem extends StatelessWidget {
  final UploadableAttachment file;

  _UploadProgressListItem({required this.file});

  Widget getListTileIcon() {
    if (file.uploadSucceded == true)
      return Icon(Icons.check, color: CoreTheme.green);
    else if (file.uploadSucceded == false)
      return Icon(Icons.error, color: CoreTheme.error);
    else
      return CircularProgressIndicator();
  }

  Widget getSubtitle() {
    if (file.uploadSucceded == true)
      return Text(
        'Complete',
        style: CoreTheme.textStyleLabel(color: FontColor.green),
      );
    else if (file.uploadSucceded == false)
      return Text(
        'Problem Uploading',
        style: CoreTheme.textStyleLabel(color: FontColor.red),
      );
    else
      return Text('Uploading', style: CoreTheme.textStyleLabel());
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(14.0),
      child: ListTile(
        leading: getListTileIcon(),
        title: Text(
          file.filename,
          overflow: TextOverflow.ellipsis,
          style: CoreTheme.textStyleBodyRegular(),
        ),
        subtitle: getSubtitle(),
      ),
    );
  }
}

class UploadableAttachment {
  bool? uploadSucceded;
  final File file;
  final String filename;
  final String fieldKey;

  UploadableAttachment({
    this.uploadSucceded,
    required this.file,
    required this.filename,
    required this.fieldKey,
  });
}

I'm wondering if Im taking the wrong asynchronous route. Should I be using a stream instead of a list? Should I be using a BlocListener or BlocConsumer instead of BlocBuilder?

1 Answers1

0

You can add a copy with the method in UploadableAttachment class.

final _newList = state.uploadableAttachments.[ 
uploadableAttachmentIndex] = state.uploadableAttachments. 
[uploadableAttachmentIndex].copWith(uploadSucceded: true);

After firstly checking the index what is the upload value then you should be added a parameter, for instance, is loading because if you add some item in the list and it'll be immediately updated (reference added automaticity) value after cubit or bloc doesn't show new data. Finally, I generally added is loading parameter for this problem.

emit(state.copyWith(
 uploadableAttachments: _newList,
 isUpload:true
));
vb10
  • 641
  • 5
  • 14