Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Navigate while Widget state build is being executed

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!

like image 685
Husayn Hakeem Avatar asked Jul 27 '19 17:07

Husayn Hakeem


People also ask

Why is the build () method on state and not StatefulWidget?

Why is the build method on State, and not StatefulWidget? Putting a Widget build(BuildContext context) method on State rather than putting a Widget build(BuildContext context, State state) method on StatefulWidget gives developers more flexibility when subclassing StatefulWidget.

How do you deal with unwanted widget build in Flutter?

One of the easiest ways to avoid unwanted reBuilds that are caused usually by calling setState() in order to update only a specific Widget and not refreshing the whole page, is to cut that part of your code and wrap it as an independent Widget in another Stateful class.

What does widget build do in Flutter?

Flutter widgets are built using a modern framework that takes inspiration from React. The central idea is that you build your UI out of widgets. Widgets describe what their view should look like given their current configuration and state.


1 Answers

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.

like image 151
Esen Mehmet Avatar answered Oct 08 '22 02:10

Esen Mehmet