如何用Flutter创建应用卸载后仍留存的不可删除文件?
Flutter实现应用卸载后文件留存的方案
针对你的问题,分Android和iOS平台分别给出可行方案,同时结合你存储设备唯一ID的需求给出优化建议:
Android 平台:公共外部存储
Android的应用私有外部存储(getExternalStorageDirectory()返回的路径)会随应用卸载被清理,要留存文件必须存到系统公共外部存储目录(如Download、Documents等),这些目录属于系统公共空间,不受应用卸载影响。
代码实现(使用path_provider)
import 'package:path_provider/path_provider.dart'; import 'dart:io'; Future<File> createPersistentDeviceFile(String imei, String mac) async { // 获取系统公共Download目录 final Directory? publicDownloadDir = await getExternalStoragePublicDirectory(DirectoryDownloads); if (publicDownloadDir == null) throw Exception("外部存储不可用"); // 创建应用专属的公共子目录,避免文件混乱 final Directory appPublicDir = Directory('${publicDownloadDir.path}/MyAppDeviceData'); if (!await appPublicDir.exists()) { await appPublicDir.create(recursive: true); } // 创建并写入文件 final File deviceFile = File('${appPublicDir.path}/device_ids.txt'); await deviceFile.writeAsString('IMEI: $imei\nMAC Address: $mac'); return deviceFile; }
权限说明
- Android 12及以下:需在
AndroidManifest.xml中声明WRITE_EXTERNAL_STORAGE权限,并在运行时动态申请。 - Android 13+:写入公共Download目录无需单独申请WRITE权限,但读取时需申请对应媒体权限(如
READ_MEDIA_IMAGES),具体权限根据文件类型调整。 - 注意:用户可手动删除公共目录下的文件,且无外部存储的设备无法使用该方案。
iOS 平台:无本地留存方案,可选云端或Keychain
iOS的沙盒机制严格隔离应用数据,所有应用私有目录内的文件都会随应用卸载被删除,本地无法直接留存文件。针对你的需求,推荐两种方案:
1. Keychain存储设备ID(推荐)
如果只是存储IMEI、MAC这类小体积的敏感数据,Keychain是最佳选择——它是iOS系统级安全存储,数据不会随应用卸载丢失(除非用户重置设备或手动清除Keychain)。
使用flutter_keychain插件实现:
import 'package:flutter_keychain/flutter_keychain.dart'; // 存储设备ID Future<void> saveDeviceCredentials(String imei, String mac) async { await FlutterKeychain.put(key: 'device_imei', value: imei); await FlutterKeychain.put(key: 'device_mac', value: mac); } // 读取设备ID Future<Map<String, String?>> getDeviceCredentials() async { final String? imei = await FlutterKeychain.get(key: 'device_imei'); final String? mac = await FlutterKeychain.get(key: 'device_mac'); return {'imei': imei, 'mac': mac}; }
2. iCloud Drive同步
将文件保存到iCloud Drive,用户登录同一Apple ID时,重装应用可从云端恢复文件。需先在Xcode中配置iCloud权限:
- 在
Info.plist中添加iCloud容器配置:
<key>NSUbiquitousContainers</key> <dict> <key>iCloud.com.yourdomain.yourapp</key> <dict> <key>NSUbiquitousContainerIsDocumentScopePublic</key> <true/> <key>NSUbiquitousContainerName</key> <string>My App Device Data</string> <key>NSUbiquitousContainerSupportedFolderLevels</key> <string>Any</string> </dict> </dict>
- 代码实现:
import 'package:path_provider/path_provider.dart'; import 'dart:io'; Future<File> saveDeviceDataToiCloud(String imei, String mac) async { // 获取iCloud对应的Documents目录 final Directory? iCloudDocsDir = await getApplicationDocumentsDirectory(); if (iCloudDocsDir == null) throw Exception("iCloud Drive未开启"); final File deviceFile = File('${iCloudDocsDir.path}/device_ids.txt'); await deviceFile.writeAsString('IMEI: $imei\nMAC Address: $mac'); return deviceFile; }
注意
- 用户必须开启iCloud Drive才能使用该功能,且依赖网络同步。
- 受iCloud存储空间限制,大文件不适用。
针对存储设备ID的优化建议
你无需通过文件存储设备唯一ID,更高效安全的方式是:
- Android:使用
flutter_secure_storage插件(基于系统Keystore)存储敏感ID,或开启默认的Auto Backup功能(SharedPreferences数据会备份到Google Drive,卸载重装后恢复)。 - iOS:直接用Keychain存储,完全满足卸载后留存需求,且安全性更高。
内容的提问来源于stack exchange,提问作者Cenk YAGMUR
相关产品推荐
相关产品推荐

