团体,颤动和导航

时间:2019-01-09 16:10:12

标签: navigation flutter bloc

因此,与大多数人一样,我是Bloc的新手,挥舞着飞镖,挥舞着头。我已经用Google搜索,浏览了这里的帖子,但还没有找到真正的答案。

所以这是关于用团块和颤动进行导航的。以登录为例。因此,有一个登录页面,后面有一个集团,有时有人按下按钮进行登录。

因此,我们可以在bloc中调用一个函数进行验证。我认为这违反严格的方法,但我看到有人这样做。但是,如果登录成功,您如何导航到下一个屏幕?您不应该在一个集团中导航吗?

但是,如果该登录页面正在使用StreamBuilder更改状态,那么您也不能在构建器中添加导航吗?您无法返回导航,而返回小部件。

可以在initstate中导航某个位置,但是可以在initstate中放置一个流生成器,以侦听bloc中状态的变化吗?

这一切现在有点令人困惑,但是我很坚持,因为应该是前进的方向...

谢谢 保罗

4 个答案:

答案 0 :(得分:2)

BlocListener是您可能需要的小部件。如果状态更改为(例如)LoginSuccess,则块侦听器可以调用通常的Navigate.of(context)example here shows the BlocListener in action,但没有导航,而是显示了Snackbar。

另一个选择是将回调传递给事件。

 BlocProvider.of<MyBloc>(context).add(MyEvent(
              data: data,
              onSuccess: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) {
                    return HomePage();
                  }),
                );
              }));

答案 1 :(得分:2)

首先:如果没有业务逻辑,则无需进入YourBloc类。

但是不时需要某些用户的活动来执行 Bloc 类中的某些逻辑,然后Bloc类必须决定下一步要做的事情:只是重建小部件或< strong>显示对话框,甚至导航到下一条路线。在这种情况下,您必须向UI发送一些状态才能完成操作。 然后出现另一个问题:当Bloc发送State来显示吐司时,我该如何处理窗口小部件?

这是所有故事的主要问题。

许多答案和文章建议使用flutter_block。该库具有BlocBuilderBlocListener。使用这些课程,您可以解决一些问题,但不能解决其中的100%。

在我的情况下,我使用BlocConsumer来管理 BlocBuilder BlocListener ,并提供了一种管理状态的绝妙方法。

从文档中:

BlocConsumer<BlocA, BlocAState>(
  listenWhen: (previous, current) {
    // return true/false to determine whether or not
    // to invoke listener with state
  },
  listener: (context, state) {
    // do stuff here based on BlocA's state
  },
  buildWhen: (previous, current) {
    // return true/false to determine whether or not
    // to rebuild the widget with state
  },
  builder: (context, state) {
    // return widget here based on BlocA's state
  }
)

使用 BlocConsumer 可以看到,您可以过滤状态:您可以轻松定义状态以重建小部件,并可以定义状态以显示一些弹出窗口或导航至下一个屏幕。

答案 2 :(得分:1)

使BLoC的神话成为 前进的道路:没有完美的状态处理方法。 每种状态管理体系结构都比其他解决方案更好地解决了一些问题。总会有权衡取舍,在决定体系结构时要意识到这一点很重要。

通常,好的架构很实用:它具有可扩展性和可扩展性,而只需要最小的开销。 由于人们对实用性的观点各不相同,因此架构始终会涉及各种意见,因此,请仔细考虑以下内容,因为我将就如何为您的应用程序采用BLoC提出自己的看法。

BLoC是Flutter中非常有前途的状态管理方法,因为它具有一个标志性成分:流。 它们允许将UI与业务逻辑分离,并且它们与Flutter-ish的方法(当过时重新构建整个小部件子树)一起使用时效果很好。 因此,很自然,往返于BLoC的每次通信都应使用流,对吗?

+----+  Stream   +------+
| UI | --------> | BLoC |
|    | <-------- |      |
+----+   Stream  +------+

好吧,

