Tobias Eliasson
Tobias Eliasson

Reputation: 215

Why is initState() called twice?

initState() is called twice on the first Widget I route to.

I have removed all method calls and work being done in the initState() method to exclude any possibility that it somehow calls itself. All it does is calling super.initState().

routes.dart:

final routes = {
  '/login' : (BuildContext context) => new LoginPage(),
  '/' : (BuildContext context) => new LoginPage()
};

main.dart:

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Personnel Ledger',
      initialRoute: '/login',
      routes: routes,
      theme: ThemeData(
        scaffoldBackgroundColor: Color(0xFF30778B)
      ),
    );
  }
}

LoginPage.dart:

class LoginPage extends StatefulWidget {
  @override
  LoginPageState createState() => LoginPageState();
}

class LoginPageState extends State<LoginPage> {
  TextEditingController emailTextfieldCtrl;
  TextEditingController passwordTextfieldCtrl;
  AuthHttpService authHttpService;
  bool loggaInDisabled;
  Widget invalidCredentialsText;

  @override
  void initState() {
    super.initState();
    // setInvalidCredentialsTextVisibleWithoutSetState(false);
    // authHttpService = new AuthHttpService();
    // emailTextfieldCtrl = new TextEditingController();
    // passwordTextfieldCtrl = new TextEditingController();
    loggaInDisabled = true;
    // refreshApplicationAccessToken();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomPadding: false,
      body: SafeArea(
        child: Stack(
          children: <Widget>[
            Container(
              decoration: BoxDecoration(
                  gradient: LinearGradient(
                      begin: Alignment.topLeft,
                      end: Alignment.bottomRight,
                      colors: [Color(0xFF30778B), Color(0xFF2F3648)])),
              child: Center(
                child: Stack(
                  children: <Widget>[
                    Column(
                      children: <Widget>[
                        Spacer(
                          flex: 11,
                        ),
                        Flexible(
                          flex: 50,
                          child: Container(
                            child: Image.asset("assets/knowe-logo.png"),
                          ),
                        ),
                      ],
                    ),
                    Column(
                      mainAxisSize: MainAxisSize.min,
                      children: <Widget>[
                        Spacer(
                          flex: 35,
                        ),
                        Flexible(
                          flex: 10,
                          child: Container(
                            child: Row(
                              children: <Widget>[
                                Spacer(
                                  flex: 1,
                                ),
                                Flexible(
                                  flex: 7,
                                  child: Theme(
                                    data:
                                        ThemeData(hintColor: Color(0xFF9E9C9C)),
                                    child: TextField(
                                      style:
                                          TextStyle(color: Color(0xFF9E9C9C)),
                                      cursorColor: Color(0xFF9E9C9C),
                                      controller: emailTextfieldCtrl,
                                      onChanged: emailTextfieldChanged,
                                      decoration: InputDecoration(
                                          prefixIcon: Icon(
                                            Icons.person,
                                            size: 30,
                                            color: Color(0xFF9E9C9C),
                                          ),
                                          labelText: "Email",
                                          border: new UnderlineInputBorder(
                                              borderSide: BorderSide(
                                                  color: Color(0xFF9E9C9C),
                                                  style: BorderStyle.solid,
                                                  width: 2))),
                                    ),
                                  ),
                                ),
                                Spacer(
                                  flex: 1,
                                )
                              ],
                            ),
                          ),
                        ),
                        Flexible(
                          flex: 10,
                          child: Container(
                            margin: EdgeInsets.only(top: 5),
                            child: Row(
                              children: <Widget>[
                                Spacer(
                                  flex: 1,
                                ),
                                Flexible(
                                  flex: 7,
                                  child: Theme(
                                    data:
                                        ThemeData(hintColor: Color(0xFF9E9C9C)),
                                    child: TextField(
                                      obscureText: true,
                                      style:
                                          TextStyle(color: Color(0xFF9E9C9C)),
                                      cursorColor: Color(0xFF9E9C9C),
                                      controller: passwordTextfieldCtrl,
                                      onChanged: passwordTextfieldChanged,
                                      decoration: InputDecoration(
                                          prefixIcon: Icon(
                                            Icons.lock,
                                            size: 26,
                                            color: Color(0xFF9E9C9C),
                                          ),
                                          labelText: "Lösenord",
                                          border: new UnderlineInputBorder(
                                              borderSide: BorderSide(
                                                  color: Color(0xFF9E9C9C),
                                                  style: BorderStyle.solid,
                                                  width: 2))),
                                    ),
                                  ),
                                ),
                                Spacer(flex: 1)
                              ],
                            ),
                          ),
                        ),
                        Flexible(
                            flex: 10,
                            child: Container(
                              child: invalidCredentialsText,
                            )),
                        Flexible(
                          flex: 20,
                          child: Container(
                            margin: EdgeInsets.only(top: 0),
                            child: Row(
                              children: <Widget>[
                                Spacer(
                                  flex: 1,
                                ),
                                Expanded(
                                  flex: 7,
                                  child: Container(
                                    height: 50,
                                    child: RaisedButton(
                                      disabledColor: Color(0xff395A52),
                                      child: Text(
                                        "Logga in",
                                        style: TextStyle(
                                            color: loggaInDisabled
                                                ? Color(0xff7a7a7a)
                                                : Colors.white),
                                      ),
                                      onPressed: loggaInDisabled
                                          ? null
                                          : loggaInPressed,
                                      color: Color(0xff10846D),
                                    ),
                                  ),
                                ),
                                Spacer(flex: 1)
                              ],
                            ),
                          ),
                        ),
                        Container(
                          margin: EdgeInsets.only(top: 7.5),
                          child: InkWell(
                            child: Text(
                              "Glömt lösenord?",
                              style: TextStyle(color: Color(0xFF9E9C9C)),
                            ),
                            onTap: glomtLosenordPressed,
                          ),
                        ),
                        Spacer(
                          flex: 10,
                        ),
                      ],
                    )
                  ],
                ),
              ),
            ),
            Column(
              mainAxisSize: MainAxisSize.max,
              mainAxisAlignment: MainAxisAlignment.end,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Expanded(
                  child: Column(
                    mainAxisSize: MainAxisSize.max,
                    mainAxisAlignment: MainAxisAlignment.end,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Container(
                        alignment: Alignment.bottomLeft,
                        padding: EdgeInsets.only(left: 15, bottom: 10),
                        color: Colors.transparent,
                        child: InkWell(
                          onTap: privacyPolicyTapped,
                          child: Text("Privacy policy",
                              style: TextStyle(
                                  color: Color(0xFF9E9C9C),
                                  fontSize: 15,
                                  decoration: TextDecoration.underline)),
                        ),
                      )
                    ],
                  ),
                )
              ],
            )
          ],
        ),
      ),
    );
  }

  setInvalidCredentialsTextVisible(bool show) {
    setState(() {
      if (show)
        invalidCredentialsText = Row(
          children: <Widget>[
            Spacer(flex: 1),
            Flexible(
              flex: 7,
              child: Column(
                children: <Widget>[
                  Flexible(
                    flex: 4,
                    child: Container(
                      alignment: Alignment.centerLeft,
                      child: Row(
                        children: <Widget>[
                          Flexible(
                            flex: 1,
                            child: Icon(
                              Icons.error,
                              size: 20,
                              color: Color(0xFF9E9C9C),
                            ),
                          ),
                          Flexible(
                            flex: 5,
                            child: Container(
                              padding: EdgeInsets.only(left: 2.5),
                              child: Text(
                                "Invalid email or password",
                                style: TextStyle(color: Color(0xFF9E9C9C)),
                              ),
                            ),
                          )
                        ],
                      ),
                    ),
                  ),
                  Spacer(
                    flex: 2,
                  )
                ],
              ),
            ),
            Spacer(flex: 1)
          ],
        );
      else
        invalidCredentialsText = Row(
          children: <Widget>[
            Spacer(flex: 1),
            Flexible(
              flex: 7,
              child: Column(
                children: <Widget>[
                  Flexible(
                    flex: 3,
                    child: Container(
                      alignment: Alignment.centerLeft,
                      child: Row(
                        children: <Widget>[
                          Flexible(
                            flex: 1,
                            child: Icon(
                              Icons.error,
                              size: 20,
                              color: Color(0x009E9C9C),
                            ),
                          ),
                          Flexible(
                            flex: 5,
                            child: Container(
                              padding: EdgeInsets.only(left: 2.5),
                              child: Text(
                                "",
                                style: TextStyle(color: Color(0xFF9E9C9C)),
                              ),
                            ),
                          )
                        ],
                      ),
                    ),
                  ),
                  Spacer(
                    flex: 2,
                  )
                ],
              ),
            ),
            Spacer(flex: 1)
          ],
        );
    });
  }

  setInvalidCredentialsTextVisibleWithoutSetState(bool show) {
    if (show)
      invalidCredentialsText = Row(
        children: <Widget>[
          Spacer(flex: 1),
          Flexible(
            flex: 7,
            child: Column(
              children: <Widget>[
                Flexible(
                  flex: 4,
                  child: Container(
                    alignment: Alignment.centerLeft,
                    child: Row(
                      children: <Widget>[
                        Flexible(
                          flex: 1,
                          child: Icon(
                            Icons.error,
                            size: 20,
                            color: Color(0xFF9E9C9C),
                          ),
                        ),
                        Flexible(
                          flex: 5,
                          child: Container(
                            padding: EdgeInsets.only(left: 2.5),
                            child: Text(
                              "Invalid email or password",
                              style: TextStyle(color: Color(0xFF9E9C9C)),
                            ),
                          ),
                        )
                      ],
                    ),
                  ),
                ),
                Spacer(
                  flex: 2,
                )
              ],
            ),
          ),
          Spacer(flex: 1)
        ],
      );
    else
      invalidCredentialsText = Row(
        children: <Widget>[
          Spacer(flex: 1),
          Flexible(
            flex: 7,
            child: Column(
              children: <Widget>[
                Flexible(
                  flex: 3,
                  child: Container(
                    alignment: Alignment.centerLeft,
                    child: Row(
                      children: <Widget>[
                        Flexible(
                          flex: 1,
                          child: Icon(
                            Icons.error,
                            size: 20,
                            color: Color(0x009E9C9C),
                          ),
                        ),
                        Flexible(
                          flex: 5,
                          child: Container(
                            padding: EdgeInsets.only(left: 2.5),
                            child: Text(
                              "",
                              style: TextStyle(color: Color(0xFF9E9C9C)),
                            ),
                          ),
                        )
                      ],
                    ),
                  ),
                ),
                Spacer(
                  flex: 2,
                )
              ],
            ),
          ),
          Spacer(flex: 1)
        ],
      );
  }

  privacyPolicyTapped() {
    launch("https://www.knowe.se/policy.html");
  }

  emailTextfieldChanged(String newEmail) {
    setState(() {
      loggaInDisabled = shouldDisableLoggaIn();
    });
  }

  passwordTextfieldChanged(String newPassword) {
    setState(() {
      loggaInDisabled = shouldDisableLoggaIn();
    });
  }

  bool shouldDisableLoggaIn() {
    var email = emailTextfieldCtrl.text;
    var password = passwordTextfieldCtrl.text;
    return !(email != null &&
        email != "" &&
        password != null &&
        password != "");
  }

  loggaInPressed() {
    logIn();
  }

  logIn() {
    var email = emailTextfieldCtrl.text;
    var password = passwordTextfieldCtrl.text;
    var appId = "4d45d54d45d45dd45d45d54d54d54d54";
    var response = authHttpService.logIn(email, password, appId);
    onLoading();
    response
        .then((response) async {
          Navigator.pop(context);
          if (response.statusCode == 200) {
            setInvalidCredentialsTextVisible(false);
            var tokensModel = TokensModel.fromJson(json.decode(response.body));
            final prefs = await SharedPreferences.getInstance();
            prefs.setString("RefreshToken", tokensModel.refreshToken);
            prefs.setString(
                "ApplicationAccessToken", tokensModel.appAccessToken);
            navigateToMainMenuPage();
          } else
            setInvalidCredentialsTextVisible(true);
        })
        .timeout(Duration(seconds: 10))
        .catchError((error) {
          Navigator.pop(context);
          showMessageDialog(
              "Fel vid inloggning", "Kunde inte få kontakt med servern.");
        });
  }

  configureFCM() {
    // var fcmHelper = new FCMHelper();
    // fcmHelper.configureFCM(context);
    // fcmHelper.registerFCMToken(userModel);
  }

  glomtLosenordPressed() {
    navigateToResetPasswordPage();
  }

  refreshApplicationAccessToken() async {
    var prefs = await SharedPreferences.getInstance();
    var refreshToken = prefs.getString("RefreshToken");
    if (refreshToken != null && refreshToken != "") {
      var response =
          authHttpService.refreshApplicationAccessToken(refreshToken);
      onLoading();
      response
          .then((response) async {
            Navigator.pop(context);
            if (response.statusCode == 200) {
              var tokensModel =
                  TokensModel.fromJson(json.decode(response.body));
              var applicationAccessToken = tokensModel.appAccessToken;
              var prefs = await SharedPreferences.getInstance();
              prefs.setString("ApplicationAccessToken", applicationAccessToken);
              navigateToMainMenuPage();
            } else
              showMessageDialog("Inloggningssessionen utgången",
                  "Logga in igen med dina uppgifter.");
          })
          .timeout(Duration(seconds: 10))
          .catchError((error) {
            Navigator.pop(context);
            showMessageDialog(
                "Fel vid inloggning", "Kunde inte få kontakt med servern.");
          });
    }
  }

  navigateToMainMenuPage() {
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => MainMenuPage()),
    );
  }

  navigateToResetPasswordPage() {
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => ResetPasswordPage()),
    );
  }

  void onLoading() {
    showDialog(
      context: context,
      barrierDismissible: false,
      builder: (_) => Scaffold(
          backgroundColor: Color.fromRGBO(0, 0, 0, 0.25),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                Container(
                    width: 40,
                    height: 40,
                    child: CircularProgressIndicator(strokeWidth: 5)),
                Container(
                  margin: EdgeInsets.only(top: 15),
                  child: Text(
                    "Loggar in...",
                    style: TextStyle(color: Colors.white, fontSize: 20),
                  ),
                )
              ],
            ),
          )),
    );
  }

  void showMessageDialog(String title, String body) {
    try {
      showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: new Text(title),
              content: new Text(body),
            );
          });
    } catch (e) {
      print(e.toString());
    }
  }
}

