BlocListener不监听状态变化

huangapple go评论74阅读模式
英文:

BlocListener doesn't listen a changing state

问题

BlocListener必须在LoginBloc的状态为LogIn()时运行HomeEvent.loadUserDetails()。

body: BlocListener<LoginBloc, LoginState>(
  listener: (context, state) {
    state.maybeWhen(
      logIn: (user) => homeBloc.add(
        HomeEvent.loadUserDetails(user.email!),
      ),
      orElse: () {},
    );
  },
  child: NestedScrollView(
    headerSliverBuilder: (context, bool innerBoxIsScrolled) => [
      const HomeSliverAppBar(),
    ],
    body: BlocBuilder<HomeBloc, HomeState>(
      bloc: homeBloc,
      builder: (context, state) {
        return state.maybeWhen(
          loaded: (userInfo) => const HomeListView(),
          // TODO: implement shimmer view
          orElse: () => const Center(child: CircularProgressIndicator()),
        );
      },
    ),
  ),
),

在这段代码中,BlocListener会监听LoginBloc的状态变化,并在状态为LogIn()时触发HomeBloc的事件,具体是通过添加HomeEvent.loadUserDetails(user.email!)事件来实现的。

英文:

I try to solve that problem a couple of days, but I can't. I have a login form, when user signs in, the LoginBloc changes its state to LogIn() and BlocListener(which is located in HomeView.dart) must change HomeBloc's state from Initial() to Loaded(), but it doesn't work. I don't know why but BlocListener ignores that LoginBloc has LogIn() state and doesn't run HomeBlocEvent.loadUserDetails()
I appreciate any help or pieces of advice how to solve this problem.
I used Firebase as Authentication service.

// LoginBloc.dart
@injectable
class LoginBloc extends Bloc&lt;LoginEvent, LoginState&gt; {
  final UserSignInImpl _userSignInImpl;

  LoginBloc(this._userSignInImpl) : super(const LoginState.initial()) {
    on&lt;SignIn&gt;((event, emit) async {
      emit(const LoginState.loading());
      final result = await _userSignInImpl.call(event.user);
      result.fold((error) {
        emit(LoginState.loadFailed(error));
      }, (success) {
        emit(LoginState.logIn(user: success));
      });
    });
  }
}
  // HomeView.dart
  @override
  Widget build(BuildContext context) {
    final homeBloc = BlocProvider.of&lt;HomeBloc&gt;(context);
    final loginBloc = BlocProvider.of&lt;LoginBloc&gt;(context);
      ......
      body: BlocListener&lt;LoginBloc, LoginState&gt;(
        listener: (context, state) {
          state.maybeWhen(
            // function below isn&#39;t called, but LoginState is LogIn()
            logIn: (user) =&gt; homeBloc.add(
              HomeEvent.loadUserDetails(user.email!),
            ),
            orElse: () {},
          );
        },
        child: NestedScrollView(
          headerSliverBuilder: (context, bool innerBoxIsScrolled) =&gt; [
            const HomeSliverAppBar(),
          ],
          body: BlocBuilder&lt;HomeBloc, HomeState&gt;(
            bloc: homeBloc,
            builder: (context, state) {
              return state.maybeWhen(
                loaded: (userInfo) =&gt; const HomeListView(),
                // TODO: implement shimmer view
                orElse: () =&gt; const Center(child: CircularProgressIndicator()),
              );
            },
          ),
        ),
      ),
// HomeBloc.dart
@injectable
class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; {
  HomeBloc() : super(const HomeState.initial()) {
    on&lt;LoadUserDetails&gt;((event, emit) async {
      emit(const HomeState.loading());
      await fbFireStore.getUserDetails(event.email).then(
            (userDetails) =&gt; emit(HomeState.loaded(user: userDetails)),
          );
    });
    on&lt;SignOut&gt;((event, emit) async {
      await fbAuth.signOut();
    });
  }
}

  // LoginView.dart
  @override
  Widget build(BuildContext context) {
    final loginBloc = BlocProvider.of&lt;LoginBloc&gt;(context);
  .....
                        child: ElevatedButton(
                        style: AppStyles.widePurpleButtonStyle,
                        onPressed: () {
                          if (_signInKey.currentState!.validate()) {
                           // Adding SignIn() event                            
                           loginBloc.add(LoginEvent.signIn(LoginUser(
                              email: _emailController.text.trim(),
                              password: _passwordController.text.trim(),
                            )));
                          }
                        },
                        child: Text(S.of(context).logIn),
                      ),
    .....
                   SizedBox(
                    height: MediaQuery.sizeOf(context).height * 0.05,
                    child: BlocConsumer&lt;LoginBloc, LoginState&gt;(
                      builder: (context, state) {
                        return state.maybeWhen(
                          loading: () {
                            return const CircularProgressIndicator();
                          },
                          orElse: () {
                            return const SizedBox();
                          },
                        );
                      },
                      listener: (context, state) {
                        state.maybeWhen(
                          loadFailed: (failure) {
                            showDialog(
                              context: context,
                              builder: (context) {
                                return ErrorAlertDialog(error: failure);
                              },
                            );
                          },
                          logIn: (_) {
                            context.router.navigate(const HomeRoute());
                          },
                          orElse: () {},
                        );
                      },
                    ),
                  ),

BlocListener must run HomeEvent.loadUserDetails() when LoginBloc's state is LogIn()

答案1

得分: 0

问题是当 LoginBloc 发出 LoginState.logIn() 时,HomeView 还没有在导航堆栈中。请注意,您在 LoginViewBlocConsumer&lt;LoginBloc, LoginState&gt;() 监听器中执行了 context.router.navigate(const HomeRoute());

因此,当实际打开 HomeView 时,LoginBloc 的状态已经是 LoginState.logIn(),而且 LoginBloc 不会发出其他状态,这就是为什么 HomeView 中的监听器没有收到任何内容的原因。

要在 HomeView 中访问用户详细信息,您只需获取 LoginBloc 的状态:

final state = loginBloc.state; // LoginState.login(user: ...)

但我建议只将“用户电子邮件”作为 HomeView 的参数传递,然后使用它来加载用户详细信息。

英文:

The thing is that when LoginBloc emits LoginState.logIn() your HomeView is not yet in the Navigation stack. Note that you are performing context.router.navigate(const HomeRoute()); inside listener of BlocConsumer&lt;LoginBloc, LoginState&gt;() in LoginView.

So when HomeView is actually opened LoginBloc state is LoginState.logIn() already and no other states are emitted by LoginBloc and that's why your listener in HomeView is not receiving anything.

To access user details inside HomeView you can just get LoginBloc state:

final state = loginBloc.state; // LoginState.login(user: ...)

But I suggest just to pass a user email as a parameter for HomeView and then use it to load user details.

huangapple
  • 本文由 发表于 2023年7月3日 21:12:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76605094.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定