在执行 Widget 状态构建时导航

Hus*_*eem 4 flutter flutter-navigation flutter-provider

我正在构建一个简单的 Flutter 应用程序。它的启动屏幕确定用户是否登录,然后根据它重定向到登录或主/主屏幕。

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

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();
                  },
                ),
              ),
            );
          }
        }
Run Code Online (Sandbox Code Playgroud)

ViewModel 发出 3 种状态之一:

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

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

This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets
Run Code Online (Sandbox Code Playgroud)

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

谢谢!

Ese*_*met 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();
                  },
Run Code Online (Sandbox Code Playgroud)

您必须Widgetbuild方法中与每个孩子一起返回。

或者,您可以使用以下方法执行此操作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();
  }
Run Code Online (Sandbox Code Playgroud)

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

确保你provider没有lifecycle问题。