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?