要记住的重要一点是,状态管理体系结构是达到目的的一种手段;您不应该只是为了做到这一点而做事,而是要保持开放的态度并仔细评估每种选择的利弊。 我们将BLoC与UI分开的原因是,BLoC不需要关心UI的结构-它仅提供一些不错的简单流,而数据发生的一切都是UI的责任。

但是,尽管流已被证明是从BLoC到UI传输信息的绝佳方式,但它们却在另一个方向增加了不必要的开销: 流被设计为传输连续的数据流(甚至在名称中也是如此),但是大多数时候,UI只需要触发BLoC中的单个事件即可。这就是为什么有时您会看到某些Stream<void>或类似的恶意解决方案¹,只是为了遵循严格的BLoC-y处理方式。

此外,如果我们根据来自BLoC的流推送新路由,则BLoC基本上可以控制UI流程-但是,我们试图避免的是拥有直接控制UI和业务逻辑的代码!

这就是为什么某些开发人员(包括我)只是打破了完全基于流的解决方案,并采用自定义方式从UI触发BLoC中的事件的原因。 就我个人而言,我只是使用方法调用(通常返回Future)来触发BLoC的事件:

+----+   method calls    +------+
| UI | ----------------> | BLoC |
|    | <---------------- |      |
+----+   Stream, Future  +------+

在这里,BLoC返回Stream的“实时”数据和Future的数据作为方法调用的答案。

让我们看看如何解决您的示例:

  • BLoC可以提供Stream<bool>用户是否登录的信息,甚至可以提供Stream<Account>的信息,其中Account包含用户的帐户信息。
  • BLoC还可以提供一种异步Future<void> signIn(String username, String password)方法,如果登录成功,则该方法不返回任何内容,否则将引发错误。
  • UI可以自行处理输入管理,并在按下登录按钮后触发类似以下的内容:
try {
  setState(() => _isLoading = true); // This could display a loading spinner of sorts.
  await Bloc.of(context).signIn(_usernameController.text, _passwordController.text);
  Navigator.of(context).pushReplacement(...); // Push logged in screen.
} catch (e) {
  setState(() => _isLoading = false);
  // TODO: Display the error on the screen.
}

这样,您就可以很好地分离关注点:

  • BLoC实际上只是按照预期的方式工作–处理业务逻辑(在这种情况下,请登录用户)。
  • UI只关心两件事:
    • 显示来自Stream
    • 的用户数据
    • 通过在BLoC中触发用户操作并根据结果执行UI操作来对用户操作做出反应。²

最后,我想指出的是,这只是一种可能的解决方案,它通过尝试在复杂应用程序中处理状态的不同方式随着时间而发展。 重要的是要了解有关状态管理如何工作的不同观点,因此我鼓励您深入研究该主题,也许是通过观看Google I / O的"Pragmatic State Management in Flutter"会话来进行。


¹尝试为BLoC动作提供多个参数时,它变得更加丑陋–因为这时您需要定义一个包装器类,以便将其传递给Stream。

²我承认启动应用程序时它有点难看:您将需要某种启动屏幕,该启动屏幕仅检查BLoC的流并将用户重定向到基于他/她是否登录。发生该规则的异常是因为用户执行了一个操作(启动应用程序),但是Flutter框架不允许我们直接参与(据我所知,至少不是很优雅)。

答案 3 :(得分:0)

正如felangel在下面的issue中在Github中提到的那样,我们可以为此目的使用BlocListner。

BlocListener(
    bloc: BlocProvider.of<DataBloc>(context),
    listener: (BuildContext context, DataState state) {
        if (state is Success) {              
            Navigator.of(context).pushNamed('/details');
        }              
    },
    child: BlocBuilder(
        bloc: BlocProvider.of<DataBloc>(context),
        builder: (BuildContext context, DataState state) {        
            if (state is Initial) {
                return Text('Press the Button');
            }
            if (state is Loading) {
                return CircularProgressIndicator();
            }  
            if (state is Success) {
                return Text('Success');
            }  
            if (state is Failure) {
                return Text('Failure');
            }
        },
    }
)