Actual result: initState() method in LoginPage is called twice.

Expected result and goal: initState() should only be called once.

Upvotes: 20

Views: 13651

Answers (4)

Aqib shahzad
Aqib shahzad

Reputation: 747

if you are using Get dependency and GetMaterialApp instead of MaterialApp and initialRoute follow these steps.

  1. clean project
  2. delete the build folder
  3. in terminal => pub get
  4. issue resolved

Upvotes: 0

Joshua Herr
Joshua Herr

Reputation: 47

I had a different root cause for double page loads on all of my flutter routes for web. I'm not sure how this got into the index.html file in the web folder, but if you're seeing everything execute twice all the way from the void main() function through building the MaterialApp, check your index.html file for this javascript link:

<script src="main.dart.js" type="application/javascript"></script>

If you find that in your web/index.html file, delete it.

The indicator that you have that erroneous script reference in your index.html is if you see

Flutter Web Bootstrap: Auto
Flutter Web Bootstrap: Programmatic

in the console.log statements for the app.

Upvotes: 0

Adrian Moisa
Adrian Moisa

Reputation: 4363

I faced the same issue. I'm going to share what I've learned out of my own research.

  • When opening a deep link, the flutter router recreates all the prior router/history states of the app.
  • This means that the back button can return the user trough all the screens needed to get there even if it got there via deep linking.
  • This behavior makes sense for mobile devices. However, on the web, this behavior is not wanted.
  • If we load a deep link such as: http://domain/s/lorem-ipsum/all/posts we end up initialising each route level if it has it's own screen defined
  • If you happen to have the same component on all levels this in turn leads to 5 sets of duplicated API calls which is completely unacceptable
  • Therefore we can use onGenerateInitialRoutes to prevent this behavior from occurring.
  • The default history stack is overridden with a router/history stack with just one state

