flutter - setState 不更新 TextFormField 小部件的 initialValue
问题描述
我对 Flutter 完全陌生。我试图制作一个带有记住凭据的选项的登录表单。
由于SharedPreferences
是异步的,它使这个任务有点棘手:我创建了一个异步方法 - 在构造函数中启动 - 以异步获取SharedPreferences
实例,读取一些值,然后调用setState()
写入新状态1。
但是当setState()
被调用并写入新状态时,UI 不会更新(即文本字段保持空白)。
该build()
方法类似于:
@override
Widget build(BuildContext context) {
developer.log("build called: rememberCredentials=$rememberCredentials, username=$username, password=$password");
return (... TextFormField(
initialValue: username,
onChanged: (String value) {username = value;}
) ...);
}
我添加了几个developer.log()
调用来查看何时build()
调用,以及是否实际读取了保存的首选项(它们确实读取了):
[log] build called: rememberCredentials=false, username=, password=
[log] read prefs: rememberCredentials=true, username=john, password=1234
[log] build called: rememberCredentials=true, username=john, password=1234
[log] build called: rememberCredentials=true, username=john, password=1234
这是相关代码:
class LoginForm extends StatefulWidget {
const LoginForm({Key? key}) : super(key: key);
@override
_LoginFormState createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
String username = "";
String password = "";
bool rememberCredentials = false;
_LoginFormState() {
readSavedCredentials();
}
void readSavedCredentials() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
rememberCredentials = prefs.getBool("rememberCredentials") ?? false;
username = prefs.getString("username") ?? "";
password = prefs.getString("password") ?? "";
developer.log("read prefs: rememberCredentials=$rememberCredentials, username=$username, password=$password");
});
}
void login() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setBool("rememberCredentials", rememberCredentials);
await prefs.setString("username", rememberCredentials ? username : "");
await prefs.setString("password", rememberCredentials ? password : "");
final resp = await http.get(
Uri.parse('https://127.0.0.1:56873/?action=login&user=$username&pass=${md5hex(password)}')
);
if(resp.statusCode == 200) {
Map o = jsonDecode(resp.body);
if(o["status"] == "AUTH_OK") {
developer.log(jsonEncode(o));
Navigator.push(
context,
MaterialPageRoute(builder: (context) => FilesView(o["base_url"], o["files"])),
);
} else {
// TODO: report error
}
} else {
// TODO: report error
}
}
@override
Widget build(BuildContext context) {
developer.log("build called: rememberCredentials=$rememberCredentials, username=$username, password=$password");
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(title: Text(title())),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.all(15),
child: TextFormField(
initialValue: username,
onChanged: (String value) {
username = value;
},
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Username',
)
)
),
Padding(
padding: EdgeInsets.all(15),
child: TextFormField(
initialValue: password,
onChanged: (String value) {
password = value;
},
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
)
)
),
Padding(
padding: EdgeInsets.fromLTRB(0, 10, 0, 30),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Checkbox(
value: rememberCredentials,
onChanged: (bool? value) {
setState(() {
rememberCredentials = value!;
});
}),
Text('Remember credentials'),
],
),
),
Container(
width: 220,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(20),
),
child: TextButton(
onPressed: () {
login();
},
child: Padding(
padding: EdgeInsets.all(10),
child: Text(
'Login',
style: TextStyle(color: Colors.white),
),
),
),
),
]
)
);
}
}
由于我对 Flutter 完全陌生,因此我可能犯了几个错误。非常欢迎任何关于设计的建议:)
1:另一种选择是使用FutureBuilder
,但它更麻烦,并且会延迟 UI 创建...我更喜欢尽快显示 UI,然后更改字段值。
解决方案
您可以使用文本编辑控制器,而不是使用字符串用户名来保存您的值并使用 onChanged 来更改它
String username = '';
变成
TextEditingController usernameController = TextEditingController();
然后在你的 TextFormFields
TextFormField(
initialValue: username,
onChanged: (String value) {
username = value;
},
...
)
变成
TextFormField(
controller: usernameController,
...
)
最后,在您的 readSavedCredentials() 方法中,您可以使用文本编辑控制器设置文本(用户名)
void readSavedCredentials() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
...
usernameController.text = prefs.getString("username") ?? "";
...
});
}
推荐阅读
- r - as.POSIXlt.numeric(x, tz = tz(x)) 中的错误:必须提供“原点”
- node.js - AWS SES 中的电子邮件问题
- sonarqube - Sonarqube git ci 推送拒绝
- angular - 存储价值不改变ngrx
- jquery - 使用 jquery 添加类但不工作
- wordpress - blogdesigner插件博客设计模板问题
- php - 如何将热门帖子限制为持续 6 个月
- c# - 前两次公开时间有什么问题?
- c - 如何只使用一个函数 showCnt() 来显示结构数组的成员?如何传递参数?
- c# - 带有 Cosmos DB 输入的 Azure 函数 HttpTrigger