BlocListener 混乱

BlocListener confusion

我正在尝试使用 flutter blocs 制作一个应用程序,但我遇到了 BlocListener 未被调用的问题,我无法弄清楚我做错了什么。

这是重现我的问题的最简代码:


import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => AuthBloc(),
      child: const AppView(),
    );
  }
}

/**************** APP VIEW **************/

class AppView extends StatefulWidget {
  const AppView({Key? key}) : super(key: key);

  @override
  State<AppView> createState() => _AppViewState();
}

class _AppViewState extends State<AppView> {
  final _navigatorKey = GlobalKey<NavigatorState>();

  NavigatorState get _navigator => _navigatorKey.currentState!;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: _navigatorKey,
      builder: (context, child) {
        print('App builder');
        return BlocListener<AuthBloc, AuthState>(
          listener: (context, state) {
            print('Bloc listener');
            switch (state.status) {
              case AuthStatus.authenticated:
                _navigator.pushAndRemoveUntil<void>(
                  MaterialPageRoute(
                    builder: (context) {
                      return Center(
                        child: Column(
                          children: [
                            const Text('Home'),
                            ElevatedButton(
                              onPressed: () {
                                context.read<AuthBloc>().add(
                                    const AuthStatusChanged(
                                        AuthStatus.unauthenticated));
                              },
                              child: const Text('Log out'),
                            ),
                          ],
                        ),
                      );
                    },
                  ),
                  (route) => false,
                );
                break;
              default:
                _navigator.pushAndRemoveUntil<void>(
                  MaterialPageRoute(
                    builder: (context) {
                      return Center(
                        child: Column(
                          children: [
                            const Text('Login'),
                            ElevatedButton(
                              onPressed: () {
                                context.read<AuthBloc>().add(
                                    const AuthStatusChanged(
                                        AuthStatus.authenticated));
                              },
                              child: const Text('Log in'),
                            ),
                          ],
                        ),
                      );
                    },
                  ),
                  (route) => false,
                );
                break;
            }
          },
          child: child,
        );
      },
      onGenerateRoute: (_) => MaterialPageRoute(
        builder: (context) {
          return const Center(
            child: Text('splash'),
          );
        },
      ),
    );
  }
}

/**************** AUTH BLOC CLASSES **************/

/**************** AUTH State **************/
enum AuthStatus { unknown, unauthenticated, authenticated }

class AuthState extends Equatable {
  final AuthStatus status;

  const AuthState._({
    this.status = AuthStatus.unknown,
  });

  const AuthState.unknown() : this._();

  const AuthState.authenticated() : this._(status: AuthStatus.authenticated);

  const AuthState.unauthenticated()
      : this._(status: AuthStatus.unauthenticated);

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

/**************** AUTH Event **************/
abstract class AuthEvent extends Equatable {
  const AuthEvent();

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

class AuthStatusChanged extends AuthEvent {
  final AuthStatus status;

  const AuthStatusChanged(this.status);

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

/**************** AUTH BLOC **************/
class AuthBloc extends Bloc<AuthEvent, AuthState> {
  AuthBloc() : super(const AuthState.unknown()) {
    print('Bloc constructor');
    on<AuthStatusChanged>(_onAuthStatusChanged);
  }

  _onAuthStatusChanged(
    AuthStatusChanged event,
    Emitter<AuthState> emit,
  ) async {
    switch (event.status) {
      case AuthStatus.unauthenticated:
        return emit(const AuthState.unauthenticated());
      case AuthStatus.authenticated:
        return emit(const AuthState.authenticated());
      default:
        return emit(const AuthState.unknown());
    }
  }
}

当我启动应用程序时,我希望 BlocListener 被调用一次,但它却位于初始页面上。

我使用本教程生成了这段代码:https://bloclibrary.dev/#/flutterlogintutorial

编辑: 谢谢大家的洞察力,我不明白 BlocListener 不会在 initialState 上触发事件(我猜是 RTFM xD)。回顾我使用的教程,这是由“存储库”处理的,它提供创建时延迟的流,Bloc 正在侦听该流以触发状态事件的更改。重复使用相同的概念对我有用!

BlocListener 仅在状态改变时触发。在应用程序加载时,您可能想要触发一个 bloc 事件来更改 AuthBloc 状态。 这可以通过在 initState() 函数中添加一个 bloc 事件并放置一个断点以查看是否触发侦听器来实现。

https://pub.dev/documentation/flutter_bloc/latest/flutter_bloc/BlocListener-class.html

侦听器仅在状态更改后调用,在创建 AuthBloc 后不会调用。

还有一个提示:context.read<AuthBloc>().add(const AuthStatusChanged(AuthStatus.authenticated)); 不要 那样做。更喜欢在你的 bloc 实例中创建将触发事件的方法,它使你的代码更容易测试,如果将 very_good_analysis 添加到你的项目中,这是一个由 VGV(flutter bloc 背后的同一家公司)构建的 linter,你将看到有一条 lint 规则反对它。