Flutter BLoC: Navigator.pop в StreamBuilder в методе build ()

Я следую шаблону BLoC и подписываюсь на поток и реагирую на изменения состояния в методе сборки. Когда данные загружены, я хочу закрыть экран.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Bloc'),
      ),
      body: SafeArea(
        child: StreamBuilder<UserState>(
          stream: _userBloc.user,
          initialData: UserInitState(),
          builder: (context, snapshot) {
            if (snapshot.data is UserInitState) {
              return _buildInit();
            }
            if (snapshot.data is UserDataState) {
              Navigator.pop(context, true);
              return Container();
            }
            if (snapshot.data is UserLoadingState) {
              return _buildLoading();
            }
          },
        ),
      ),
    );
  } 

Когда я делаю Navigator.pop(context, true); методом build(), я получаю:

I/flutter ( 4360): ══╡ EXCEPTION CAUGHT BY ANIMATION LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter ( 4360): The following assertion was thrown while notifying status listeners for AnimationController:
I/flutter ( 4360): setState() or markNeedsBuild() called during build.
I/flutter ( 4360): This Overlay widget cannot be marked as needing to build because the framework is already in the
I/flutter ( 4360): process of building widgets. A widget can be marked as needing to be built during the build phase
I/flutter ( 4360): only if one of its ancestors is currently building. This exception is allowed because the framework
I/flutter ( 4360): builds parent widgets before children, which means a dirty descendant will always be built.
I/flutter ( 4360): Otherwise, the framework might not visit this widget during this build phase.

Как правильно обрабатывать такие случаи в шаблоне BLoC?

Одно из решений, которое я придумал, - начать слушать стрим на initState(). В этом случае мне нужно broadcast() мой поток, потому что у меня 2 подписчика.

Есть ли для этого лучшие решения?


person Vadims Savjolovs    schedule 13.04.2019    source источник
comment
Вы уже упомянули об идеальном решении.   -  person creativecreatorormaybenot    schedule 13.04.2019
comment
Возможный дубликат Переход к новому экрану при потоковой передаче значение в BLOC изменяется   -  person SoloWolf93    schedule 12.11.2019


Ответы (3)


Думаю, у меня есть решение для вас. (Пожалуйста, проверь это)

Сделайте так, чтобы ваш код выглядел так:

Widget build(BuildContext context) {
    // other stuff

    if (snapshot.data is UserDataState) {
      myCallback(() {
        Navigator.pop(context, true);
      });
    }

    // other stuff
}
// after build method (but still in the same class,...) write below method

void myCallback(Function callback) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      callback();
    });
}

Надеюсь, это поможет. Просто попробуйте и, пожалуйста, сообщите здесь, чтобы помочь другим!

Источник (статья о среде входа flutter_bloc)

Описание

person Ravi Kavaiya    schedule 20.05.2019

Я мог представить себе три возможных решения:

1) Мне кажется, что было бы лучше реструктурировать ваши виджеты. Насколько я понимаю, вам нужен «экран загрузки» ... Я не вижу причин, по которым это должен быть отдельный элемент навигации, а не просто еще один виджет.

Т.е. Вы могли бы подтолкнуть StreamBuilder один? уровень вверх .. так что ваш метод построения выглядит так:

if (!snapshot.hasData) {
  return LoadingScreen(snapshot);
}
// Whatever you do with your data.

2) Я думаю, что лично я бы создал StatefulWidget, прослушал поток вручную в initState() и вызвал setState() вручную. Нет необходимости в StreamBuilder

3) в качестве безумного обходного пути вы, вероятно, могли бы использовать Future(() { Navigator.of(context).pop(); }); в своем конструкторе. (возможно, вам придется использовать context метода build, а не конструктор, но я бы все равно не рекомендовал это решение)

person Herbert Poul    schedule 13.04.2019

person    schedule
comment
пожалуйста, вы можете объяснить этот код? Благодарность! - person corn on the cob; 23.02.2021
comment
Каждое изменение в StreamBuilder запускается дважды. Navigator.pop нельзя вызывать дважды. - person mario francois; 24.02.2021