在Flutter中,我使用BLoC模式和Firebase创建了一个登录/注册页面。注册用户后,我发送验证邮件并将状态更改为“VerificationEmailSentState”(因此我可以在PageView中显示SnackBar并切换到Login页面),然后将其重新更改为初始状态(InitialState)。问题是它直接跳转到InitialState而没有通过VerificationEmailSentState,因此没有显示SnackBar或切换到Login页面!
当我调试代码时,我发现它实际上将状态更改为(VerificationEmailSentState),然后立即将其更改回InitialState,因此BlocBuilder中的代码没有被执行,因为当我更改时它被中断(重建) state to 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
。
你在状态raiseSnackBar()
时调用is SignupVerificationEmailSentState
,这是正确的,但是,正如你所指出的那样,视图正在重建,因为你实际上是在没有足够时间让用户看到SnackBar
的情况下还原它。 await Future.delayed(Duration(seconds: 1))
实际上给了它第二次展示它。
这不是一个“坏方法”,它将取决于您的应用程序的流程以及您实际尝试实现的目标。但是,我宁愿这样做
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());