Navigate while Widget state build is being executed

Issue

I’m building a simple Flutter app. Its launch screen determines if the user if logged in or not, and depending on that redirects to the login or main/home screen afterwards.

My Launch screen is a StatefulWidget, its state is shown below. It uses a ViewModel class that extends ChangeNotifier (its code is irrelevant, so I didn’t include it).

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

The ViewModel emits one of 3 states:

  • LaunchInitial: Default state.
  • LaunchLoginPage: Indicates that the Login page should be displayed.
  • LaunchMainPage: Indicates that the Main page should be displayed.

The LaunchInitial state is handled fine, and a progress bar is displayed on the screen. But the other 2 states cause the app to crash. The following error is thrown:

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

It seems that trying to redirect to another screen while the Consumer’s build method is being executed is causing this issue. What’s the correct way to do this?

Thanks!

Solution

You can’t directly call Navigator within widget tree. If you have event-state builder, so better change the widget tree you are rendering:

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

You have to return Widget with each child inside build method.

Alternatively, you can do this with 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 method will be called right after the build method completed and you can navigate inside.

Be sure your provider don’t have lifecycle issue.

Answered By – Esen Mehmet

Answer Checked By – Senaida (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *