繁体   English   中英

验证后的 flutter_login 和 flutter_bloc 导航:BlocListener 未监听 state 更改

[英]flutter_login and flutter_bloc navigation after authentication: BlocListener not listening to state change

我正在尝试使用文档中设计模式将与 bloc 结合起来。

实例化 state 后,BlocListener 停止侦听身份验证集团,我有点被迫使用登录表单的 onSubmitAnimationCompleted 方法进行路由,这首先使侦听器无用。

  • MaterialApp() 与文档中提供的示例相同(我试图从登录屏幕(在本例中为 initialRoute)导航到主屏幕)

  • 登录表单如下所示:

@override
  Widget build(BuildContext context) {
    return BlocListener<AuthenticationBloc, AuthenticationState> (
      listener: (context, state) {
        // first time around state is read
        if (state is AuthenticationAuthenticated) {
          Navigator.of(context).pushNamed(Home.routeName);
        }
      },
      child: BlocBuilder(
        bloc: _loginBloc,
        builder: (BuildContext context, state) {
          return FlutterLogin(
            title: 'Login',
            logo: const AssetImage('lib/assets/madrid.png'),
            onLogin: _authUser,
            onSignup: _signupUser,
            onRecoverPassword: _recoverPassword,
            loginProviders: <LoginProvider>[
              ... Providers here...
            ],
            // if this method is omitted, I'll get a [ERROR:flutter/lib/ui/ui_dart_state.cc(209)]
            onSubmitAnimationCompleted: () {
              Navigator.of(context).pushNamed(Home.routeName);
            },
          );
        },
      ),
    );
  }
  • 我将事件 state 拆分为两个块,“AuthenticationBloc”(包装整个应用程序,如果已存储令牌,则 state 将是“AuthenticationAuthenticated”)和“LoginBloc”(用于登录/注销事件)

#1 当我点击注册按钮时,关联的方法将调用 _loginBloc?.add(SignUpButtonPressed(email: email, password: password))

#2 快进到集团:

LoginBloc({required this.authenticationBloc, required this.loginRepository})
    : super(const SignInInitial()) {
      on<SignUpButtonPressed>(_signUp);
    }

...

FutureOr<void> _signUp<LoginEvent>(SignUpButtonPressed event, Emitter<LoginState> emit) async {
    emit(const SignInLoading());

    try {

      final credentials = User(email: event.email, password: event.password);
      final success = await loginRepository.signUp(credentials);

      if (success) {
        final token = await loginRepository.signIn(credentials);
        authenticationBloc.add(LoggedIn(email: event.email, token: token));
      } else {
        emit(const SignInFailure(error: 'Something went wrong'));
      }

    } on Exception {
      emit(const SignInFailure(error: 'A network Exception was thrown'));
    } catch (error) {
      emit(SignInFailure(error: error.toString()));
    }
  }

  • 这是成功的,它触发了身份验证块:
  AuthenticationBloc({required this.userRepository})
    : super(const AuthenticationUninitialized()) {
      on<LoggedIn>(_loggedIn);
    }

...

  FutureOr<void> _loggedIn<AuthenticationEvent>(LoggedIn event, Emitter<AuthenticationState> emit) async {
    await userRepository?.persistEmailAndToken(
        event.email, event.token);
    await _initStartup(emit);
  }

...

  Future<void> _initStartup(Emitter<AuthenticationState> emit) async {
    final hasToken = await userRepository?.hasToken();

    if (hasToken != null && hasToken == true) {
      emit(const AuthenticationAuthenticated());
      return;
    } else {
      emit(const AuthenticationUnauthenticated());
    }
  }

...最后,state 更新为 AuthenticationAuthenticated,这是预期的行为,观察者按预期记录转换。

现在,这个 state 的变化应该会从 BlocListener 中触发导航,但是没有。

我想摆脱 onSubmitAnimationCompleted 中的导航器,并依赖于 state 更改。

我认为这可能是由 Equatable 引起的,因为我的 state 对此进行了扩展:

abstract class AuthenticationState extends Equatable {
  const AuthenticationState();

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

class AuthenticationAuthenticated extends AuthenticationState {
  const AuthenticationAuthenticated();
}

但是,我已经尝试了几个小时,但我在文档 github 或 SO 中找不到任何有用的东西。

因此,我一直无法摆脱 onSubmitAnimationCompleted 内部的 Navigator(我猜 BlocListener 在提交表单时被处理,并且在 animation 完成之前),但在这个过程中我设法让我的 state 管理干净而健壮,所以我会在下面留下一个小备忘单,请随时发表评论或发表您的意见:

  • 假设您的小部件的构建方法看起来像这样:
  @override
  Widget build(BuildContext context) {
    return BlocListener<AuthenticationBloc, AuthenticationState> (
      bloc: _authenticationBloc,
      listener: (context, state) {
        if (state.status == AuthenticationAppState.authenticated) {
          Navigator.of(context).pushNamed(Home.routeName);
        }
      },
      child: BlocBuilder(
        bloc: _loginBloc,
        builder: (BuildContext context, state) {
          return FlutterLogin(
      ...
  • 并且您的活动扩展了 Equatable
import 'package:equatable/equatable.dart';

abstract class AuthenticationEvent extends Equatable {
  const AuthenticationEvent();

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

class LoggedIn extends AuthenticationEvent {
  final String email;
  final dynamic token;
  const LoggedIn({ required this.email, this.token });

  @override
  List<Object> get props => [email, token];
}
  • 你的 Bloc 看起来像:
class AuthenticationBloc extends Bloc<AuthenticationEvent, AuthenticationState> {
  final SecureStorage? userRepository;

  AuthenticationBloc({required this.userRepository})
    : super(const AuthenticationState.uninitialized()) {
      on<LoggedIn>(_loggedIn);
      on<LoggedOut>(_loggedOut);
      on<UserDeleted>(_userDeleted);
    }

  ...
  FutureOr<void> _loggedOut<AuthenticationEvent>(LoggedOut event, Emitter<AuthenticationState> emit) async {
    emit(const AuthenticationState.loggingOut());
    await userRepository?.deleteToken();
    // API calls here
    // event has access the event's properties e.g. event.email etc
  }
  • state 已重构为:
import 'package:equatable/equatable.dart';

enum AuthenticationAppState {
  uninitialized,
  unauthenticated,
  authenticated,
  loggingOut,
  loading,
}

class AuthenticationState extends Equatable {
  const AuthenticationState._({
    required this.status,
});

  const AuthenticationState.uninitialized() : this._(status: AuthenticationAppState.uninitialized);
  const AuthenticationState.unauthenticated() : this._(status: AuthenticationAppState.unauthenticated);
  const AuthenticationState.authenticated() : this._(status: AuthenticationAppState.authenticated);
  const AuthenticationState.loggingOut() : this._(status: AuthenticationAppState.loggingOut);
  const AuthenticationState.loading() : this._(status: AuthenticationAppState.loading);

  final AuthenticationAppState status;

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM