Поместив два "yield" рядом друг с другом в Dart (Flutter), выполняется только вторая - PullRequest
2 голосов
/ 24 апреля 2019

Во Flutter я делаю страницу входа / регистрации, используя шаблон BLoC и Firebase. После регистрации пользователя я отправляю электронное письмо с подтверждением и меняю состояние на «VerificationEmailSentState» (чтобы я мог показать SnackBar и переключиться на страницу входа в PageView), а затем снова изменить его на исходное состояние (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 ...
      }
)

1 Ответ

1 голос
/ 24 апреля 2019

Это на самом деле поведение, которое вы просите это сделать. Вы переходите в состояние (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());
...