颤振集团状态更改不会使用 get_it 更新 UI



我一直在结合使用此登录教程和 resocoder 清理架构教程来构建登录/身份验证功能。它 99% 工作正常,但它没有正确响应被按下的LoginButton

出于某种原因,当LoginBloc调用AuthenticationBloc.add(loggedin())时,AuthenticationBloc 可以很好地生成AuthenticationAuthenticated()状态,但 Main.dart 中的BlocBuilder不会接收状态更改。即使是 SimpleBlocDelegate 内部的OnTransition也会在生成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的小部件树中创建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没有与 Main.dart 正在侦听的同一个AuthBloc实例交谈?我不知道如何确保没有单例...

  1. UserAuthenticationBloc的代码:
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
Use bloc 提供了用于依赖注入而不是get_it的方法。创建单一实例可能是一个问题,因为它不会自动释放。BlocProvider处理和处置文档中提到的创建的块

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

并使用BlocProvider.value按照 bloc 官方文档中的建议传递值。

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

这就是我如何使用BlocProvider和get_it在一起。我将get_it用于集团以外的所有内容。而 bloc 的参数由 get_it 的依赖注入提供。

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

TLDR;

仅在必要时使用Singleton(AuthenticationBloc(。并继续对所有其他集团(LoginBloc等(使用Factory

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()));
}

概念

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

为什么registerFactory不起作用。但registerLazySingleton做到了

getit 对registerFactory的定义

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

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

registerLazySingleton定义为

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

如上所述,简单的解决方案是将依赖注入从registerFactory更改为registerLazySingleton。通过执行此操作,您将在整个应用程序中提供单个AuthenticationBloc实例。因此,从LoginBloc添加到AuthenticationBloc的事件将开始工作。

建议的解决方案

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

另一种方法是仅在必要时进行Singleton(AuthenticationBloc(。并继续将Factory用于所有其他集团(LoginBloc等(。

身份验证组

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()));
}

我在项目中遇到了同样的问题。我使用相同的堆栈,而且当我从另一个集团添加事件时,UI 看不到任何更改。我使用的临时解决方案 - 我使用GlobalKey<ScaffoldState>将上下文传递给该集团。因此,UI 和 Bloc 使用相同的上下文。您可以在LoginBloc中使用它:

BlocProvider.of<UserAuthenticationBloc>(yourScaffoldKey.currentContext).add(LoggedIn event)

相关内容

  • 没有找到相关文章

最新更新