返回页面后 bloc 状态是相同的,即使它已被显式更改

bloc state is the same after coming back to a page even though it was explicitly changed

我有一个使用 CourseBloc

的小部件
class CourseBloc extends Bloc<CourseEvent, CourseState> {
  final GetCoursesQuery getCoursesQuery;
  final GetSettingsQuery getSettingsQuery;
  final PullCoursesFromServerCommand pullCoursesFromServerCommand;
  final UpdateTasksToServerCommand updateTasksToServerCommand;
  final GetNetworkInfoQuery getNetworkInfoQuery;

  CourseBloc({
    @required GetCoursesQuery getCoursesQuery,
    @required GetSettingsQuery getSettingsQuery,
    @required PullCoursesFromServerCommand pullCoursesFromServerCommand,
    @required UpdateTasksToServerCommand updateTasksToServerCommand,
    @required GetNetworkInfoQuery getNetworkInfoQuery,
  })  : assert(getCoursesQuery != null),
        assert(getSettingsQuery != null),
        assert(pullCoursesFromServerCommand != null),
        assert(updateTasksToServerCommand != null),
        assert(getNetworkInfoQuery != null),
        this.getCoursesQuery = getCoursesQuery,
        this.getSettingsQuery = getSettingsQuery,
        this.pullCoursesFromServerCommand = pullCoursesFromServerCommand,
        this.updateTasksToServerCommand = updateTasksToServerCommand,
        this.getNetworkInfoQuery = getNetworkInfoQuery;

  @override
  CourseState get initialState => CourseInitialState();

  @override
  Stream<CourseState> mapEventToState(
    CourseEvent event,
  ) async* {
    if (event is CoursesReturnedFromTasksEvent) {
      yield CourseInitialState();
    } else if (event is CoursesPageLoadedEvent) {
      yield CourseLoadingState();

      final getCoursesEither = await getCoursesQuery(
        GetCoursesParams(
          truckNumber: event.truckNumber,
        ),
      );

      yield* getCoursesEither.fold((failure) async* {
        yield CourseFetchedStateFailureState(error: "coursesDatabaseError");
      }, (result) async* {
        if (result != null) {
          final getSettingsEither = await getSettingsQuery(NoQueryParams());

          yield* getSettingsEither.fold((failure) async* {
            yield CourseFetchedStateFailureState(error: "coursesDatabaseError");
          }, (settingsResult) async* {
            if (result != null) {
              final networkInfoEither =
                  await this.getNetworkInfoQuery(NoQueryParams());

              yield* networkInfoEither.fold((failure) async* {
                yield CourseErrorState();
              }, (success) async* {
                yield CourseFetchedState(
                  settings: settingsResult,
                  courses: result,
                  isThereInternet: success,
                );
              });
            } else {
              yield CourseFetchedStateFailureState(
                  error: "coursesFetchFromDatabaseError");
            }
          });
        } else {
          yield CourseFetchedStateFailureState(
              error: "coursesFetchFromDatabaseError");
        }
      });
    } else if (event is CoursesRefreshButtonPressedEvent) {
      yield CourseLoadingState();

      final networkInfoEither = await this.getNetworkInfoQuery(NoQueryParams());

      yield* networkInfoEither.fold((failure) async* {
        yield CourseErrorState();
      }, (success) async* {
        if (success) {
          final updateTasksToServerEither = await updateTasksToServerCommand(
              UpdateTasksParams(truckNumber: event.truckNumber));

          yield* updateTasksToServerEither.fold((failure) async* {
            yield CourseFetchedStateFailureState(error: "coursesDatabaseError");
          }, (result) async* {
            final pullCoursesFromServerEither =
                await pullCoursesFromServerCommand(
                    PullCoursesParams(truckNumber: event.truckNumber));

            yield* pullCoursesFromServerEither.fold((failure) async* {
              yield CourseFetchedStateFailureState(
                  error: "coursesDatabaseError");
            }, (result) async* {
              if (result != null) {
                yield CoursePulledFromServerState();
              } else {
                yield CourseFetchedStateFailureState(
                    error: "coursesFetchFromDatabaseError");
              }
            });
          });
        } else {
          yield CourseNoInternetState();
        }
      });
    } else if (event is CoursesRefreshFromTasksButtonPressedEvent) {
      serviceLocator<TaskBloc>().add(
        TasksLoadingEvent(),
      );

      final networkInfoEither = await this.getNetworkInfoQuery(NoQueryParams());

      yield* networkInfoEither.fold((failure) async* {
        serviceLocator<TaskBloc>().add(
          TasksReloadingErrorEvent(),
        );
      }, (success) async* {
        if (success) {
          final updateTasksToServerEither = await updateTasksToServerCommand(
              UpdateTasksParams(truckNumber: event.truckNumber));

          yield* updateTasksToServerEither.fold((failure) async* {
            yield CourseFetchedStateFailureState(error: "coursesDatabaseError");
          }, (result) async* {
            final pullCoursesFromServerEither =
                await pullCoursesFromServerCommand(
                    PullCoursesParams(truckNumber: event.truckNumber));

            yield* pullCoursesFromServerEither.fold((failure) async* {
              serviceLocator<TaskBloc>().add(
                TasksFetchedFailureEvent(failure: "coursesDatabaseError"),
              );
            }, (result) async* {
              if (result != null) {
                serviceLocator<TaskBloc>().add(
                  TasksPulledFromServerEvent(
                    truckNumber: event.truckNumber,
                    courseNumber: event.courseNumber,
                    courseId: event.courseId,
                  ),
                );
              } else {
                serviceLocator<TaskBloc>().add(
                  TasksFetchedFailureEvent(
                      failure: "coursesFetchFromDatabaseError"),
                );
              }
            });
          });
        } else {
          yield CourseNoInternetState();
        }
      });
    }
  }
}

