Flutter中复选框状态保存后UI不更新问题求助
Flutter复选框状态保存后UI不更新问题
我开发了一个Flutter个人资料页面,保存并重新绘制页面后,其他组件能正常保留新状态,但复选框无法做到这一点。新的复选框值已成功保存到数据库,但UI界面并未保留该状态。例如将邮箱复选框设为true后,该布尔值已保存,但点击提交按钮后前端不会显示这个新状态。
class _ProfilePageState extends State<ProfilePage> { final _formKey = GlobalKey<FormState>(); bool? emailIsChecked = false; bool? smsisChecked = false; bool isTextFieldEnabled = false; bool isIgnored = true; bool isVisible = false; bool editIsVisible = true; bool emailIsEnabled = false; bool smsIsEnabled = false; bool nameEdit = false; late TextEditingController countryController; late TextEditingController languageController; @override void initState() { super.initState(); countryController = TextEditingController(text: globals.signedInUser!.country); languageController = TextEditingController(text: globals.signedInUser!.language); selectedCountry = globals.signedInUser!.country; selectedLanguage = globals.signedInUser!.language; globals.signedInUser!.notifications.forEach( (element) { if ((element['type']) == 'Email') { emailIsChecked = element['status']; } if ((element['type']) == 'SMS') { smsisChecked = element['status']; } }, ); cleanInputStates(); } @override void dispose() { countryController.dispose(); languageController.dispose(); super.dispose(); } checkEditVisibility() { if (editIsVisible == true) { return true; } if (editIsVisible == false) { return false; } } checkEditAvailability() { if (editIsVisible == false) { return true; } if (editIsVisible == true) { return false; } } cleanInputStates() { isTextFieldEnabled = false; isIgnored = true; editIsVisible = true; isVisible = false; smsIsEnabled = false; emailIsEnabled = false; } @override Widget build(BuildContext context) { return Scaffold( body: Form( key: _formKey, child: Row( children: [ Flexible( flex: 5, child: Padding( padding: const EdgeInsets.only(top: 20), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 30, ), Row( mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ Visibility( visible: editIsVisible, maintainSize: true, maintainAnimation: true, maintainState: true, child: ElevatedButton.icon( style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Colors.green)), icon: const Icon(Icons.edit), label: Text('Edit'), onPressed: () { setState(() { isTextFieldEnabled = true; isIgnored = false; isVisible = true; editIsVisible = false; smsIsEnabled = true; emailIsEnabled = true; }); }), ), SizedBox( width: 250, ) ], ), SizedBox( height: 30, ), Flexible( flex: 1, child: Padding( padding: const EdgeInsets.only(top: 20), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 20, ), ... // TextFieldInputs Row( children: [ Container( child: Text( "Country: ", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 22, color: Colors.black.withOpacity(0.3), ), ), ), SizedBox( width: 100, ), Container( width: 300, child: IgnorePointer( ignoring: isIgnored, child: DropdownButtonFormField2( decoration: InputDecoration( isDense: true, contentPadding: EdgeInsets.zero, border: OutlineInputBorder( borderRadius: BorderRadius.circular(15), ), ), isExpanded: true, hint: const Text( 'Select Your Country', style: TextStyle(fontSize: 14), ), icon: const Icon( Icons.arrow_drop_down, color: Colors.black45, ), iconSize: 30, buttonHeight: 60, buttonPadding: const EdgeInsets.only(left: 20, right: 10), dropdownDecoration: BoxDecoration( borderRadius: BorderRadius.circular(15), ), items: countryItems .map((item) => DropdownMenuItem<String>( value: item, child: Text( item, style: const TextStyle( fontSize: 14, ), ), )) .toList(), validator: (valueCountry) { if (valueCountry == null) { return 'Please select country.'; } return null; }, value: selectedCountry, onChanged: (String? valueCountry) { selectedCountry = valueCountry; setState(() { valueCountry; }); }, ), ), ), // ), ], ), SizedBox( height: 20, ), Row( children: [ Container( child: Text( "Language: ", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 22, color: Colors.black.withOpacity(0.3), ), ), ), SizedBox( width: 80, ), Container( width: 300, child: IgnorePointer( ignoring: isIgnored, child: DropdownButtonFormField2( decoration: InputDecoration( isDense: true, contentPadding: EdgeInsets.zero, border: OutlineInputBorder( borderRadius: BorderRadius.circular(15), ), ), isExpanded: true, hint: const Text( 'Select Your Language', style: TextStyle(fontSize: 14), ), icon: const Icon( Icons.arrow_drop_down, color: Colors.black45, ), iconSize: 30, buttonHeight: 60, buttonPadding: const EdgeInsets.only(left: 20, right: 10), dropdownDecoration: BoxDecoration( borderRadius: BorderRadius.circular(15), ), items: languageItems .map((item) => DropdownMenuItem<String>( value: item, child: Text( item, style: const TextStyle( fontSize: 14, ), ), )) .toList(), value: selectedLanguage, validator: (valueLanguage) { if (valueLanguage == null) { return 'Please select language.'; } return null; }, onChanged: (String? valueLanguage) { selectedLanguage = valueLanguage; setState(() { valueLanguage; }); }, ), ), ), // ), ], ), Row() ], ), SizedBox( width: 120, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( child: Row( children: [ Text( "Receive notifications by: ", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 22, color: Colors.black.withOpacity(0.3), ), ), ], ), ), ], ), SizedBox( height: 10, ), Row( children: [ Container( width: 300, child: CheckboxListTile( enabled: emailIsEnabled, title: Text("E-mail"), value: emailIsChecked, onChanged: (bool? newEmailValue) { setState(() { emailIsChecked = newEmailValue; }); }, activeColor: Colors.green, ), ), ], ), Row( children: [ Container( width: 300, child: CheckboxListTile( enabled: smsIsEnabled, title: Text("SMS"), value: smsisChecked, onChanged: (bool? newSmsValue) { setState(() { smsisChecked = newSmsValue; }); }, activeColor: Colors.green, ), ), ], ), SizedBox( height: 100, ), Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Visibility( visible: isVisible, child: Row( children: <Widget>[ ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Colors.red)), onPressed: () { setState(() { cleanInputStates(); _formKey.currentState!.reset(); Navigator.push( context, MaterialPageRoute( builder: (context) => ProfilePage())); }); print("Cleaning states"); }, child: Text("Dismiss"), ), SizedBox( width: 100, ), ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Colors.green)), onPressed: () { if (_formKey.currentState!.validate()) { final userInfo = { "_id": globals.signedInUser!.userId, "firstName": firstnameTextController.text, "lastName": lastnameTextController.text, "email": emailTextController.text, "phoneNumber": phoneNumberTextController.text, "country": selectedCountry.toString(), "language": selectedLanguage.toString(), "notifications": [ {"type": "Email", "status": emailIsChecked}, {"type": "SMS", "status": smsisChecked} ] }; globals.socketController.updateUser(userInfo); Fluttertoast.showToast( msg: "Applying changes.", // message toastLength: Toast.LENGTH_LONG, // length gravity: ToastGravity.BOTTOM_RIGHT, // location timeInSecForIosWeb: 2, webBgColor: "#4caf50", ); } print("DATA IS BEING SAVED"); setState(() { if (_formKey.currentState!.validate()) { globals.signedInUser!.email = emailTextController.text; globals.signedInUser!.phoneNumber = phoneNumberTextController.text; globals.signedInUser!.firstName = firstnameTextController.text; globals.signedInUser!.lastName = lastnameTextController.text; globals.signedInUser!.country = selectedCountry as String; globals.signedInUser!.language = selectedLanguage as String; cleanInputStates(); Navigator.push( context, MaterialPageRoute( builder: (context) => ProfilePage())); } else { print("ingresando else"); } }); }, child: Text("Save"), ), ], ), ) ], ) ], ), ], ), ), ), ], ), //Column ends here ), ), ], ), ), ); } }
问题原因
你在保存后更新了globals.signedInUser的基础信息,但没有更新notifications数组里的对应状态值,导致页面重新初始化时,从globals.signedInUser.notifications读取的还是旧值,所以复选框显示的是之前的状态。另外,你通过Navigator.push重新打开当前页面,会创建新的页面实例,即便触发initState,也因为全局数据未更新而读取旧状态。
修复步骤
1. 更新全局用户的notifications状态
在保存按钮的setState中,同步更新globals.signedInUser.notifications数组:
setState(() { if (_formKey.currentState!.validate()) { // ... 其他字段更新代码 // 同步更新notifications数组 globals.signedInUser!.notifications = [ {"type": "Email", "status": emailIsChecked}, {"type": "SMS", "status": smsisChecked} ]; cleanInputStates(); // 替换Navigator.push为Navigator.pop,避免创建新页面 Navigator.pop(context); } else { print("ingresando else"); } });
2. 优化页面刷新方式
不要通过重新路由刷新页面,直接调用setState刷新当前状态即可:
// 去掉Navigator.push,改为直接刷新 cleanInputStates(); setState(() {});
3. 简化initState中的状态读取
用firstWhere替代forEach遍历,更简洁高效:
@override void initState() { super.initState(); // ... 其他初始化代码 // 获取Email通知状态 emailIsChecked = globals.signedInUser!.notifications.firstWhere( (e) => e['type'] == 'Email', orElse: () => {'status': false}, )['status']; // 获取SMS通知状态 smsisChecked = globals.signedInUser!.notifications.firstWhere( (e) => e['type'] == 'SMS', orElse: () => {'status': false}, )['status']; cleanInputStates(); }
额外建议
- 避免直接操作全局变量,推荐用Provider、Riverpod等状态管理工具管理用户信息,状态变化时自动通知UI更新。
- 尽量不要通过路由跳转刷新页面,直接调用
setState或依赖状态管理的监听实现UI更新,性能更优。
内容的提问来源于stack exchange,提问作者djag07.dev
相关产品推荐
相关产品推荐

