英文:
flutter cubit to cubit communication not working
问题
NOTE: POST UPDATED!
我有两个cubit,其中一个是`LoginCubit`,负责处理登录操作,有3个不同的状态(`LoginLoading`,`LoginSuccess`,`LoginFailure`)。另一个cubit是`AuthCubit`,有4个状态(`AuthInitial`, `AuthLoading`, `Authenticated`, `Unauthenticated`),负责身份验证事件。
如果用户已登录(即状态为`LoginSuccess`),则`AuthState`必须转换为`Authenticated`。在这一点上,我监听来自`AuthCubit`的`LoginState`,以检测是否发生任何状态更改。以下是`AuthCubit`监听`LoginState`的代码。
**AuthCubit**
final IsSignInUseCase isSignInUseCase;
final SignOutUseCase signOutUseCase;
final ReadAuthFromStorageUseCase readAuthFromStorageUseCase;
final LoginCubit loginCubit;
late final StreamSubscription streamSubscription;
AuthCubit(
{required this.signOutUseCase,
required this.loginCubit,
required this.readAuthFromStorageUseCase,
required this.isSignInUseCase})
: super(AuthInitial()) {
streamSubscription = loginCubit.stream.listen((state) {
//在这里,我监听Login状态,如果登录状态成功,则进行身份验证
if (state is LoginSuccess) {
emit(Authenticated(authResponse: state.authResponse));
}
});
}
Future
emit(AuthLoading());
bool isSignIn = await isSignInUseCase.call();
if (isSignIn) {
final AuthResponse authResponse = await readAuthFromStorageUseCase.call();
emit(Authenticated(authResponse: authResponse));
} else {
emit(Unauthenticated());
}
}
//关闭流
@override
Future
streamSubscription.cancel();
return super.close();
}
由于我正在监听`LoginState`,现在我可以区分已登录用户和未登录用户。根据这一点,我构建以下屏幕。
**main.dart**
BlocBuilder<AuthCubit, AuthState>(
builder: (context, state) {
if (state is Authenticated) {
return const HomeScreen();
} else if (state is Unauthenticated) {
return BlocListener<LoginCubit, LoginState>(
listener: (context, state) {
if (state is LoginFailure) {
showDialog(
context: context,
builder: (context) => CustomAlertDialog(
title: ExceptionConst.exceptionTitle,
content: state.appException.message,
onPressed: () => Navigator.pop(context)));
}
},
child: const LoginScreen(),
);
} else {
return const SplashScreen();
}
},
));
我使用`get_it`进行依赖注入。
**dependency_injection**
sl.registerFactory(() => LoginCubit(loginUserUseCase: sl.call()));
sl.registerFactory(() => AuthCubit(
isSignInUseCase: sl.call(),
loginCubit: sl.call(),
readAuthFromStorageUseCase: sl.call(),signOutUseCase: sl.call()));
sl.registerLazySingleton(() => LoginUserUseCase(authRepo: sl.call()));
sl.registerLazySingleton(() => SignUpUserUseCase(authRepo: sl.call()));
sl.registerLazySingleton(() => IsSignInUseCase(authRepo: sl.call()));
sl.registerLazySingleton(() => SignOutUseCase(authRepo: sl.call()));
sl.registerLazySingleton(
() => ReadAuthFromStorageUseCase(authRepo: sl.call()));
...
最后,这是我的`MultiBlocProvider`。
void main() async {
await di.init();
runApp(MultiBlocProvider(providers: [
BlocProvider(create: () => di.sl
BlocProvider(create: (
], child: const MyApp()));
}
但是,当我按下登录按钮时,除非我手动使用热重启重新加载应用程序,否则`AuthState`不会更改。
英文:
NOTE: POST UPDATED!
I have two cubits one of them is LoginCubit
which is responsible for login actions and has 3 different states( LoginLoading
, LoginSuccess
, LoginFailure
). The other cubit is AuthCubit
which has 4 states(AuthInitial
, AuthLoading
, Authenticated
, Unauthenticated
) and it is responsible for authentication events.
If the user is logged in (that means if the state is LoginSuccess
), then the AuthState
must be converted to Authenticated
). At this point, I listen to LoginState
from my AuthCubit
, if any state changes occur. This is the code where AuthCubit
listens LoginState
.
AuthCubit
final IsSignInUseCase isSignInUseCase;
final SignOutUseCase signOutUseCase;
final ReadAuthFromStorageUseCase readAuthFromStorageUseCase;
final LoginCubit loginCubit;
late final StreamSubscription streamSubscription;
AuthCubit(
{required this.signOutUseCase,
required this.loginCubit,
required this.readAuthFromStorageUseCase,
required this.isSignInUseCase})
: super(AuthInitial()) {
streamSubscription = loginCubit.stream.listen((state) {
//here I listen to Login changes, if the login state is success, then authenticate
if (state is LoginSuccess) {
emit(Authenticated(authResponse: state.authResponse));
}
});
}
Future<void> checkAuthentication() async {
emit(AuthLoading());
bool isSignIn = await isSignInUseCase.call();
if (isSignIn) {
final AuthResponse authResponse = await readAuthFromStorageUseCase.call();
emit(Authenticated(authResponse: authResponse));
} else {
emit(Unauthenticated());
}
}
//close stream sub
@override
Future<void> close() {
streamSubscription.cancel();
return super.close();
}
Since I am listening to LoginState
, now I can distinguish between the logged-in users and non-logged-in users. According to this, I build my screen as the following.
main.dart
BlocBuilder<AuthCubit, AuthState>(
builder: (context, state) {
if (state is Authenticated) {
return const HomeScreen();
} else if (state is Unauthenticated) {
return BlocListener<LoginCubit, LoginState>(
listener: (context, state) {
if (state is LoginFailure) {
showDialog(
context: context,
builder: (context) => CustomAlertDialog(
title: ExceptionConst.exceptionTitle,
content: state.appException.message,
onPressed: () => Navigator.pop(context)));
}
},
child: const LoginScreen(),
);
} else {
return const SplashScreen();
}
},
));
I use get_it
for di.
dependency_injection
sl.registerFactory(() => LoginCubit(loginUserUseCase: sl.call()));
sl.registerFactory(() => AuthCubit(
isSignInUseCase: sl.call(),
loginCubit: sl.call(),
readAuthFromStorageUseCase: sl.call(),signOutUseCase: sl.call()));
sl.registerLazySingleton(() => LoginUserUseCase(authRepo: sl.call()));
sl.registerLazySingleton(() => SignUpUserUseCase(authRepo: sl.call()));
sl.registerLazySingleton(() => IsSignInUseCase(authRepo: sl.call()));
sl.registerLazySingleton(() => SignOutUseCase(authRepo: sl.call()));
sl.registerLazySingleton(
() => ReadAuthFromStorageUseCase(authRepo: sl.call()));
...
and lastly this is my MultiBlocProvider
void main() async {
await di.init();
runApp(MultiBlocProvider(providers: [
BlocProvider(create: (_) => di.sl<LoginCubit>()),
BlocProvider(create: (_) => di.sl<AuthCubit>()..checkAuthentication()),
], child: const MyApp()));
}
However, when I press the login button the AuthState
is not changing unless I reload the app manually using hot restart.
答案1
得分: 1
I solved this problem by changing my listening technique from StreamSubscription
to BlocListener
. Still, I don't know why the StreamSubscription
has not worked. Until someone can solve it I will accept my answer.
Here is the solution, I cleaned all StreamSubscription
codes.
AuthCubit
class AuthCubit extends Cubit<AuthState> {
final IsSignInUseCase isSignInUseCase;
final SignOutUseCase signOutUseCase;
final ReadAuthFromStorageUseCase readAuthFromStorageUseCase;
AuthCubit(
{required this.signOutUseCase,
required this.readAuthFromStorageUseCase,
required this.isSignInUseCase})
: super(AuthInitial());
Future<void> checkAuthentication() async {
emit(AuthLoading());
try{
bool isSignIn = await isSignInUseCase.call();
if (isSignIn) {
final AuthResponse authResponse = await readAuthFromStorageUseCase.call();
emit(Authenticated(authResponse: authResponse));
} else {
emit(Unauthenticated());
}
}catch(_){
emit(Unauthenticated());
}
}
Future<void> signOut() async {
try {
emit(AuthLoading());
await signOutUseCase.call();
emit(Unauthenticated());
} catch (e) {
emit(Unauthenticated());
}
}
}
main.dart
BlocBuilder<AuthCubit, AuthState>(
builder: (context, authState) {
if (authState is Authenticated) {
return const HomeScreen();
} else if (authState is Unauthenticated) {
return BlocListener<LoginCubit, LoginState>(
listener: (context, loginState) {
//the following if check make it work. If the LoginSuccess then recheck authentication status.
if (loginState is LoginSuccess) {
BlocProvider.of<AuthCubit>(context).checkAuthentication();
}
if (loginState is LoginFailure) {
showDialog(
context: context,
builder: (context) => CustomAlertDialog(
title: ExceptionConst.exceptionTitle,
content: loginState.appException.message,
onPressed: () => Navigator.pop(context)));
}
},
child: const LoginScreen(),
);
} else if (authState is AuthLoading) {
return const Center(
child: ButtonSpinner(),
);
}
return const SplashScreen();
},
)
Finally, in my LoginCubit
, I emit AuthState
.
class LoginCubit extends Cubit<LoginState> {
final LoginUserUseCase loginUserUseCase;
final AuthCubit authCubit;
LoginCubit({required this.authCubit, required this.loginUserUseCase})
: super(LoginInitial());
Future<void> loginUser(LoginRequest loginDto) async {
try {
emit(LoginLoading());
final authResponse = await loginUserUseCase.call(loginDto);
emit(LoginSuccess(authResponse: authResponse));
//if the login process is succeed, then change emit the AuthState as Authenticated.
authCubit.emit(Authenticated(authResponse: authResponse));
} on AppException catch (appEx) {
emit(LoginFailure(appException: appEx));
}
}
}
英文:
I solved this problem by changing my listening technique from StreamSubscription
to BlocListener
. Still, I don't know why the StreamSubscription
has not worked. Until someone can solve it I will accept my answer.
Here is the solution, I cleaned all `StreamSubscription' codes.
AuthCubit
class AuthCubit extends Cubit<AuthState> {
final IsSignInUseCase isSignInUseCase;
final SignOutUseCase signOutUseCase;
final ReadAuthFromStorageUseCase readAuthFromStorageUseCase;
AuthCubit(
{required this.signOutUseCase,
required this.readAuthFromStorageUseCase,
required this.isSignInUseCase})
: super(AuthInitial());
Future<void> checkAuthentication() async {
emit(AuthLoading());
try{
bool isSignIn = await isSignInUseCase.call();
if (isSignIn) {
final AuthResponse authResponse = await readAuthFromStorageUseCase.call();
emit(Authenticated(authResponse: authResponse));
} else {
emit(Unauthenticated());
}
}catch(_){
emit(Unauthenticated());
}
}
Future<void> signOut() async {
try {
emit(AuthLoading());
await signOutUseCase.call();
emit(Unauthenticated());
} catch (e) {
emit(Unauthenticated());
}
}
}
main.dart
BlocBuilder<AuthCubit, AuthState>(
builder: (context, authState) {
if (authState is Authenticated) {
return const HomeScreen();
} else if (authState is Unauthenticated) {
return BlocListener<LoginCubit, LoginState>(
listener: (context, loginState) {
//the following if check make it work. If the LoginSuccess then recheck authentication status.
if (loginState is LoginSuccess) {
BlocProvider.of<AuthCubit>(context).checkAuthentication();
}
if (loginState is LoginFailure) {
showDialog(
context: context,
builder: (context) => CustomAlertDialog(
title: ExceptionConst.exceptionTitle,
content: loginState.appException.message,
onPressed: () => Navigator.pop(context)));
}
},
child: const LoginScreen(),
);
} else if (authState is AuthLoading) {
return const Center(
child: ButtonSpinner(),
);
}
return const SplashScreen();
},
),
finally in my LoginCubit I emit AuthState
.
class LoginCubit extends Cubit<LoginState> {
final LoginUserUseCase loginUserUseCase;
final AuthCubit authCubit;
LoginCubit({required this.authCubit, required this.loginUserUseCase})
: super(LoginInitial());
Future<void> loginUser(LoginRequest loginDto) async {
try {
emit(LoginLoading());
final authResponse = await loginUserUseCase.call(loginDto);
emit(LoginSuccess(authResponse: authResponse));
//if the login process is succeed, then change emit the AuthState as Authenticated.
authCubit.emit(Authenticated(authResponse: authResponse));
} on AppException catch (appEx) {
emit(LoginFailure(appException: appEx));
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论