在 Widget 状态构建执行时进行导航

5

我正在构建一个简单的Flutter应用程序。它的启动画面决定用户是否已登录,根据情况重定向到登录或主屏幕。

我的启动画面是一个 StatefulWidget,其状态如下所示。它使用扩展了 ChangeNotifier 的 ViewModel 类(它的代码无关紧要,因此我没有包含它)。

class _LaunchPageState extends State<LaunchPage> {
          LaunchViewModel _viewModel = LaunchViewModel();

          @override
          void initState() {
            super.initState();
            _viewModel.checkSessionStatus();
          }

          @override
          Widget build(BuildContext context) {
            return ChangeNotifierProvider<LaunchViewModel>(
              builder: (_) => _viewModel,
              child: Scaffold(
                body: Consumer<LaunchViewModel>(
                  builder: (context, viewModel, _) {
                    if (viewModel.state is LaunchInitial) {
                      return CircularProgressIndicator();
                    }
                    if (viewModel.state is LaunchLoginPage) {
                      Navigator.pushNamed(context, "login");
                    }
                    if (viewModel.state is LaunchMainPage) {
                      Navigator.pushNamed(context, "main");
                    }
                    return Container();
                  },
                ),
              ),
            );
          }
        }

ViewModel会发出3种状态之一:

  • LaunchInitial: 默认状态。
  • LaunchLoginPage: 表示应该显示登录页面。
  • LaunchMainPage: 表示应该显示主页面。

处理LaunchInitial状态很好,屏幕上会显示进度条。但是其他两个状态会导致应用程序崩溃。将抛出以下错误:

This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets

似乎在执行消费者的build方法时尝试重定向到另一个屏幕会导致此问题。正确的方法是什么?
谢谢!
1个回答

6

您不能直接在小部件树中调用 Navigator。如果您有 event-state 建造者,则最好更改您正在呈现的小部件树:

builder: (context, viewModel, _) {
                    if (viewModel.state is LaunchInitial) {
                      return CircularProgressIndicator();
                    }
                    if (viewModel.state is LaunchLoginPage) {
                      return LoginPage();
                    }
                    if (viewModel.state is LaunchMainPage) {
                      return MainPage();
                    }
                    return Container();
                  },

build方法中,您必须返回具有每个子组件的Widget

或者,您可以使用Navigation来实现此目的:

  @override
  void didChangeDependencies() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (viewModel.state is LaunchLoginPage) {
        Navigator.pushNamed(context, "login");
      }
      if (viewModel.state is LaunchMainPage) {
        Navigator.pushNamed(context, "main");
      }
    });
    super.didChangeDependencies();
  }

addPostFrameCallback方法会在build方法完成后立即调用,您可以在其中进行导航。

确保您的provider没有lifecycle问题。


但我想要离开启动屏幕,转而进入登录或主屏幕,而不仅仅是替换启动屏幕的内容。 - Husayn Hakeem
1
啊,所以导航是在小部件构建之后发生的,看起来是一个不错的方法。谢谢! - Husayn Hakeem
1
在帖子框架回调中返回进度小部件是无用的,应该将其移除。这应该在build()方法中完成(而且不需要任何资格证明)。 - Ber
@Ber 你说得对。谢谢提醒,可能我直接复制粘贴了代码,不管怎样,这是误导性的。 - Mehmet Esen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接