Flutter中使用shared_preferences本地保存对象列表失败求助
解决Flutter红外应用本地持久化数据丢失问题
问题描述
我开发了一款可发送红外(IR)信号的Flutter应用,支持自行添加IR信号,信号存储在名为Item的对象列表中。希望将信号名称与内容在设备本地持久化,但添加新信号后重启应用,数据会丢失,应用恢复初始状态。相关代码如下:
import 'package:flutter/material.dart'; import 'package:ir_sensor_plugin/ir_sensor_plugin.dart'; import 'package:flutter/services.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:async'; import 'dart:convert'; class Item { String buttonName = "", hexCode = ""; bool isSelected = false; Item({ required this.buttonName, required this.hexCode, }); // Method to convert the Item object to a JSON object Map<String, dynamic> toJson() { return { 'buttonName': buttonName, 'hexCode': hexCode, 'isSelected': isSelected, }; } // Method to convert a JSON object to an Item object static Item fromJson(Map<String, dynamic> json) { return Item( buttonName: json['buttonName'], hexCode: json['hexCode'], ); } } class IrRemoteEmulator extends StatefulWidget { const IrRemoteEmulator({Key? key}) : super(key: key); @override State<IrRemoteEmulator> createState() => _IrRemoteEmulatorState(); } class _IrRemoteEmulatorState extends State<IrRemoteEmulator> { String buttonName = ""; TextEditingController _buttonName = TextEditingController(); String hexCode = ""; TextEditingController _hexCode = TextEditingController(); List<Item> items = [ Item( buttonName: "Samsung Power", hexCode: "0000 006d 0022 0003 00a9 00a8 0015 003f 0015 003f 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003f 0015 003f 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0040 0015 0015 0015 003f 0015 003f 0015 003f 0015 003f 0015 003f 0015 003f 0015 0702 00a9 00a8 0015 0015 0015 0e6e"), Item( buttonName: "Prowise Power", hexCode: "0000 006d 0022 0003 00a9 00a8 0015 003f 0015 003f 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003f 0015 003f 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0040 0015 0015 0015 003f 0015 003f 0015 003f 0015 003f 0015 003f 0015 003f 0015 0702 00a9 00a8 0015 0015 0015 0e6e") ]; // Method to save the items list to the local storage Future<void> saveList(List<Item> items) async { // Get the instance of the shared preferences final prefs = await SharedPreferences.getInstance(); // Convert the items list to a JSON string var itemsJson = json.encode(items); // Save the JSON string to the local storage prefs.setString('items', itemsJson); debugPrint("Items saved"); } Future<List<Item>> loadList() async { // Get the instance of the shared preferences final prefs = await SharedPreferences.getInstance(); // Retrieve the JSON string from the local storage var itemsJson = prefs.getString('items') ?? ''; // Convert the JSON string to a List<Item> object var items = json.decode(itemsJson); debugPrint("Items loaded"); return items; } String selectedHexcode = "0000 006d 0022 0003 00a9 00a8 0015 003f 0015 003f 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003f 0015 003f 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0040 0015 0015 0015 003f 0015 003f 0015 003f 0015 003f 0015 003f 0015 003f 0015 0702 00a9 00a8 0015 0015 0015 0e6e"; String _platformVersion = 'Unkown'; bool _hasIrEmitter = false; String _getCarrierFrequencies = 'Unknown'; @override void initState() { super.initState(); // Load the saved items list from the local storage loadList().then((savedItems) { // Set the items list to the loaded list setState(() { items = savedItems; }); }); initPlatformState(); } // Platform messages are asynchronous, so we initialize in an async method. Future<void> initPlatformState() async { String platformVersion; bool hasIrEmitter; String getCarrierFrequencies; // Platform messages may fail, so we use a try/catch PlatformException. try { platformVersion = await IrSensorPlugin.platformVersion; hasIrEmitter = await IrSensorPlugin.hasIrEmitter; getCarrierFrequencies = await IrSensorPlugin.getCarrierFrequencies; } on PlatformException { platformVersion = 'Failed to get data in a platform.'; hasIrEmitter = false; getCarrierFrequencies = 'None'; } // If the widget was removed from the tree while the asynchronous platform // message was in flight, we want to discard the reply rather than calling // setState to update our non-existent appearance. if (!mounted) return; setState(() { _platformVersion = platformVersion; _hasIrEmitter = hasIrEmitter; _getCarrierFrequencies = getCarrierFrequencies; }); } @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( body: Center( child: Column( children: [ const SizedBox( height: 25, ), const SizedBox( height: 15.0, ), Container( decoration: BoxDecoration(border: Border.all(color: Colors.white)), child: Padding( padding: const EdgeInsets.all(8.0), child: Column( children: [ FloatingActionButton.large( onPressed: () async { final String result = await IrSensorPlugin.transmitString( pattern: selectedHexcode); debugPrint('Emitting List Int Signal: $result'); }, child: const Icon(Icons.power_settings_new_outlined), ), ], ), ), ), Expanded( child: SizedBox( height: 200, child: ListView.builder( itemCount: items.length, itemBuilder: (context, index) { return ListTile( onTap: () { items[index].isSelected = true; selectedHexcode = items[index].hexCode; debugPrint(items[index].hexCode); setState(() {}); }, onLongPress: () { items.remove(items[index]); saveList(items); setState(() {}); }, title: Text(items[index].buttonName), leading: Icon((items[index].hexCode == selectedHexcode) ? Icons.circle : Icons.circle_outlined), ); }, ), ), ), ], ), ), floatingActionButton: FloatingActionButton.extended( onPressed: () => showDialog( context: context, builder: (context) => AlertDialog( title: const Text("Add IR Hexcode"), content: Column( children: <Widget>[ TextFormField( controller: _buttonName, decoration: InputDecoration( labelText: "Remotebutton name", hintText: "Ac Powerbutton", hintStyle: TextStyle(color: Colors.grey[300])), ), TextFormField( controller: _hexCode, decoration: InputDecoration( labelText: "Hexcode", hintText: "0000 006d 0022 0003 00a9 00a8 0015 003f 0015 003f 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003f 0015 003f 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0040 0015 0015 0015 003f 0015 003f 0015 003f 0015 003f 0015 003f 0015 003f 0015 0702 00a9 00a8 0015 0015 0015 0e6e", hintStyle: TextStyle(color: Colors.grey[300])), ), ], ), actions: [ TextButton( child: const Text("Cancel"), onPressed: () => Navigator.pop(context), ), TextButton( child: const Text("Ok"), onPressed: () { if (_hexCode.text.isEmpty || _buttonName.text.isEmpty) { debugPrint("${(items.length)}"); } else { items.add(Item( buttonName: _buttonName.text, hexCode: _hexCode.text, )); saveList(items); debugPrint("${(items)}"); } Navigator.pop(context); setState(() {}); return; }, ), ], ), ), label: const Text("Add IR Hexcode"), icon: const Icon(Icons.add), ), ), ); } }
问题根源
- JSON序列化错误:
saveList中直接调用json.encode(items),但json.encode无法自动将自定义Item对象序列化为JSON,导致存储的内容无效。 - 反序列化逻辑缺失:
loadList中json.decode(itemsJson)返回的是原始JSON数组(List - 空值处理不当:首次启动时SharedPreferences中无
items数据,json.decode('')会抛出异常,导致加载逻辑失败,应用继续使用硬编码的初始列表。
修复方案
1. 修正saveList方法
手动遍历Item列表,调用每个对象的toJson()方法转为JSON Map后再序列化:
Future<void> saveList(List<Item> items) async { final prefs = await SharedPreferences.getInstance(); // 将每个Item转为JSON Map,再组成列表 final itemsJsonList = items.map((item) => item.toJson()).toList(); final itemsJson = json.encode(itemsJsonList); await prefs.setString('items', itemsJson); debugPrint("Items saved"); }
2. 完善loadList方法
添加空值判断和异常捕获,将解析后的JSON数组转为Item对象列表,无数据时返回默认初始列表:
Future<List<Item>> loadList() async { final prefs = await SharedPreferences.getInstance(); final itemsJson = prefs.getString('items'); // 无存储数据时返回默认初始列表 if (itemsJson == null || itemsJson.isEmpty) { return [ Item( buttonName: "Samsung Power", hexCode: "0000 006d 0022 0003 00a9 00a8 0015 003f 0015 003f 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003f 0015 003f 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003f 0015 0015 0015
相关产品推荐
相关产品推荐

