Flutter Bloc 状态更改未使用 get_it 更新 UI

2023-12-30

我一直在结合使用本登录教程和 resocoder clean 架构教程来构建登录/身份验证功能。 99% 工作正常,但无法正确响应LoginButton被压。

由于某种原因,当LoginBloc calls AuthenticationBloc.add(loggedin()), AuthenticationBloc 产生AuthenticationAuthenticated()状态很好,但是BlocBuilderMain.dart 中没有收到状态更改。即便是OnTransitionSimpleBlocDelegate 内部被触发时AuthenticationAuthenticated已产生,但是BlocBuilder什么也没做。

Main.dart看起来像这样:

import 'package:bloc/bloc.dart';
import 'package:flutter_app/dependency_injector.dart' as di;
import 'package:flutter_app/features/login/presentation/pages/login_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'features/login/presentation/bloc/user_login_bloc.dart';
import 'features/login/presentation/bloc/user_login_events.dart';
import 'features/login/presentation/bloc/user_login_states.dart';

class SimpleBlocDelegate extends BlocDelegate {
  @override
  void onEvent(Bloc bloc, Object event) {
    print(event);
    super.onEvent(bloc, event);
  }

  @override
  void onTransition(Bloc bloc, Transition transition) {
    print(transition);
    super.onTransition(bloc, transition);
  }

  @override
  void onError(Bloc bloc, Object error, StackTrace stackTrace) {
    print(error);
    super.onError(bloc, error, stackTrace);
  }
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await di.init(); //Dependency Injection using get_it
  BlocSupervisor.delegate = SimpleBlocDelegate();
  runApp(
    BlocProvider<UserAuthenticationBloc>(
      create: (_) => sl<UserAuthenticationBloc>()..add(AppStarted()),
      child: App(),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocBuilder<UserAuthenticationBloc, AuthenticationState>(
        builder: (context, state) {
          if (state is AuthenticationAuthenticated) {
            return Container(
              child: HomePage(); // THIS NEVER HAPPENS, even though AuthBloc yields the State
          }
          if (state is AuthenticationUnauthenticated) {
            return LoginScreen(); // THIS yeilds fine when AppStarted in passed on init.
          }
          if (state is AuthenticationLoading) {
            return LoadingIndicator();
          }
          return Scaffold(
            body: SplashPage();
          )
        },
      ),
    );
  }
}

我只能认为这与get_it。依赖注入看起来像这样:

final sl = GetIt.instance;

Future<void> init() async {
  sl.registerFactory(
    () => UserAuthenticationBloc(
      getCachedUser: sl(),
    ),
  );

  sl.registerFactory(
    () => LoginBloc(authenticationBloc: sl(), getUserFromEmailAndPassword: sl()),
  );
...
}

然后在小部件树中loginscreen the LoginBloc已创建,因此可用于登录表单。

class LoginScreen extends StatelessWidget {
  LoginScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocProvider<LoginBloc>(
        create: (_) => sl<LoginBloc>(),
        child: LoginForm(), //login form
      ),
    );
  }
}

两次编辑: 1.我变了UserAuthenticationBloc在从工厂到惰性单例的依赖注入文件中......现在它可以工作了。但是,我听说对带有 Streams 的类使用单例会导致内存泄漏?我想这意味着LoginBloc不是在与同一个实例交谈AuthBlocMain.dart 正在听什么?我不知道如何确保没有单例......

  1. 用户认证块代码:
    class UserAuthenticationBloc extends Bloc<AuthenticationEvent, AuthenticationState> {
      final GetCachedUser getCachedUser;
      UserAuthenticationBloc({
        @required GetCachedUser getCachedUser,
      })  : assert(getCachedUser != null),
            getCachedUser = getCachedUser;

      @override
      AuthenticationState get initialState => AuthenticationUninitialized();

      @override
      Stream<AuthenticationState> mapEventToState(AuthenticationEvent event) async* {
        if (event is AppStarted) {
             yield AuthenticationUnauthenticated();
          }
        }

        if (event is LoggedIn) {
          yield AuthenticationAuthenticated(); //this fires.
        }
      }
    }

EDIT
使用 bloc 提供的方法进行依赖注入,而不是 get_it。创建单例可能是一个问题,因为它不会自动处理。BlocProvider处理和处置创建的块,如文档中所述

在大多数情况下,BlocProvider 应该用于创建新的块,这些块将可供子树的其余部分使用。在这种情况下,由于 BlocProvider 负责创建块,因此它将自动处理关闭块。

And use BlocProvider.value按照 bloc 官方建议传递值文档 https://bloclibrary.dev/#/flutterbloccoreconcepts?id=blocprovider.

BlocProvider(
  create: (BuildContext context) => BlocA(service1: sl<Service1>()),
  child: ChildA(),
);

这就是我一起使用 BlocProvider 和 get_it 的方式。我将 get_it 用于除 Bloc 之外的所有内容。而bloc的参数是由get_it的依赖注入提供的。

如果你想使用 get_it,请阅读 TLDR;部分。

