Flutter Secure Storage卸载后数据持久化及跨端适配方案咨询
解决方案:flutter_secure_storage数据丢失及卸载后保留数据问题
核心问题梳理
用户反馈的数据频繁丢失,大概率是两种场景导致:
- 应用更新:签名一致的正常更新不会清除flutter_secure_storage数据,但如果签名变更、iOS端Keychain配置出错,会出现数据无法读取的情况;
- 卸载应用:系统默认会清除Android Keystore、iOS Keychain这类安全存储的数据,这是系统原生行为。
各平台卸载后保留数据的可行方案
Android 端:hasFragileUserData + 外部存储组合
- 启用hasFragileUserData:在
AndroidManifest.xml的<application>标签中添加android:hasFragileUserData="true",用户卸载应用时,系统会弹出提示询问是否保留数据。但这只是引导,用户可以选择不保留,并非强制留存。 - 配合外部存储:把加密后的关键数据存在应用专属外部目录(
getExternalFilesDir),这个目录默认卸载时会被删除,但结合上面的提示,用户选择保留就能留存数据。注意:Android 13+访问该目录不需要额外权限,但数据可能被用户手动删除或清理工具清理。
iOS 端:iCloud Keychain 或自定义云存储
- iCloud Keychain:flutter_secure_storage支持配置iCloud同步,数据会绑定用户的iCloud账号,卸载后重新安装只要登录同一账号就能恢复。配置步骤:
- 在Xcode的
Signing & Capabilities中添加iCloud,勾选Keychain Sharing; - 在
Info.plist中添加keychain-access-groups,值为团队ID加bundle ID(例如$(AppIdentifierPrefix)com.yourapp.id); - 初始化flutter_secure_storage时指定
iCloudOptions:final _storage = const fss.FlutterSecureStorage( iCloudOptions: fss.IcloudOptions( keychainAccessGroup: 'your_team_id.com.yourapp.id', ), );
- 在Xcode的
- 自定义云存储:把加密后的用户数据上传到自有服务器或Firebase等第三方服务,卸载后重新安装时通过用户身份验证(手机号/邮箱登录)拉取数据。这是最可靠的跨平台方案,但需要后端支持。
备选方案:自定义加密文件存储
如果不想依赖系统安全存储或云端,可以自己实现加密文件存储:
- 用AES-256-GCM等强加密算法对数据加密,存储到应用文档目录(Android的
getFilesDir、iOS的Documents),但这个目录卸载时仍会被清除,要留存的话需要结合Android外部存储或iOS iCloud Drive(需配置iCloud能力)。 - 简化版代码示例:
import 'dart:convert'; import 'dart:io'; import 'package:path_provider/path_provider.dart'; import 'package:encrypt/encrypt.dart'; class EncryptedFileStorage { // 注意:密钥绝对不能硬编码!建议通过服务器下发或结合设备安全信息生成 final Key _encryptionKey = Key.fromUtf8('your_32_character_secure_key_here'); final IV _iv = IV.fromLength(16); Future<String> get _documentPath async { final dir = await getApplicationDocumentsDirectory(); return dir.path; } Future<File> _getStorageFile() async { final path = await _documentPath; return File('$path/encrypted_user_data.json'); } Future<void> saveValue({required String key, required String value}) async { final encrypter = Encrypter(AES(_encryptionKey, mode: AESMode.gcm)); final encryptedValue = encrypter.encrypt(value, iv: _iv); final file = await _getStorageFile(); Map<String, String> data = {}; if (await file.exists()) { final content = await file.readAsString(); data = jsonDecode(content); } data[key] = encryptedValue.base64; await file.writeAsString(jsonEncode(data)); } Future<String> getValue({required String key}) async { try { final file = await _getStorageFile(); if (!await file.exists()) return ''; final content = await file.readAsString(); final data = jsonDecode(content) as Map<String, String>; if (!data.containsKey(key)) return ''; final encrypter = Encrypter(AES(_encryptionKey, mode: AESMode.gcm)); final encryptedValue = Encrypted.fromBase64(data[key]!); return encrypter.decrypt(encryptedValue, iv: _iv); } catch (_) { return ''; } } }
现有flutter_secure_storage数据丢失排查建议
- 检查Android应用签名:更新时签名不一致会导致Keystore数据无法访问;
- 检查iOS Keychain配置:bundle ID变更、未开启Keychain Sharing都可能导致数据丢失;
- 加日志埋点:在存储、读取、删除操作的异常捕获里记录日志,方便定位具体丢数据的场景。
内容的提问来源于stack exchange,提问作者Rafael Stefani Baptista
相关产品推荐
相关产品推荐

