0

I'm going to create Photo entity in firebase store and I need to get two fields like current user's email and Photo url but my BLoC doesn't update state with asynchronously received data. Here is my code

  saved: (e) async* {
    Either<PhotoFailure, Unit> failureOrSuccess;
    final userOption = await _authFacade.getSignedInUser();
    final user = userOption.fold(() => null, (user) => user);
    print(user.emailAddress.getOrCrash());
    yield state.copyWith(
        photo:
            state.photo.copyWith(author: user.emailAddress.getOrCrash()));
    print(state.photo.author);
    yield state.copyWith(
        photo: state.photo.copyWith(
            uploadDate: DateFormat("dd-MM-yyyy").format(DateTime.now())));
    print(state.photo.uploadDate);
    FirebaseStorage _storage = FirebaseStorage.instance;
    Reference _rootReference = _storage.ref().child('photos');
    UploadTask task = _rootReference.putFile(state.photoFile);
    String downloadUrl = await (await task).ref.getDownloadURL();
    print(downloadUrl);
    yield state.copyWith(
      photo: state.photo.copyWith(url: downloadUrl),
    );
    print(state.photo.url);

    yield state.copyWith(
      isSaving: true,
      saveFailureOrSuccessOption: none(),
    );

    if (state.photo.failureOption.isNone()) {
      state.isEditing
          ? await _photoRepository.update(state.photo)
          : await _photoRepository.create(state.photo);
    }

    yield state.copyWith(
      isSaving: false,
      showErrorMessages: AutovalidateMode.always,
      saveFailureOrSuccessOption: optionOf(failureOrSuccess),
    );
  },
);

}

As you can see I've decided to print all results and Console logs this

I/flutter (19212): test@gmail.com
I/flutter (19212): 
I/flutter (19212): 2021-01-07 16:47:23.279989
D/UploadTask(19212): Increasing chunk size to 524288
I/flutter (19212): https://firebasestorage.googleapis.com/v0/b/simplefirebasegalley.appspot.com/o/photos?alt=media&token=73b3537f-d36c-49a8-b080-5cd27837c50e
I/flutter (19212):

So i'm getting correct data but i can't update state with only asynchronous data because uploadDate works correct.

My state code

@freezed
 abstract class PhotoFormState with _$PhotoFormState {
   const factory PhotoFormState({
     @required Photo photo,
     @nullable @required File photoFile,
     @required AutovalidateMode showErrorMessages,
     @required bool isEditing,
     @required bool isSaving,
     @required Option<Either<PhotoFailure, Unit>> saveFailureOrSuccessOption,
     }) = _PhotoFormState;

   factory PhotoFormState.initial() => PhotoFormState(
       photo: Photo.empty(),
       photoFile: null,
       showErrorMessages: AutovalidateMode.disabled,
       isEditing: false,
       isSaving: false,
       saveFailureOrSuccessOption: none(),
       );
 }

And here is my Photo entity class

@freezed
 abstract class Photo with _$Photo {
   const Photo._();

   const factory Photo({
     @required String url,
     @required String type,
     @required int watchCount,
     @required UniqueId id,
     @required PhotoName name,
     @required PhotoDescription description,
     @required TagList<Tag> tagList,
     @required String author,
     @required String uploadDate,
     @required FieldValue serverTimeStamp,
   }) = _Photo;

   factory Photo.empty() => Photo(
         url: '',
         type: 'new',
         id: UniqueId(),
         name: PhotoName(''),
         description: PhotoDescription(''),
         tagList: TagList(new List()),
         watchCount: 0,
         author: '',
         serverTimeStamp: FieldValue.serverTimestamp(),
         uploadDate: DateTime.now().toString(),
       );

   Option<ValueFailure<dynamic>> get failureOption {
     return name.failureOrUnit
         .andThen(description.failureOrUnit)
         .andThen(tagList.failureOrUnit)
         .fold((f) => some(f), (r) => none());
   }
 }
Ivanius
  • 109
  • 1
  • 2
  • 9
  • what do you mean with "because uploadDate works correct."?? and when is it not updating the state (in which yield)? – Jose Georges Jan 07 '21 at 14:12
  • i can update photo entity with uploadDate but yielding user's email and photo url doesn't work. I printed their value(print(state.photo.author)) but it's empty and print(user.emailAddress.getOrCrash()) works and show concrete user's email. So i think that i'm yielding empty value but i don't know why is it hapenning – Ivanius Jan 07 '21 at 15:33
  • can you share your state class and your photo class? – Jose Georges Jan 07 '21 at 16:07
  • No problem. Edited. – Ivanius Jan 07 '21 at 17:21

1 Answers1

-1

If I see this correctly, you are yielding the same state multiple times using .copyWith. However, in order to have the UI update, you need to send an intermittent state. So typically you would

yield myDisplayState(); yield inProgress(); yield myDisplayState(); yield inProgress(); yield myDisplayState();

If you have no other state in between, you will see no change on the UI

w461
  • 2,168
  • 4
  • 14
  • 40
  • This is correct however the copyWith method creates a new instance, which is fine. – Jose Georges Jan 07 '21 at 16:07
  • meaning copyWith does not require the intermediate state?! Good to know – w461 Jan 07 '21 at 16:10
  • Yes, meaning that copyWith creates what you call "intermediate state". If op shares the code for the class, you'll see that copyWith should be creating a new instance and thus the stream will catch the update. if you pass the exact same values as before to the instance however the update will not happen. – Jose Georges Jan 07 '21 at 16:13
  • I just wanted to implement this. For the interested reader, .copyWith first needs to be implemented as a method to that specific state. See https://stackoverflow.com/a/63877434/13648205 – w461 Jan 10 '21 at 16:55
  • @JoseGeorges And the result is: in my case it does not update the UI unless I yield an InProgress state in between. I sticked to the example of my last comment. Anything else I might have missed? In my bloc, I have already added to the `MapToEvent` for the relevant event `if (state is CustomerSearchDisplayResults) { CustomerSearchDisplayResults thisState = state as CustomerSearchDisplayResults;` – w461 Jan 10 '21 at 17:30
  • I'd suggest you open a new question and explain your use case a bit more so I can take a look at it, but I have a project with 10+ blocs and they all have its own `state` with a `copyWith` method. You have to keep in mind that basically a bloc is a `Stream` and listeners only react when they notice the last element added is different from the previous one. You should debug how your state compares to itself. Are you using `equatable`? Like I said, it'll be easier to see it on a new question. – Jose Georges Jan 11 '21 at 13:59