Flutter中使用Shared Preferences本地持久化数据失败问题排查
问题背景
需求:在Flutter应用中通过Shared Preferences实现本地数据持久化,确保重启应用后数据不会丢失。
问题:已编写Home页(用于添加/管理数据)和Info页(展示数据详情),但使用Shared Preferences保存数据后,重启应用无法获取到之前保存的值。
核心问题分析
- 生命周期方法错误:Home页的
dispose方法被错误嵌套在initState内部,导致初始化逻辑执行异常,无法正确读取本地存储的数据。 - 数据存储方式错误:当前代码仅单独存储单个商品名称和价格,而非存储整个商品列表,无法在重启后恢复完整的商品数据集合。
- StatelessWidget误用生命周期:Info页是StatelessWidget,却尝试重写
initState方法,该方法仅适用于StatefulWidget,因此无法执行数据读取逻辑。 - 删除数据未同步本地存储:删除商品时仅移除内存中的列表项,未同步更新Shared Preferences中的数据,导致重启后已删除的数据重新出现。
修复步骤及代码修改
1. 修正Home页的生命周期方法
将dispose方法从initState中移出,作为_MyhomeState的独立方法,并新增列表加载/保存逻辑:
import 'package:flutter/material.dart'; import 'package:list_ex/product.dart'; import 'package:sizer/sizer.dart'; import 'package:list_ex/info.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:convert'; class Myhome extends StatefulWidget { const Myhome({Key? key}) : super(key: key); @override State<Myhome> createState() => _MyhomeState(); } class _MyhomeState extends State<Myhome> { List <Data> productdata = []; final myController = TextEditingController(); TextEditingController productController = TextEditingController(); TextEditingController prizeController = TextEditingController(); late SharedPreferences sharedPreferences; @override void initState() { super.initState(); _loadSavedProducts(); } // 独立的资源释放方法 @override void dispose() { myController.dispose(); productController.dispose(); prizeController.dispose(); super.dispose(); } // 从本地加载保存的商品列表 Future<void> _loadSavedProducts() async { sharedPreferences = await SharedPreferences.getInstance(); String? productsJson = sharedPreferences.getString('products'); if (productsJson != null) { List<dynamic> productsList = jsonDecode(productsJson); setState(() { productdata = productsList.map((item) => Data(item['product'], item['prize'])).toList(); }); } } // 将商品列表保存到本地 Future<void> _saveProducts() async { List<Map<String, dynamic>> productsMap = productdata.map((product) => { 'product': product.product, 'prize': product.prize, }).toList(); String productsJson = jsonEncode(productsMap); await sharedPreferences.setString('products', productsJson); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Products', style: TextStyle(fontSize: 30.0)), centerTitle: true, backgroundColor: Colors.grey[800], actions: [ IconButton(onPressed: () { showDialog(context: context, builder: (context) => Dialog( child: SizedBox( height: 200, width: 200, child: Column( children: [ TextFormField( decoration: const InputDecoration( filled: true, labelText: 'product', icon: Icon(Icons.star), ), controller: productController, validator: (value){ if(value == null || value.isEmpty){ return 'Enter product name'; } return null; }, ), const Divider(height: 20.0, color: Colors.grey), TextFormField( decoration: const InputDecoration( filled: true, labelText: 'price', icon: Icon(Icons.star), ), keyboardType: TextInputType.number, controller: prizeController, ), ElevatedButton(onPressed: () { if (productController.text.isEmpty || prizeController.text.isEmpty){ showDialog(context: context, builder: (context) => const AlertDialog( title: Text('Enter Value'), )); } else{ setState(() { productdata.add(Data(productController.text, prizeController.text)); productController.text = ""; prizeController.text = ""; Navigator.of(context).pop(); }); _saveProducts(); // 添加后同步保存 } }, child: const Text('Submit')), ], ), ), ), ); }, icon: const Icon(Icons.add)) ], ), drawer: Drawer( child: ListView( padding: EdgeInsets.zero, children: const <Widget>[ UserAccountsDrawerHeader( decoration: BoxDecoration(color: Colors.black45), accountName: Text('Raj'), accountEmail: Text('abc123@gmail.com'), currentAccountPicture: CircleAvatar( backgroundColor: Colors.orange, child: Text('R', style: TextStyle(fontSize: 40)), ), ), ListTile( leading: Icon(Icons.home), title: Text('Home'), onTap: () {}, ), ListTile( leading: Icon(Icons.contact_mail), title: Text('Contact Us'), onTap: () {}, ), ], ), ), body: ListView.builder( itemCount: productdata.length, itemBuilder: (BuildContext context, int index) { return Row( mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ Expanded( child: ListTile( tileColor: Colors.cyan, leading: const Icon(Icons.star), trailing: IconButton(onPressed: () { showDialog(context: context, builder: (context) => AlertDialog( title: const Text('Delete this?'), content: const Text('Are you sure?'), actions: <Widget>[ TextButton(onPressed: () { Navigator.pop(context); }, child: const Text('Cancel')), TextButton(onPressed: () { setState(() { productdata.remove(productdata[index]); Navigator.pop(context); }); _saveProducts(); // 删除后同步保存 }, child: const Text('Delete', style: TextStyle( color: Colors.black87, fontSize: 16, ),)) ], )); }, icon: const Icon(Icons.delete)), title: Text(productdata[index].product!, style: const TextStyle( fontSize: 22, fontWeight: FontWeight.bold, )), onTap: () { Navigator.of(context).push(MaterialPageRoute( builder: (context) => Info(value: productdata[index]))); }, )), ], ); }), ); } }
2. 修正Info页的无效逻辑
移除StatelessWidget中无效的initState方法,直接使用传递的参数展示数据:
import 'package:flutter/material.dart'; import 'package:list_ex/product.dart'; class Info extends StatelessWidget { final Data value; const Info({Key? key, required this.value}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Product Info'), centerTitle: true, backgroundColor: Colors.grey[800], ), body: Center( child: Card( color: Colors.cyan, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20.0)), child: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ ListTile( title: Text('Product Name: ${value.product!}', style: const TextStyle(fontSize: 20)), subtitle: Text('Price: ${value.prize!}', style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold )), ), ], ), ), ) ); } }
3. 确保Data类支持序列化
修改product.dart中的Data类,使其可以转换为JSON格式:
class Data { String? product; String? prize; Data(this.product, this.prize); // 从JSON解析Data对象 Data.fromJson(Map<String, dynamic> json) { product = json['product']; prize = json['prize']; } // 将Data对象转换为JSON Map<String, dynamic> toJson() { final Map<String, dynamic> data = <String, dynamic>{}; data['product'] = product; data['prize'] = prize; return data; } }
内容的提问来源于stack exchange,提问作者Raj Kambli
相关产品推荐
相关产品推荐

