简体   繁体   English

Flutter BLoC:在 build() 方法中的 StreamBuilder 中的 Navigator.pop

[英]Flutter BLoC: Navigator.pop in StreamBuilder in build() method

I'm following BLoC pattern and subscribing to stream, and reacting to state changes in build method.我遵循 BLoC 模式并订阅流,并对构建方法中的状态变化做出反应。 When data is loaded I want to close the screen.加载数据后,我想关闭屏幕。

  @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();
            }
          },
        ),
      ),
    );
  } 

When I do Navigator.pop(context, true);当我做Navigator.pop(context, true); in build() method I get: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.

What is the right way to handle such cases in BLoC pattern?在 BLoC 模式中处理此类情况的正确方法是什么?

On of the solutions I come up with is to start listening to stream on initState() .我提出的解决方案之一是开始在initState()上收听流。 In this case I need to broadcast() my stream because I have 2 subscribers.在这种情况下,我需要broadcast()我的流,因为我有 2 个订阅者。

Are there any better solutions for this?有没有更好的解决方案?

I think I have got a solution for you.我想我已经为你找到了解决方案。 (Please check it) (请检查一下)

Make your code look like :让你的代码看起来像:

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();
    });
}

Hope it helps.希望能帮助到你。 Just try it and please report here to help others too!试试吧,请在此处报告以帮助他人!

Source (flutter_bloc Login medium article) 来源(flutter_bloc 登录中篇文章)

Description 描述

I could imagine three possible solutions:我可以想象三种可能的解决方案:

1) It looks to me like it would be best to restructure your widgets. 1) 在我看来,最好重组您的小部件。 As far as I can see you want a "Loading Screen" .. I see no reason that this has to be it's own navigation item, instead of just another widget.据我所知,你想要一个“加载屏幕”..我认为没有理由这必须是它自己的导航项,而不仅仅是另一个小部件。

Ie. IE。 You could push the StreamBuilder one?你可以推StreamBuilder一个吗? level up.. so your builder method looks like:升级.. 所以你的构建器方法看起来像:

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

2) I think i personally would create a StatefulWidget , listen to the stream manually in initState() and call setState() manually. 2)我认为我个人会创建一个StatefulWidget ,在initState()手动收听流并手动调用setState() No need for a StreamBuilder不需要StreamBuilder

3) as a crazy workaround you could probably use Future(() { Navigator.of(context).pop(); }); 3) 作为一种疯狂的解决方法,您可能可以使用Future(() { Navigator.of(context).pop(); }); in your builder.在你的建造者中。 (it's possible that you'd have to use the context of the build method, not the builder. but I wouldn't recommend that solution anyway) (您可能必须使用build方法的context ,而不是构建器。但无论如何我都不推荐该解决方案)

bool hasPop = false;

  @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) {
            if(!hasPop){
               hasPop = true;
               Navigator.pop(context, true);
             }
            
            return Container();
          }
          if (snapshot.data is UserLoadingState) {
            return _buildLoading();
          }
        },
      ),
    ),
  );
} ```

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM