英文:
BlocListener doesn't listen a changing state
问题
BlocListener必须在LoginBloc的状态为LogIn()时运行HomeEvent.loadUserDetails()。
body: BlocListener<LoginBloc, LoginState>(
listener: (context, state) {
state.maybeWhen(
logIn: (user) => homeBloc.add(
HomeEvent.loadUserDetails(user.email!),
),
orElse: () {},
);
},
child: NestedScrollView(
headerSliverBuilder: (context, bool innerBoxIsScrolled) => [
const HomeSliverAppBar(),
],
body: BlocBuilder<HomeBloc, HomeState>(
bloc: homeBloc,
builder: (context, state) {
return state.maybeWhen(
loaded: (userInfo) => const HomeListView(),
// TODO: implement shimmer view
orElse: () => const Center(child: CircularProgressIndicator()),
);
},
),
),
),
在这段代码中,BlocListener会监听LoginBloc的状态变化,并在状态为LogIn()时触发HomeBloc的事件,具体是通过添加HomeEvent.loadUserDetails(user.email!)事件来实现的。
英文:
I try to solve that problem a couple of days, but I can't. I have a login form, when user signs in, the LoginBloc changes its state to LogIn() and BlocListener(which is located in HomeView.dart) must change HomeBloc's state from Initial() to Loaded(), but it doesn't work. I don't know why but BlocListener ignores that LoginBloc has LogIn() state and doesn't run HomeBlocEvent.loadUserDetails()
I appreciate any help or pieces of advice how to solve this problem.
I used Firebase as Authentication service.
// LoginBloc.dart
@injectable
class LoginBloc extends Bloc<LoginEvent, LoginState> {
final UserSignInImpl _userSignInImpl;
LoginBloc(this._userSignInImpl) : super(const LoginState.initial()) {
on<SignIn>((event, emit) async {
emit(const LoginState.loading());
final result = await _userSignInImpl.call(event.user);
result.fold((error) {
emit(LoginState.loadFailed(error));
}, (success) {
emit(LoginState.logIn(user: success));
});
});
}
}
// HomeView.dart
@override
Widget build(BuildContext context) {
final homeBloc = BlocProvider.of<HomeBloc>(context);
final loginBloc = BlocProvider.of<LoginBloc>(context);
......
body: BlocListener<LoginBloc, LoginState>(
listener: (context, state) {
state.maybeWhen(
// function below isn't called, but LoginState is LogIn()
logIn: (user) => homeBloc.add(
HomeEvent.loadUserDetails(user.email!),
),
orElse: () {},
);
},
child: NestedScrollView(
headerSliverBuilder: (context, bool innerBoxIsScrolled) => [
const HomeSliverAppBar(),
],
body: BlocBuilder<HomeBloc, HomeState>(
bloc: homeBloc,
builder: (context, state) {
return state.maybeWhen(
loaded: (userInfo) => const HomeListView(),
// TODO: implement shimmer view
orElse: () => const Center(child: CircularProgressIndicator()),
);
},
),
),
),
// HomeBloc.dart
@injectable
class HomeBloc extends Bloc<HomeEvent, HomeState> {
HomeBloc() : super(const HomeState.initial()) {
on<LoadUserDetails>((event, emit) async {
emit(const HomeState.loading());
await fbFireStore.getUserDetails(event.email).then(
(userDetails) => emit(HomeState.loaded(user: userDetails)),
);
});
on<SignOut>((event, emit) async {
await fbAuth.signOut();
});
}
}
// LoginView.dart
@override
Widget build(BuildContext context) {
final loginBloc = BlocProvider.of<LoginBloc>(context);
.....
child: ElevatedButton(
style: AppStyles.widePurpleButtonStyle,
onPressed: () {
if (_signInKey.currentState!.validate()) {
// Adding SignIn() event
loginBloc.add(LoginEvent.signIn(LoginUser(
email: _emailController.text.trim(),
password: _passwordController.text.trim(),
)));
}
},
child: Text(S.of(context).logIn),
),
.....
SizedBox(
height: MediaQuery.sizeOf(context).height * 0.05,
child: BlocConsumer<LoginBloc, LoginState>(
builder: (context, state) {
return state.maybeWhen(
loading: () {
return const CircularProgressIndicator();
},
orElse: () {
return const SizedBox();
},
);
},
listener: (context, state) {
state.maybeWhen(
loadFailed: (failure) {
showDialog(
context: context,
builder: (context) {
return ErrorAlertDialog(error: failure);
},
);
},
logIn: (_) {
context.router.navigate(const HomeRoute());
},
orElse: () {},
);
},
),
),
BlocListener must run HomeEvent.loadUserDetails() when LoginBloc's state is LogIn()
答案1
得分: 0
问题是当 LoginBloc
发出 LoginState.logIn()
时,HomeView
还没有在导航堆栈中。请注意,您在 LoginView
的 BlocConsumer<LoginBloc, LoginState>()
监听器中执行了 context.router.navigate(const HomeRoute());
。
因此,当实际打开 HomeView
时,LoginBloc
的状态已经是 LoginState.logIn()
,而且 LoginBloc
不会发出其他状态,这就是为什么 HomeView
中的监听器没有收到任何内容的原因。
要在 HomeView
中访问用户详细信息,您只需获取 LoginBloc
的状态:
final state = loginBloc.state; // LoginState.login(user: ...)
但我建议只将“用户电子邮件”作为 HomeView
的参数传递,然后使用它来加载用户详细信息。
英文:
The thing is that when LoginBloc
emits LoginState.logIn()
your HomeView
is not yet in the Navigation stack. Note that you are performing context.router.navigate(const HomeRoute());
inside listener of BlocConsumer<LoginBloc, LoginState>()
in LoginView
.
So when HomeView
is actually opened LoginBloc
state is LoginState.logIn()
already and no other states are emitted by LoginBloc
and that's why your listener in HomeView
is not receiving anything.
To access user details inside HomeView
you can just get LoginBloc
state:
final state = loginBloc.state; // LoginState.login(user: ...)
But I suggest just to pass a user email
as a parameter for HomeView
and then use it to load user details.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论