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

12 浏览
0 Comments

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

在我路由到第一个小部件时,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 {

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: [

Container(

decoration: BoxDecoration(

gradient: LinearGradient(

begin: Alignment.topLeft,

end: Alignment.bottomRight,

colors: [Color(0xFF30778B), Color(0xFF2F3648)])),

child: Center(

child: Stack(

children: [

Column(

children: [

Spacer(

flex: 11,

),

Flexible(

flex: 50,

child: Container(

child: Image.asset("assets/knowe-logo.png"),

),

),

],

),

Column(

mainAxisSize: MainAxisSize.min,

children: [

Spacer(

flex: 35,

),

Flexible(

flex: 10,

child: Container(

child: Row(

children: [

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: [

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: [

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: [

Expanded(

child: Column(

mainAxisSize: MainAxisSize.max,

mainAxisAlignment: MainAxisAlignment.end,

crossAxisAlignment: CrossAxisAlignment.start,

children: [

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: [

Spacer(flex: 1),

Flexible(

flex: 7,

child: Column(

children: [

Flexible(

flex: 4,

child: Container(

alignment: Alignment.centerLeft,

child: Row(

children: [

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: [

Spacer(flex: 1),

Flexible(

flex: 7,

child: Column(

children: [

Flexible(

flex: 3,

child: Container(

alignment: Alignment.centerLeft,

child: Row(

children: [

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: [

Spacer(flex: 1),

Flexible(

flex: 7,

child: Column(

children: [

Flexible(

flex: 4,

child: Container(

alignment: Alignment.centerLeft,

child: Row(

children: [

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: [

Spacer(flex: 1),

Flexible(

flex: 7,

child: Column(

children: [

Flexible(

flex: 3,

child: Container(

alignment: Alignment.centerLeft,

child: Row(

children: [

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: [

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()应该只被调用一次。

0
0 Comments

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

当打开一个深层链接时,Flutter路由器会重新创建应用的所有先前路由/历史状态。

这意味着即使通过深层链接到达某个界面,返回按钮也可以将用户带回到之前的所有屏幕。

这种行为在移动设备上是有意义的。然而,在Web上,这种行为是不被期望的。

如果我们加载一个深层链接,比如:http://domain/s/lorem-ipsum/all/posts,那么如果每个路由级别都有自己定义的屏幕,就会初始化每个层级。

如果你碰巧在所有层级上都有相同的组件,这将导致5组重复的API调用,这是完全不能接受的。

因此,我们可以使用onGenerateInitialRoutes来阻止发生这种行为。

默认的历史堆栈被一个只有一个状态的路由/历史堆栈覆盖。

在这个例子中,我使用了fluro,但是任何其他的onGenerateRoute方法都可以达到同样的效果。

main.dart中:

...

return MaterialApp(

...

initialRoute: user != null ? '/' : '/login',

// 默认的历史堆栈被一个只有一个状态的路由/历史堆栈覆盖

// 在这个例子中我使用了fluro,但是任何其他的onGenerateRoute方法都可以达到同样的效果

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

... 其他很多路由

};

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
0 Comments

问题出现的原因是在index.html文件中引入了一个错误的javascript链接。解决方法是删除index.html文件中的错误引用。

0
0 Comments

这个问题的出现是由以下两个事实组合而成的:

1. 每个路由都是完全独立的。"/"与"/login"不共享状态,因此从"/"跳转到"/login"会触发"/login"中的initState。

2. "initialRoute: '/login'"并不意味着应用程序直接启动在"/login"上。

使用"initialRoute"参数设置为"/foo/bar"时,应用程序将以以下路由历史开始:

1. "/"

2. "/foo"

3. "/foo/bar"

因此,即使有"initialRoute"属性,"/"仍然被推入堆栈。

当指定初始路由时,除了单个斜杠之外,为什么会发生这种情况呢?

在initState()代码中是否有一种方法可以检测出你处于(1)还是(2),并跳过一切直到达到(3)?

是否有办法来回答这个问题?请查看stackoverflow.com/questions/74552940/...。

0