Flutter应用中SharedPreferences存储内容首次加载不显示的解决方法
问题原因
核心问题是异步获取SharedPreferences数据后,没有通知Flutter框架触发UI重建:
_fetchData是异步方法,在initState中调用后,数据加载完成时组件已经完成首次渲染,此时给pname/pdesc/pdue赋值不会自动更新UI- 热重载时,数据已经提前加载到变量中,所以渲染时能直接显示值
解决方案
在异步获取数据完成后,调用setState(() {})通知框架更新状态,触发组件重建。
修正后的完整代码
import 'package:flutter/material.dart'; import 'package:mne/Actual%20Tasks/activity_widget.dart'; import 'package:mne/UserTasks/task_widget.dart'; import 'package:shared_preferences/shared_preferences.dart'; class ProjectTask extends StatefulWidget { const ProjectTask({Key key}) : super(key: key); @override State<ProjectTask> createState() => _ProjectTaskState(); } class _ProjectTaskState extends State<ProjectTask> { String pname; String pdesc; String pdue; @override void initState() { super.initState(); _fetchData(); } Future<Null> _fetchData() async { // 注:WidgetsFlutterBinding.ensureInitialized()在initState中不需要,此时Flutter绑定已初始化 SharedPreferences localStorage = await SharedPreferences.getInstance(); // 获取数据后更新状态 setState(() { pname = localStorage.getString('project_name'); pdesc = localStorage.getString('project_desc'); pdue = localStorage.getString('project_due'); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( iconTheme: const IconThemeData(color: Colors.black), backgroundColor: Colors.white, automaticallyImplyLeading: true, centerTitle: true, title: const Text('Project Details', style: TextStyle(color: Colors.black))), body: SingleChildScrollView( child: Column(children: [ Container(height: 10, color: Colors.transparent), // for image Container( width: 330, child: Image.asset('assets/images/projectbanner.png'), ), //for project name Container( padding: const EdgeInsets.only(bottom: 25, top: 15), child: Row(children: [ Container( padding: const EdgeInsets.only(left: 20, right: 145), child: Text(pname ?? '', style: const TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 16)), ), Container( padding: const EdgeInsets.only(right: 10, top: 8), child: const Icon(Icons.calendar_month_outlined)), RichText( text: TextSpan(children: [ const TextSpan( text: 'Due: ', style: TextStyle( fontSize: 12, fontWeight: FontWeight.bold, color: Colors.black)), TextSpan( text: pdue ?? '', style: const TextStyle(fontSize: 12, color: Colors.black)) ])), ])), // for description title Container( alignment: Alignment.centerLeft, padding: const EdgeInsets.only(left: 20, bottom: 20), child: const Text('Description', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16))), // for actual desc Container( padding: const EdgeInsets.only(left: 20), alignment: Alignment.centerLeft, child: Text( pdesc ?? '', style: const TextStyle(color: Colors.grey), )), // for task title Container( padding: const EdgeInsets.only(left: 20, top: 20, bottom: 20), alignment: Alignment.centerLeft, child: const Text('Tasks', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16))), // for task widget Container(height: 630, child: const ActivityWidget()), ]), ), ); } }
优化建议(可选)
可以使用FutureBuilder替代手动管理异步状态,代码更简洁且符合Flutter最佳实践:
// 替换原有的变量和initState、_fetchData方法 @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(/* 保持原代码不变 */), body: FutureBuilder<SharedPreferences>( future: SharedPreferences.getInstance(), builder: (context, snapshot) { if (!snapshot.hasData) { // 数据加载中显示占位符 return const Center(child: CircularProgressIndicator()); } final localStorage = snapshot.data!; final pname = localStorage.getString('project_name'); final pdesc = localStorage.getString('project_desc'); final pdue = localStorage.getString('project_due'); // 原有的SingleChildScrollView内容 return SingleChildScrollView( child: Column(/* 保持原代码结构,使用上面的pname/pdesc/pdue变量 */), ); }, ), ); }
效果说明
- 首次加载问题状态:页面仅显示顶部图片、"Description"和"Tasks"标题,项目名称、截止日期、描述内容均为空
- 修复后效果:页面加载时会自动获取数据并更新UI,直接显示完整的项目详情信息
内容的提问来源于stack exchange,提问作者clio
相关产品推荐
相关产品推荐

