Flutter中如何实现SharedPreferences的XML导出与导入
shared_preferences官方插件没有提供XML导出/导入的内置API,但该功能完全可以实现,不需要依赖Java侧的原生专属方案,可同时兼容Android、iOS双端。
核心实现思路
Android端原生SharedPreferences本身就是以XML格式存储在应用私有目录下,iOS端对应存储为plist格式。如果需要统一导出为标准XML格式(和安卓原生SP格式兼容),直接在Dart层完成KV数据的XML序列化/反序列化即可,不需要编写大量原生代码;如果仅需要Android端直接导出系统生成的原生SP文件,也可以通过原生桥接直接拷贝对应文件。
前置依赖
在pubspec.yaml中添加以下依赖:
dependencies: shared_preferences: ^2.2.2 xml: ^6.5.0 path_provider: ^2.1.2 permission_handler: ^11.3.0 # 仅导出到外部公共目录时需要
具体实现步骤
1. 导出SharedPreferences为XML文件
实现逻辑:
- 获取SharedPreferences实例,遍历取出所有已存储的KV键值对,支持的类型包括
bool、int、double、String、List<String>,和插件本身支持的类型完全对齐 - 构建和Android原生SP完全一致的XML结构,根节点为
<map>,每个存储项对应一个带类型标识的子节点 - 将生成的XML字符串写入目标路径,导出到应用私有目录无需权限,导出到公共存储目录需先申请对应存储权限
核心实现代码:
import 'package:shared_preferences/shared_preferences.dart'; import 'package:xml/xml.dart'; import 'dart:io'; import 'package:path_provider/path_provider.dart'; Future<File> exportSpToXml() async { final prefs = await SharedPreferences.getInstance(); final xmlBuilder = XmlBuilder(); xmlBuilder.processing('xml', 'version="1.0" encoding="utf-8" standalone="yes" ?>'); xmlBuilder.element('map', nest: () { for (final key in prefs.getKeys()) { final value = prefs.get(key); switch (value.runtimeType) { case bool: xmlBuilder.element('boolean', nest: () { xmlBuilder.attribute('name', key); xmlBuilder.attribute('value', value.toString()); }); break; case int: xmlBuilder.element('int', nest: () { xmlBuilder.attribute('name', key); xmlBuilder.attribute('value', value.toString()); }); break; case double: xmlBuilder.element('float', nest: () { xmlBuilder.attribute('name', key); xmlBuilder.attribute('value', value.toString()); }); break; case String: xmlBuilder.element('string', nest: () { xmlBuilder.attribute('name', key); xmlBuilder.text(value as String); }); break; case List<String>: xmlBuilder.element('set', nest: () { xmlBuilder.attribute('name', key); for (final item in value as List<String>) { xmlBuilder.element('string', nest: item); } }); break; } } }); final xmlDoc = xmlBuilder.buildDocument(); // 此处默认导出到应用文档目录,可按需修改为公共存储路径 final appDir = await getApplicationDocumentsDirectory(); final backupFile = File('${appDir.path}/sp_backup_${DateTime.now().millisecondsSinceEpoch}.xml'); await backupFile.writeAsString(xmlDoc.toXmlString(pretty: true)); return backupFile; }
2. 从XML文件导入还原SharedPreferences
实现逻辑:
- 读取目标XML文件内容,解析为XML文档对象
- 遍历根节点下的所有子节点,根据节点标签识别值类型,读取
name属性作为存储key,解析对应节点值 - 调用SharedPreferences的对应set方法写入所有KV对,完成后执行reload重载使配置生效
核心实现代码:
Future<void> importSpFromXml(File backupXmlFile) async { final prefs = await SharedPreferences.getInstance(); final xmlStr = await backupXmlFile.readAsString(); final xmlDoc = XmlDocument.parse(xmlStr); final rootNode = xmlDoc.rootElement; for (final node in rootNode.childElements) { final storageKey = node.getAttribute('name'); if (storageKey == null) continue; switch (node.name.local) { case 'boolean': final val = node.getAttribute('value')?.toLowerCase() == 'true'; await prefs.setBool(storageKey, val); break; case 'int': final val = int.tryParse(node.getAttribute('value') ?? '0') ?? 0; await prefs.setInt(storageKey, val); break; case 'float': final val = double.tryParse(node.getAttribute('value') ?? '0') ?? 0.0; await prefs.setDouble(storageKey, val); break; case 'string': final val = node.innerText; await prefs.setString(storageKey, val); break; case 'set': final val = node.childElements .where((e) => e.name.local == 'string') .map((e) => e.innerText) .toList(); await prefs.setStringList(storageKey, val); break; } } await prefs.reload(); }
补充说明
- 上述方案生成的XML格式和Android原生SharedPreferences的XML格式完全兼容,原生侧导出的SP文件无需额外转换即可直接导入,Flutter导出的文件也可直接给原生侧使用
- 该方案为Dart层纯逻辑实现,不依赖原生平台专属接口,Android、iOS端均可正常运行
- 如果仅需要Android端直接导出系统自动生成的原生SP文件,可通过MethodChannel调用原生代码,直接拷贝
/data/data/[应用包名]/shared_prefs/目录下的对应XML文件即可,无需手动拼接XML结构,但该方式无法兼容iOS端 - 若需要将文件导出到设备公共存储目录,需根据系统版本申请对应的存储权限,否则会出现文件写入失败问题
内容的提问来源于stack exchange,提问作者William Edward
相关产品推荐
相关产品推荐

