BlocObserver
BlocObserver - это инструмент, который позволяет глобально отслеживать и реагировать на изменения всех блоков в приложении.
Будет полезен для:
— Логирования / отладки всех событий и изменений состояния
— Интеграции со сторонними сервисами
— Отслеживания ошибок
— Мониторинга производительности
— Отправки аналитики
Пакет bloc содержит в себе только API обсервера и реализацию по умолчанию - простую заглушку без какой-либо логики.
В проект bloc_example добавил CustomBlocObserver. В нем мы просто смотрим на все изменения всех состояний всех блоков.
Вот часть реализации обсервера. Как можно заметить, тут только вызовы debugPrint:
class CustomBlocObserver extends BlocObserver {
const CustomBlocObserver();
@override
void onCreate(BlocBase bloc) {
debugPrint('--- ${bloc.runtimeType} created');
super.onCreate(bloc);
}
@override
void onEvent(Bloc bloc, Object? event) {
debugPrint('--- ${bloc.runtimeType} received event: $event');
super.onEvent(bloc, event);
}
// В полноценном примере так же реализованы методы:
// - onChange
// - onClose
// - onDone
// - onError
// - onTransition
//
// Принцип работы у них такой же - пишут логи в консоль.
}
Исходники находятся тут.
Для того, чтобы обсервер работал, нужно инициализировать его:
Future<void> main() async {
// Стоит отметить, что ваш обсервер - это синглтон, и он доступен для всех блоков.
Bloc.observer = const CustomBlocObserver();
runApp(const BLoCExampleApp());
}
В проекте события и состояния реализованы как sealed-классы. В них нет реализации метода toString, поэтому данные в
обсервере будут приходить без деталей.
Вывод без правок такой:
flutter: --- UsersBloc changed state: Change { currentState: Instance of 'UsersBlocStateLoaded', nextState: Instance of 'UsersBlocStateLoading' }
flutter: --- UsersBloc moved to: Transition { currentState: Instance of 'UsersBlocStateLoading', event: Instance of 'UsersBlocEventFetch', nextState: Instance of 'UsersBlocStateLoaded' }
flutter: --- UsersBloc changed state: Change { currentState: Instance of 'UsersBlocStateLoading', nextState: Instance of 'UsersBlocStateLoaded' }
Чтобы добавить к логам конкретики, реализовал в классах состояний и событий метод toString:
flutter: --- UsersBloc moved to: Transition { currentState: UsersBlocStateLoaded{users (count): 30, canLoadMore: true, page: 1}, event: Instance of 'UsersBlocEventRefresh', nextState: UsersBlocStateLoading{} }
flutter: --- UsersBloc changed state: Change { currentState: UsersBlocStateLoaded{users (count): 30, canLoadMore: true, page: 1}, nextState: UsersBlocStateLoading{} }
flutter: --- UsersBloc moved to: Transition { currentState: UsersBlocStateLoading{}, event: Instance of 'UsersBlocEventFetch', nextState: UsersBlocStateLoaded{users (count): 30, canLoadMore: true, page: 1} }
flutter: --- UsersBloc changed state: Change { currentState: UsersBlocStateLoading{}, nextState: UsersBlocStateLoaded{users (count): 30, canLoadMore: true, page: 1} }
Читать и обрабатывать подробный вывод намного проще, чем пытаться разобрать, что могло произойти в обезличенном событии или состоянии. Формат вывода и детали реализации можно делать на свое усмотрение и по своим нуждам.
Логика обсервера должна быть максимально легкой и нейтральной. Идеальный обсервер выполняет только наблюдение и минимальную обработку данных, не вмешиваясь в основную бизнес-логику и не создавая сайд-эффектов, которые могут изменить поведение приложения.