Alexi Coard
Alexi Coard

Reputation: 7752

Flutter Redirect to a page on initState

I have an application where you need to log in to continue (for example with Google).

I would like to redirect the user when the authentification is needed.

However when I run a Navigator.of(context).pushNamed("myroute"). I got the following error:

 ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 5624): The following assertion was thrown building _ModalScopeStatus(active):
I/flutter ( 5624): setState() or markNeedsBuild() called during build.
I/flutter ( 5624): This Overlay widget cannot be marked as needing to build because the framework is already in the
I/flutter ( 5624): process of building widgets. A widget can be marked as needing to be built during the build phase
I/flutter ( 5624): only if one of its ancestors is currently building. This exception is allowed because the framework
I/flutter ( 5624): builds parent widgets before children, which means a dirty descendant will always be built.
I/flutter ( 5624): Otherwise, the framework might not visit this widget during this build phase.

Here is a sample code

void main() {
    runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
      routes: <String, WidgetBuilder> {
        "login" : (BuildContext context) => new LoginPage(),
      }
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  int _counter = 0;

    @override
    void initState() {
      super.initState();

      if(!isLoggedIn) {
        print("not logged in, going to login page");
        Navigator.of(context).pushNamed("login");
      }

    }


  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  void test() {
    print("hello");
  }

  @override
  Widget build(BuildContext context) {

    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new Text(
          'Button tapped $_counter time${ _counter == 1 ? '' : 's' }.',
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: new Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

}

class LoginPage extends StatefulWidget {
  LoginPage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _LoginPageState createState() => new _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  @override
  Widget build(BuildContext context) {
    print("building login page");
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sign up / Log In"),
      ),
      ),
    );
  }
}

I guess I am doing something wrong, maybe aborting a widget build is causing this. However,, how can I achieve this.

Basically : "I go on my page, if not logged, go to Login page"

Thanks, Alexi

Upvotes: 54

Views: 56626

Answers (4)

CopsOnRoad
CopsOnRoad

Reputation: 268304

Override initState() funtion and use any of the following:

  • Future:

    Future(() {
      Navigator.of(context).pushNamed('login');
    });
    
  • Timer:

    Timer.run(() { // import 'dart:async:
      Navigator.of(context).pushNamed('login');
    });
    

Upvotes: 23

bouraine
bouraine

Reputation: 61

You can also do:

scheduleMicrotask(() => Navigator.of(context).push(MaterialPageRoute(builder: (context) => YourComponent())));

Upvotes: 2

StephanC
StephanC

Reputation: 97

Another approach is to perform the login check before a new page, that needs authentication, is opened. The main page is reserved as a "welcome"/info page and when user tap on a menu item the login check is performed.

Logged in: New page is opened. Logged out: Login page is opened.

Works for me :)

Upvotes: 4

Collin Jackson
Collin Jackson

Reputation: 116828

Try wrapping your Navigator call:

Navigator.of(context).pushNamed("login");

in a callback that is scheduled with addPostFrameCallback:

SchedulerBinding.instance.addPostFrameCallback((_) {
  Navigator.of(context).pushNamed("login");
});

You'll need this import at the top of your file:

import 'package:flutter/scheduler.dart';

As an alternative, consider if you could just have MyHomePage's build() method return a LoginPage instead of a Scaffold if the user isn't logged in. That will probably interact better with the back button since you don't want the user backing out of the login dialog before they are done logging in.

Upvotes: 89

Related Questions