gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 04:10:19 29 4
gpt4 key购买 nike

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

出于某种原因,当 LoginBloc来电AuthenticationBloc.add(loggedin()) , AuthenticationBloc 产生 AuthenticationAuthenticated()状态很好,但是 BlocBuilder在 Main.dart 中没有收到状态更改。甚至 OnTransitionAuthenticationAuthenticated 时触发SimpleBlocDelegate 内部已产生,但 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有关.依赖注入(inject)看起来像这样:
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在从工厂到惰性单例的依赖注入(inject)文件中......现在它可以工作了。但是,我听说对带有 Streams 的类使用单例会导致内存泄漏??我猜这意味着 LoginBloc没有与 AuthBloc 的同一个实例交谈Main.dart 正在听什么?我不知道如何确保没有单例......
  • 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.
    }
    }
    }

    最佳答案

    编辑
    使用 bloc 提供的方法进行依赖注入(inject),而不是 get_it。创建单例可能是一个问题,因为它不会自动处理。 BlocProvider处理和处置文档中提到的创建的 block

    In most cases, BlocProvider should be used to create new blocs which will be made available to the rest of the subtree. In this case, since BlocProvider is responsible for creating the bloc, it will automatically handle closing the bloc.


    并使用 BlocProvider.value按照 bloc 官方 documentation 中的建议传递值.
    BlocProvider(
    create: (BuildContext context) => BlocA(service1: sl<Service1>()),
    child: ChildA(),
    );
    这就是我一起使用 BlocProvider 和 get_it 的方式。我将 get_it 用于 Bloc 以外的所有内容。而 bloc 的参数是由 get_it 的依赖注入(inject)提供的。
    如果您想使用 get_it,请阅读 TLDR;部分。
    TLDR;
    使用 Singleton仅在必要时( AuthenticationBloc)。并继续使用 Factory对于所有其他集团( LoginBloc 等)。
    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()));
    }
    概念
    我在使用 bloc 时使用相同的方法。我们遇到的需要两个 block 进行通信的最常见情况是 AuthenticationBloc 与几乎所有其他 block 进行通信。
    为什么registerFactory不工作。但是registerLazySingleton
    getit 对 registerFactory 的定义

    You have to pass a factory function func that returns an NEW instance of an implementation of T. Each time you call get() you will get a new instance returned


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

    You have to pass a factory function func that returns an instance of an implementation of T. Only the first time you call get() this factory function will be called to create a new instance.


    如上所述,简单的解决方案是将依赖注入(inject)从 registerFactory 更改为至 registerLazySingleton .通过这样做,您将提供 AuthenticationBloc 的单个实例。在整个应用程序中。因此事件添加到 AuthenticationBloc来自 LoginBloc将开始工作。
    建议解决方案
    可以有两种解决方案。一个是在这个问题中提出的,即将每个 Bloc 更改为 lazySingleton .但它不会在需要时创建新的 Bloc。通过使用该方法,您将在整个应用程序中使用相同的 Bloc 实例。它适用于大多数情况。
    另一种方法是制作 Singleton仅在必要时( AuthenticationBloc)。并继续使用 Factory对于所有其他集团( LoginBloc 等)。
    认证 block
    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();
    }
    }

    依赖注入(inject)器
    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()));
    }

    关于Flutter Bloc 状态更改未使用 get_it 更新 UI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61851548/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com