1
votes

everyone. I am new in Flutter and BLoC pattern.

I needed to implement contact page so I created event GetContacts and passed it into context.read().add() after that I called this event into initState() of contacts screen.

Here my event:


abstract class ContactEvent extends Equatable {
  const ContactEvent([List props = const []]) : super();
}

class GetContacts extends ContactEvent {

  const GetContacts() : super();

  @override
  List<Object> get props => [];
} 

Here is my bloc:

class ContactsBloc extends Bloc<ContactEvent, ContactsState> {
  final ContactsRepo contactsRepo;
  ContactsBloc({required this.contactsRepo}) : super(ContactInitial());

  @override
  Stream<ContactsState> mapEventToState(ContactEvent event,) async* {
    yield ContactsLoading();
    //
    // if (event is UpdatePhoto) {
    //   yield PhotoLoading();
    //
    //   print("LOADING STARTED");
    //
    //   final photo = await contactsRepo.updatePhoto(event.identifier, event.photo);
    //   print("LOADING FINISHED");
    //
    //   yield PhotoLoaded(photo: photo);
    // }
    if (event is GetContacts) {
      print("get contacts photoBloc");
      try {

        final contacts = await contactsRepo.getContacts();
        yield ContactsLoaded(contacts);
      } on AccessException {
        yield ContactsError();
      }
    }
  }
} 

That works right and contacts page renders contacts as it is supposed.

[contacts screen][1] [1]: https://i.stack.imgur.com/Gx3JA.png

But then I decided to implement new feature: when user clicks on any contact he is offered to change its photo. If I understand BLoC pattern correctly then if I want to change my state I need to create new event. Then I created new action UpdatePhoto and passed it into the same Bloc as it shown at 2nd part of code (in comments). Exactly there I encounter a misunderstanding of architecture expansion. This action is not supposed to return ContactsLoaded state so when I tried to catch this into my another bloc builder it broke my previous bloc builder that caught GetContact event.

ContactState:

abstract class ContactsState extends Equatable {
  const ContactsState([List props = const []]) : super();
}


// class PhotoLoading extends PhotoState {
//   @override
//   List<Object?> get props => [];
// }
//
// class PhotoLoaded extends PhotoState {
//   final Uint8List photo;
//   const PhotoLoaded({required this.photo});
//   @override
//   List<Object?> get props => [photo];
// }

class ContactInitial extends ContactsState {
  @override
  List<Object> get props => [];
}

class ContactsLoading extends ContactsState {
  @override
  List<Object> get props => [];
}

class ContactsLoaded extends ContactsState {
  final List<MyContact> contacts;
  ContactsLoaded(this.contacts) : super([contacts]);

  @override
  List<Object> get props => [contacts];
}

class ContactsError extends ContactsState {
  @override
  List<Object?> get props => [];
}

Question: If I want to create new event (for example UpdatePhoto) which is not supposed to return the state that I caught before at the same bloc then I need to create new bloc for that purpose and cover my screen by multiProvider?

2

2 Answers

2
votes

You should also post your ContactState code.

However you do not necessarely need a new Bloc. It all depends on what you are trying to achieve. I suppose than when you yield PhotoLoading() you want to show a loader. But when you update the photos, if I understand what you are trying to achieve you should yield an updated list of contacts using again yield ContactsLoaded(contacts) or add(GetContacts())instead of yield PhotoLoaded(photo: photo). If you want to show a confirmation message, you can keep your PhotoLoaded state, but you need to build your UI taking into account the different state the bloc may emit.

Remember in BloC architecture event can yield to multiple states in successions and the UI decide if and how to react to each state.

1
votes

I guess use optional parameter buildWhen in BlocBuilder is the best way to avoid creating new bloc for each event.