Flutter中SharedPreferences数据无法及时在Widget显示的问题
在Flutter项目中遇到SharedPreferences数据无法在Widget中及时显示的问题:跳转至目标页面后,TextField始终显示默认值,仅重启页面后数据才正常显示。具体场景及代码如下:
1. Provider模型类的SharedPreferences数据获取方法
Future getDataPerson() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); id = prefs.getInt('id') ?? 000; name = prefs.getString('name') ?? "Фамилия Имя Отчество"; phone = prefs.getString('phone') ?? "3231313"; email = prefs.getString('email') ?? ""; accessToken = prefs.getString('accessToken') ?? "нет токенааа"; _login = prefs.getString('login') ?? "нет логинааа"; _password = prefs.getString('password') ?? "нет пароляяя"; notifyListeners(); }
2. Provider初始化方式
body: MultiProvider( providers: [ ChangeNotifierProvider<ApiClient>(create: (_)=>ApiClient()), ChangeNotifierProvider<FinalModel>(create: (_)=>FinalModel()), ], child: FinalScreenState(), ),
3. FinalScreen的initState调用数据获取
@override void initState() { super.initState(); finalModel = Provider.of<FinalModel>(context,listen: false); getDataPerson(); } Future getDataPerson() async{ return await finalModel.getDataPerson(); }
4. TextField绑定数据示例(PhoneWidget)
class PhoneWidget extends StatelessWidget { Widget build(BuildContext context) { var finalModel = Provider.of<FinalModel>(context,listen: true); return Padding(padding: EdgeInsets.only(top: 10, bottom: 10,right: 1,left: 1), child: Consumer<FinalModel>( builder: (context, model, widget) { finalModel.phoneController.text = model.phone; return Center( child: TextField( controller: finalModel.phoneController, enabled: false, decoration: const InputDecoration( labelText: "Телефон", contentPadding: EdgeInsets.only(left: 10, top: 10, bottom: 10), border: OutlineInputBorder(), ), )); }) ); } }
业务流程:登录后存储数据并跳转
登录成功后将API返回数据存入SharedPreferences,再跳转至FinalScreen:
var statusOne =await apiClient.signIn(_loginEmail1.text, _passwordParol1.text); Map<String, dynamic> map = jsonDecode(rawJson); bool status = map['status']; if (status == true) { setDataPerson(); Navigator.pushReplacementNamed(context, 'final');}
setDataPerson存储方法:
void setDataPerson() async { final prefs = await SharedPreferences.getInstance(); var statusOne = await apiClient.signIn(_loginEmail1.text, _passwordParol1.text); var rawJson = AuthModel.fromJson(statusOne.data); await prefs.setInt('id', rawJson.data!.performerId!); await prefs.setString('name', rawJson.data!.fullName!); await prefs.setString('phone', rawJson.data!.phone!); await prefs.setString('accessToken', rawJson.data!.accessToken!); }
问题核心
- 跳转后TextField仅显示默认值,无法读取到SharedPreferences中刚存储的最新数据
- 重启页面后数据显示正常
- 不想使用
listen: true,避免getDataPerson被频繁调用
解决方案
1. 等待SharedPreferences存储完成再跳转
当前登录流程中调用setDataPerson()后立即跳转,但setDataPerson是异步方法,数据尚未写入完成就跳转,导致FinalScreen读取的还是旧数据。修改登录逻辑,等待存储完成再跳转:
if (status == true) { await setDataPerson(); // 等待数据写入完成 Navigator.pushReplacementNamed(context, 'final'); }
2. 优化Provider与Widget的绑定逻辑
不要在Consumer的builder中直接修改controller文本,可在模型类中同步controller与数据:
class FinalModel extends ChangeNotifier { String phone = "3231313"; late TextEditingController phoneController; FinalModel() { phoneController = TextEditingController(text: phone); } Future getDataPerson() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); phone = prefs.getString('phone') ?? "3231313"; // 同步controller文本 phoneController.text = phone; notifyListeners(); } }
Widget中直接使用模型的controller即可,无需在builder中修改:
class PhoneWidget extends StatelessWidget { Widget build(BuildContext context) { return Consumer<FinalModel>( builder: (context, model, widget) { return Padding( padding: EdgeInsets.only(top: 10, bottom: 10, right: 1, left: 1), child: Center( child: TextField( controller: model.phoneController, enabled: false, decoration: const InputDecoration( labelText: "Телефон", contentPadding: EdgeInsets.only(left: 10, top: 10, bottom: 10), border: OutlineInputBorder(), ), ), ), ); }) ); } }
3. 确保数据加载完成后再渲染Widget
在FinalScreen中添加加载状态,等待getDataPerson执行完成后再显示页面内容,避免读取未更新的数据:
class FinalScreenState extends StatefulWidget { @override _FinalScreenStateState createState() => _FinalScreenStateState(); } class _FinalScreenStateState extends State<FinalScreenState> { late FinalModel finalModel; bool _isLoading = true; @override void initState() { super.initState(); finalModel = Provider.of<FinalModel>(context, listen: false); _loadData(); } Future<void> _loadData() async { await finalModel.getDataPerson(); setState(() { _isLoading = false; }); } @override Widget build(BuildContext context) { if (_isLoading) { return Center(child: CircularProgressIndicator()); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ IdWidget(), NameWidget(), PhoneWidget(), EmailWidget(), ], ); } }
内容的提问来源于stack exchange,提问作者N.E.K
相关产品推荐
相关产品推荐

