Bloc: How to access previous state inside mapEventToState?

Created on 13 May 2019  路  6Comments  路  Source: felangel/bloc

I have three states MembersLoadedState, MembersError, and MembersValidationError.

My builder looks like this:
body: BlocBuilder<MemberEvent, MemberrState>( bloc: memberBloc, builder: (BuildContext context, MemberState state) => if (state is MembersLoaded) { // show list of users } else if (state is MembersError) { // show error } ),

When the members are loaded there is an edit button that opens a modal for editing member, in there after submitting the form I dispatch an event to edit a member. memberBloc.dispatch(EditMember(member));

This is my bloc code for editing a member.

Stream<MemberState> _mapEditMemberToState(EditMember event) async* {

  try {
    Member editedMember = await memberRepository.edit(event.member);

    if (currentState is MembersLoaded) {
      List<Member> members =
          (currentState as MembersLoaded).members.map((member) {
        return member.id == editedMember.id ? editedMember : member;
      }).toList();

      yield MembersLoaded(members);
    }
  } catch (e) {
    if (e.response.status == 422) {
      yield MembersValidationError("Validation message");
      return;
    }

    yield MembersError(e.message);
  }
}

Now let's imagine that a validation error occurs and the validation message is shown to the user, then the user fills the form inputs correctly and resubmits the form. Now we know that the currentState is MembersValidationError so the user cannot edit the member because we checked if (currentState is MembersLoaded). So is there any way to access the MembersLoaded state data?

question

Most helpful comment

@felangel Thank you very much for helping me.

All 6 comments

Hi @Matiullah-karimi 馃憢
Thanks for opening an issue.

Regarding your question, based on the user experience/feature you are describing I would recommend updating your MembersValidationError state to extend from the MembersLoaded.

abstract class MembersState {}

class MembersLoaded extends MembersState {
  final List<Member> members;

  MembersLoaded(this.members);
}

class MembersValidationError extends MembersLoaded {
  final List<Member> members;
  final String errorMessage;

  MembersValidationError(this.members, this.errorMessage) {
    super(this.members);
  }
}

That way you always have access to the members list even if there was an error validating. Hope that helps 馃憤

Thank you @felangel but in this case, I always need to pass members list as a second parameter when dispatching events. For example, previously I had this memberBloc.dispatch(EditMember(member)); and now I must pass members list as well memberBloc.dispatch(EditMember(member, members)); right?

@Matiullah-karimi if that's the case, then you can probably move members to the base class and yup, you'd need to pass the members. Alternatively you can restructure your state and implement a copyWith method that returns a new instance of your class and copies the members over.

abstract class MembersState {}

class MembersLoaded extends MembersState {
  final List<Member> members;
  final String validationError;

  MembersLoaded({this.members, this.validationError});

  MembersLoaded copyWith({List<Member> members, String validationError}) {
    return MembersLoaded(
      members: members ?? this.members,
      validationError: validationError ?? this.validationError
    );
  }
}

Hope that helps 馃憤

@felangel how to use copyWith inside bloc? I'm completely lost, please help.

I would recommend taking a look at the flutter infinite list tutorial for an example. You can see copyWith being used in the PostBloc.

Hope that helps and let me know if you have additional questions 馃憤

@felangel Thank you very much for helping me.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

clicksocial picture clicksocial  路  3Comments

krusek picture krusek  路  3Comments

Reidond picture Reidond  路  3Comments

rsnider19 picture rsnider19  路  3Comments

MahdiPishguy picture MahdiPishguy  路  3Comments