шаблон блока управления событиями flutter

#flutter #dart #events #bloc

Вопрос:

каждый. Я новичок в Флаттере и блочном узоре.

Мне нужно было реализовать страницу контактов, поэтому я создал событие getContacts и передал его в context.read().add() после этого я вызвал это событие в initState() экрана контактов.

Вот мое мероприятие:

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

class GetContacts extends ContactEvent {

  const GetContacts() : super();

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

Вот мой блок:

 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();
      }
    }
  }
} 
 

Это работает правильно, и страница «Контакты» отображает контакты так, как это предполагается.

[экран контактов][1] [1]: https://i.stack.imgur.com/Gx3JA.png

Но потом я решил внедрить новую функцию: когда пользователь нажимает на любой контакт, ему предлагается изменить его фотографию. Если я правильно понимаю шаблон блока, то, если я хочу изменить свое состояние, мне нужно создать новое событие. Затем я создал новое действие UpdatePhoto и передал его в тот же блок, что и во 2-й части кода (в комментариях). Именно там я сталкиваюсь с непониманием расширения архитектуры. Это действие не должно возвращать загруженное состояние контактов, поэтому, когда я попытался поймать это в свой другой конструктор блоков, он сломал мой предыдущий конструктор блоков, который поймал событие GetContact.

Контактное государство:

 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 => [];
}
 

Вопрос: Если я хочу создать новое событие (например, UpdatePhoto), которое не должно возвращать состояние, которое я поймал ранее в том же блоке, то мне нужно создать новый блок для этой цели и закрыть свой экран с помощью мультипровидера?

Ответ №1:

Вы также должны опубликовать свой код состояния контакта.

Однако вам необязательно нужен новый блок. Все зависит от того, чего вы пытаетесь достичь. Я полагаю, чем когда yield PhotoLoading() вы хотите показать загрузчик. Но когда вы обновляете фотографии, если я понимаю, чего вы пытаетесь достичь, вы должны предоставить обновленный список контактов, используя снова yield ContactsLoaded(contacts) или add(GetContacts()) вместо yield PhotoLoaded(photo: photo) этого . Если вы хотите показать сообщение с подтверждением, вы можете сохранить загруженное состояние, но вам необходимо создать пользовательский интерфейс с учетом другого состояния, которое может излучать блок.

Помните, что в архитектуре блоков событие может последовательно переходить в несколько состояний, и пользовательский интерфейс решает, реагировать ли и как на каждое состояние.

Комментарии:

1. Ага. Загрузка фотографий означает, что я хочу показать загрузчик. Но пока отображается загрузчик, я не хочу скрывать страницу контактов, и я хочу изменить только 1 контакт, поэтому было бы здорово, если бы только один контакт был заменен загрузчиком во время загрузки, возможно ли это? Мне жаль, если мое объяснение проблемы немного глупо

2. Или, может быть, это возможно, если мой BlocBuilder реагирует только на одно специальное событие ( не на все события)

3. Конечно, можно создавать только для специального состояния (не событие, как сказал u, пользовательский интерфейс реагирует только на состояние). Это параметр Build when. Но вы можете захотеть получить обновленный список контактов, чтобы показать новый список с новой фотографией…

4. Чтобы достичь желаемого с помощью загрузчика только для одного элемента, я бы действительно создал новый блок для управления отдельными элементами… Таким образом, у вас будет поставщик блоков с блоком списка в верхней части дерева виджетов и блоком элементов для каждого виджета контактов, каждый из которых отвечает за управление пользовательским интерфейсом одной плитки контактов. Но даже при такой реализации вам все равно, вероятно, потребуется указать состояние обновления контакта в блоке списка, чтобы уведомить пользовательский интерфейс о том, что один из контактов должен быть обновлен

5. Спасибо за совет, я думаю, что параметр buildWhen-это именно то, что я нашел. 🙂

Ответ №2:

Я предполагаю, что использование необязательного параметра buildWhen в BlocBuilder-лучший способ избежать создания нового блока для каждого события.