在 Dart (Flutter) 中将两个 "yield" 并排放置,只有第二个被执行
Placing two "yield"s next to each other in Dart (Flutter), only the second get executed
在 Flutter 中,我正在使用 BLoC 模式和 Firebase 创建一个 Login/Signup 页面。注册用户后,我发送一封验证邮件并将状态更改为 "VerificationEmailSentState"(这样我就可以在 PageView 中显示一个 SnackBar 并切换到登录页面),然后将其重新更改为初始状态 (InitialState)。问题是它直接跳转到 InitialState 而没有经过 VerificationEmailSentState 因此不显示 SnackBar 或切换到登录页面!
当我调试代码时,我发现它实际上是将状态更改为 (VerificationEmailSentState),但随后立即将其更改回 InitialState,因此 BlocBuilder 中的代码不会被执行,因为它正在被中断(重新构建)当我将状态更改为 InitialState 时。为了确保,我在第一次更改状态后延迟了 1 秒,它起作用了(SnackBar 出现了)。
问题是为什么会这样?推迟状态的第二次改变是个好主意吗?如果没有,如何让它在没有它的情况下工作?
//SignupBloc
await user.sendEmailVerification();
yield SignupVerificationEmailSentState();
await Future.delayed(Duration(seconds: 1)); //Will not work without it.
yield SignupInitialState();
//LoginSignupPage
BlocBuilder<SignupEvent, SignupState>(
bloc: _signupBloc,
builder: (BuildContext context, SignupState state) {
if (state is SignupVerificationEmailSentState) {
print("About to show SnackBar");//Printed only with delay
raiseSnackBar();
swtichToLogin();
}
return ...
}
)
这实际上就是您要求它执行的行为。您正在生成一个状态 (SignupVerificationEmailSentState
),然后立即将其更改回另一个状态 SignupInitialState
。
您在状态 is SignupVerificationEmailSentState
时调用 raiseSnackBar()
,这是正确的,但是正如您指出的那样,正在重建视图,因为您实际上没有足够的时间将其还原用户看到 SnackBar
。 await Future.delayed(Duration(seconds: 1))
实际上是给了它一秒钟的时间来展示它。
这不是 "bad approach",它取决于您应用的流程和您实际想要实现的目标。但是,我宁愿做
Future.delayed(Duration(seconds: 1)).then((_) => yield SignupInitialState());
这不会阻止在该方法调用之后对 运行 的任何其他指令,假设您可能有一些指令。如果你不这样做,那很好。
如果你想确保它只在 SnackBar
显示 1 秒后恢复到初始状态,请删除这两行:
await Future.delayed(Duration(seconds: 1)); //Will not work without it.
yield SignupInitialState();
你可以这样做:
Scaffold.of(context)
.showSnackBar(
SnackBar(
content: Text('Something...'),
duration: Duration(seconds: 1),
),
)
.closed
.then((_) => bloc.revertToSignupInitialState());
在 Flutter 中,我正在使用 BLoC 模式和 Firebase 创建一个 Login/Signup 页面。注册用户后,我发送一封验证邮件并将状态更改为 "VerificationEmailSentState"(这样我就可以在 PageView 中显示一个 SnackBar 并切换到登录页面),然后将其重新更改为初始状态 (InitialState)。问题是它直接跳转到 InitialState 而没有经过 VerificationEmailSentState 因此不显示 SnackBar 或切换到登录页面!
当我调试代码时,我发现它实际上是将状态更改为 (VerificationEmailSentState),但随后立即将其更改回 InitialState,因此 BlocBuilder 中的代码不会被执行,因为它正在被中断(重新构建)当我将状态更改为 InitialState 时。为了确保,我在第一次更改状态后延迟了 1 秒,它起作用了(SnackBar 出现了)。
问题是为什么会这样?推迟状态的第二次改变是个好主意吗?如果没有,如何让它在没有它的情况下工作?
//SignupBloc
await user.sendEmailVerification();
yield SignupVerificationEmailSentState();
await Future.delayed(Duration(seconds: 1)); //Will not work without it.
yield SignupInitialState();
//LoginSignupPage
BlocBuilder<SignupEvent, SignupState>(
bloc: _signupBloc,
builder: (BuildContext context, SignupState state) {
if (state is SignupVerificationEmailSentState) {
print("About to show SnackBar");//Printed only with delay
raiseSnackBar();
swtichToLogin();
}
return ...
}
)
这实际上就是您要求它执行的行为。您正在生成一个状态 (SignupVerificationEmailSentState
),然后立即将其更改回另一个状态 SignupInitialState
。
您在状态 is SignupVerificationEmailSentState
时调用 raiseSnackBar()
,这是正确的,但是正如您指出的那样,正在重建视图,因为您实际上没有足够的时间将其还原用户看到 SnackBar
。 await Future.delayed(Duration(seconds: 1))
实际上是给了它一秒钟的时间来展示它。
这不是 "bad approach",它取决于您应用的流程和您实际想要实现的目标。但是,我宁愿做
Future.delayed(Duration(seconds: 1)).then((_) => yield SignupInitialState());
这不会阻止在该方法调用之后对 运行 的任何其他指令,假设您可能有一些指令。如果你不这样做,那很好。
如果你想确保它只在 SnackBar
显示 1 秒后恢复到初始状态,请删除这两行:
await Future.delayed(Duration(seconds: 1)); //Will not work without it.
yield SignupInitialState();
你可以这样做:
Scaffold.of(context)
.showSnackBar(
SnackBar(
content: Text('Something...'),
duration: Duration(seconds: 1),
),
)
.closed
.then((_) => bloc.revertToSignupInitialState());