为什么initState()会被调用两次?

19

在我导航到第一个Widget时,initState()方法被调用了两次。

为了排除它可能自身调用的可能性,我已经删除了initState()方法中所有的方法调用和工作。现在它只是调用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());
    }
  }
}

实际结果: LoginPage中的initState()方法被调用了两次。

期望结果和目标: initState()应该只被调用一次。

4个回答

36

以下是两个事实的结合:

  • 每个路由完全独立。 //login 不共享状态,因此从 //login 会触发在 /login 上的 LoginPageinitState
  • initialRoute: '/login' 并不意味着应用程序直接从 /login 开始。

使用 initialRoute 参数到 /foo/bar,应用程序将从以下路由历史开始:

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

因此,即使您有一个 initialRoute 属性,仍然会推送 /


2
你的回答为我节省了更多时间和精力,谢谢 @Rémi Rousselet - Karrar
当指定初始路由时,为什么除了单个正斜杠之外的任何内容都会出现这种情况? - Karrar
3
在 initState() 代码中是否有一种方法可以检测当前所处的阶段是(1)还是(2),并跳过所有内容,直到进入(3)阶段? - Eradicatore
有没有人可以帮忙回答 https://dev59.com/YXwQtIcB2Jgan1znS75K ? - RedHappyLlama

1
我遇到了相同的问题。我将分享自己的研究成果。
  • 打开深度链接时,Flutter路由器会重新创建应用程序的所有先前路由/历史状态。
  • 这意味着即使是通过深度链接到达,返回按钮也可以将用户返回到需要经过的所有屏幕。
  • 这种行为对于移动设备是有意义的。然而,在Web上,这种行为是不希望的。
  • 如果我们加载一个深度链接,例如:http://domain/s/lorem-ipsum/all/posts,我们将初始化每个路由级别(如果它有自己的屏幕定义)
  • 如果您在所有级别上都有相同的组件,则会导致5组重复的API调用,这是完全不可接受的
  • 因此,我们可以使用onGenerateInitialRoutes来防止发生此行为。
  • 默认历史堆栈被覆盖为仅具有一个状态的路由/历史堆栈

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

性能/计费问题

有些人可能正在使用Firebase,并且可能计划运行高流量的应用程序。因此,注意您的路由器的操作非常重要。您可能会发送比您实际需要加载的第一个应用程序视图更多的数据请求。特别是在Web上共享链接时,如果您不优化API调用,则可能会很昂贵。由于Firebase通过WebSockets执行所有操作,请确保在所有API调用中放置打印语句,或者您还可以在开发过程中切换到json-server以在开发者工具中获取HTTP调用的更好视图。有许多方法可以处理此优化。首先要了解您的API调用以及其数量。我使用我的应用程序未经优化版本在3-4个工作小时内生成了15K个API调用。

我仍在学习这些内容,如果我使用了一些错误的术语,请原谅我。请告诉我如何改善这个答案。希望它对您有所帮助。


0

我在Flutter Web的所有路由上遇到了双重页面加载的不同根本原因。我不确定这个问题是如何进入web文件夹中的index.html文件的,但如果你从void main()函数开始一直到构建MaterialApp时看到所有内容都执行了两次,请检查你的index.html文件是否有以下JavaScript链接:

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

如果你在web/index.html文件中找到了它,请删除它。

你可以通过观察是否出现以下指示来判断你的index.html文件中是否有错误的脚本引用:

Flutter Web Bootstrap: Auto
Flutter Web Bootstrap: Programmatic

在应用程序的 console.log 语句中。


0

如果您正在使用Get依赖项和GetMaterialApp而不是MaterialApp和initialRoute,请按照以下步骤操作。

  1. 清理项目
  2. 删除build文件夹
  3. 在终端中输入 => pub get
  4. 问题解决

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接