TLDR;

Use Singleton仅在必要时(AuthenticationBloc)。并继续使用Factory对于所有其他集团(LoginBloc, etc).

final sl = GetIt.instance;
final Environment _env = Environment();

Future<void> init() async {
  //! Core
  ..load some singletons

  //! Bloc
  sl.registerLazySingleton(() => AuthenticationBloc(secureStorage: sl()));
  sl.registerFactory(() => LoginBloc(authenticationBloc: sl(), authService: sl()));
  sl.registerFactory(() => SignupBloc(authenticationBloc: sl(), authService: sl()));
}

Concepts

我在使用 bloc 时使用相同的方法。我们遇到的需要两个块进行通信的最常见情况是 AuthenticationBloc 与几乎所有其他块进行通信。

Why registerFactory不工作。但registerLazySingleton does

getit 的定义为registerFactory

您必须传递一个工厂函数 func,它返回 T 实现的新实例。每次调用 get() 时,您都会返回一个新实例

根据 get_it 文档。registerFactory每次我们调用时都会生成一个 Bloc 对象的新实例sl<AuthenticationBloc>()方法。 现在,当LoginBloc构造函数要求一个参数,我们传递sl()在我们的依赖注入文件中,我们正在创建一个新实例并将其传递给我们的LoginBloc。因此AuthenticationBloc我们的应用程序中使用的实例不等于AuthenticationBloc我们已经提供给我们的LoginBloc构造函数。结果你的AuthenticationBloc不会听取所传达的更改LoginBloc因为它将事件添加到了其他一些实例AuthenticationBloc.

registerLazySingleton定义为

您必须传递一个返回 T 实现实例的工厂函数 func。只有在您第一次调用 get() 时,才会调用此工厂函数来创建新实例。

如上所述,简单的解决方案是将依赖注入从registerFactory to registerLazySingleton。通过这样做,您将提供一个实例AuthenticationBloc整个申请过程中。因此事件添加到AuthenticationBloc from LoginBloc将开始工作。

建议的解决方案

可以有两种解决方案。一是这个问题中提出的,即将每个集团更改为lazySingleton。但它不会在需要时创建新的集团。通过使用该方法,您将在整个应用程序中使用相同的 Bloc 实例。它适用于大多数情况。

另一种方法是使Singleton仅在必要时(AuthenticationBloc)。并继续使用Factory对于所有其他集团(LoginBloc, etc).

认证块

class AuthenticationBloc extends Bloc<AuthenticationEvent, AuthenticationState> {
  final SecureStorage secureStorage;

  AuthenticationBloc({required this.secureStorage}) : super(AppInitial());

  @override
  Stream<AuthenticationState> mapEventToState(AuthenticationEvent event) async* {
    if (event is AppStarted) {
      AuthModel? authModel = await secureStorage.getAuthUser();
      if (authModel != null && authModel.jwtToken.isNotEmpty && authModel.userId.isNotEmpty) {
        yield AuthenticationUserKnown(authModel: authModel);
      } else {
        yield AuthenticationUserUnknown();
      }
    } else if (event is UserAuthenticated) {
      yield AuthenticationUserKnown(authModel: event.authModel);
    } else if (event is UserLoggedOut) {
      yield AuthenticationUserUnknown();
    }
  }
}

登录区块

class LoginBloc extends Bloc<LoginEvent, LoginState> {
  LoginBloc({required this.authenticationBloc, required this.validationHelper, required this.authService})
      : super(LoginInitial());
  final AuthenticationBloc authenticationBloc;
  final AuthService authService;
  final ValidationHelper validationHelper;

  @override
  Stream<LoginState> mapEventToState(LoginEvent event) async* {
    if (event is EmailAuthenticationRequested) {
      yield* _mapEmailAuthencationRequestedEventToState(event);
    }
  }

  Stream<LoginState> _mapEmailAuthencationRequestedEventToState(EmailAuthenticationRequested event) async* {
    yield AuthenticationInProgress();
    final authEither = await authService.loginWithEmail(email: event.email, password: event.password);

    yield authEither.fold(
      (failure) => LoginAuthenticationFailure(failureMessage: failure.errorMessage),
      (authModel) {
        authenticationBloc.add(UserAuthenticated(authModel: authModel));
        return LoginAuthenticationSuccess(authModel: authModel, authenticationMethod: AuthenticationMethod.EMAIL);
      },
    );
  }

  @override
  Future<void> close() {
    authenticationBloc.close();
    return super.close();
  }
}

依赖注入器

final sl = GetIt.instance;
final Environment _env = Environment();

Future<void> init() async {
  //! Core
  ..load some singletons

  //! Bloc
  sl.registerLazySingleton(() => AuthenticationBloc(secureStorage: sl()));
  sl.registerFactory(() => LoginBloc(authenticationBloc: sl(), validationHelper: sl(), authService: sl()));
  sl.registerFactory(() => SignupBloc(validationHelper: sl(), authService: sl()));
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Flutter Bloc 状态更改未使用 get_it 更新 UI 的相关文章

随机推荐