我在widget页面使用BlocBuilderBlocListener如下:

BlocBuilder<CourseBloc, CourseState>(
   builder: (context, state) {
     if (state is CourseFetchedState) {
     // here I have logic if the course is fetched
   }
...
),

BlocListener<CourseBloc, CourseState>(
   listener: (context, state) {
     if (state is CourseNoInternetState) {
     ...
   }
...
),

有时我离开了这个小部件。然后我想回到第一个(课程)小部件。我愿意:

serviceLocator<CourseBloc>().add(
     CoursesReturnedFromTasksEvent(),
);

serviceLocator.resetLazySingleton<TaskBloc>(
     instance: serviceLocator<TaskBloc>(),
);

Navigator.of(context).pop(true);

这告诉 CourseBloc 期待 CoursesReturnedFromTasksEvent() ,重置新的 TaskBloc(因为我不在这个页面上了,我不需要知道它处于什么状态)和弹出当前上下文。

然后我导航回来了。 CourseBloc 的映射方法适用于新状态,并根据 'if' 它产生:

if (event is CoursesReturnedFromTasksEvent) {
  yield CourseInitialState();
}

但课程页面中的生成器处于之前的状态。 CourseFetchedState。而且它还没有 'taken' 新状态(初始)。

知道为什么会发生这种情况吗?

以下是美国:

abstract class CourseState {
  CourseState();
}

class CourseInitialState extends CourseState {}

class CourseReturnedFromTasksState extends CourseState {}

class CourseLoadingState extends CourseState {}

class CourseErrorState extends CourseState {}

class CourseNoInternetState extends CourseState {}

class CoursePulledFromServerState extends CourseState {}

class CourseFetchedState extends CourseState {
  final SettingsAggregate settings;
  final List<CourseAggregate> courses;
  final bool isThereInternet;

  CourseFetchedState({
    @required this.settings,
    @required this.courses,
    @required this.isThereInternet,
  });
}

class CourseFetchedStateFailureState extends CourseState {
  final String error;

  CourseFetchedStateFailureState({@required this.error});
}

和事件:

abstract class CourseEvent {
  CourseEvent();
}

class CoursesRefreshButtonPressedEvent extends CourseEvent {
  final String truckNumber;

  CoursesRefreshButtonPressedEvent({@required this.truckNumber});
}

class CoursesRefreshFromTasksButtonPressedEvent extends CourseEvent {
  final String courseNumber;
  final String truckNumber;
  final int courseId;

  CoursesRefreshFromTasksButtonPressedEvent({
    @required this.courseNumber,
    @required this.truckNumber,
    @required this.courseId,
  });
}

class CoursesReturnedFromTasksEvent extends CourseEvent {}

class CoursesPageLoadedEvent extends CourseEvent {
  final String truckNumber;

  CoursesPageLoadedEvent({
    this.truckNumber,
  });
}

编辑 以下是 Bloc 的提供方式:

Column buildBody(BuildContext context) {
    return Column(
      children: <Widget>[
        BlocProvider(
          create: (_) => serviceLocator<CourseBloc>(),
          child: BlocBuilder<CourseBloc, CourseState>(
            builder: (context, state) {
              if (state is CourseFetchedState) {
              ...
              }
            ...
            }
           ),
          ),
         ],
        );
       }

serviceLocator() 仅在应用程序启动时实例化一次。它注册了整个应用程序生命周期中所需的所有实例,以实现依赖注入。这是 CourseBloc 注册:

import 'package:get_it/get_it.dart';

final serviceLocator = GetIt.instance;
...
serviceLocator.registerLazySingleton(
    () => CourseBloc(
      getCoursesQuery: serviceLocator(),
      getSettingsQuery: serviceLocator(),
      pullCoursesFromServerCommand: serviceLocator(),
      updateTasksToServerCommand: serviceLocator(),
      getNetworkInfoQuery: serviceLocator(),
    ),
  );
...

我不是很熟悉GetIt,但据我所知:

  1. 你用 serviceLocator 创建了 CourseBloc 的单例。
  2. 您使用 BlocProvider 将该单例提供给相关上下文(导致 CourseBloc 的 2 个实例)。
  3. 您的 BlocBuilder 使用通过 BlocProvider 提供的实例,因为您没有通过 bloc 参数明确告知(它将查找小部件树以找到 CourseBloc在相同的上下文中)。
  4. 您使用 serviceLocator 制作的 CourseBloc 的实例来添加 CoursesReturnedFromTasksEvent()

尝试通过 BlocProvider.of 以常规方式添加事件,但您仍然需要手动处理 serviceLocator

制作的 CourseBloc 单例
BlocProvider.of<CourseBloc>(context).add(
  CoursesReturnedFromTasksEvent(),
);