main.dart

...
return MaterialApp(
  ...
  initialRoute: user != null ? '/' : '/login',
  
  // The default history stack is overridden with a router/history stack with just one state
  // In this case I used fluro but any other onGenerateRoute method will do the trick
  onGenerateInitialRoutes: (initialRoute) =>
      [appRouter.generator(RouteSettings(name: initialRoute))!],
  onGenerateRoute: appRouter.generator,
);

router.dart

final appRouter = FluroRouter();

void defineAppRoutes() {
  final routes = {
    // Auth
    '/login': LoginPage(),
    '/register': RegisterPage(),

    // Pages
    '/': MainFeed(),
    '/s': MainFeed(),
    '/s/:spaceId': MainFeed(),
    '/s/:spaceId/:activityId': MainFeed(),
    '/s/:spaceId/:activityId/posts': MainFeed(),
    ... many more such routes
  };

  routes.forEach((route, page) {
    appRouter.define(route, handler: Handler(
      handlerFunc: (BuildContext? context, Map<String, List<String>> params) =>
      page,
    ));
  });

  // Not found
  appRouter.notFoundHandler = Handler(
      handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
        return NotFoundPage();
      });
}

Performance/Billing concerns

Some of you might be using Firebase and possibly planning to run an app with high volumes of traffic. Therefore it is quite important to pay attention to what your router does. You might be sending requests for more data than what you actually need to load for your first view of the app. Especially for sharing links on the web this might be costly if you don't optimize your API calls. Since Firebase does everything via websockets make sure you either place print statements in all your API calls or you can also switch to json-server during development to get a better view of the HTTP calls in the developer tools. There are many ways to handle this optimisation. What matters first of all is to be aware of your API calls and how many. I was able to generate 15K api calls with 3-4 work hours using an unoptimised version of my app.

I'm still learning some of this stuff so please excuse me if I used some wrong terms. Let me know how can I improve this answer. Hopefully it helps you.

Upvotes: 3

R&#233;mi Rousselet
R&#233;mi Rousselet

Reputation: 277467

This is a combination of two facts:

  • each route is entirely independent. / do not share state with /login, therefore going from / to /login will trigger an initState on the LoginPage from /login.
  • initialRoute: '/login' does not mean that the app starts directly on /login.

With the initialRoute argument to /foo/bar, the app will start with the following route history:

  1. /
  2. /foo
  3. /foo/bar

As such, even though you have an initialRoute property, / is still pushed.

Upvotes: 38

Related Questions