flutter_secure_storage: Migrating from v8 to v11 (Data Loss Analysis & Fix)
Source: https://pub.dev/packages/flutter_secure_storage/changelog (latest at time of writing: 11.2.0, 2026-09-16)
After updating an existing app from flutter_secure_storage v8 to v11, data written by v8 can no longer be read.
v8 → v11 is an unsupported direct jump. The v11.0.0 changelog states that items deprecated in v10 were removed, that data saved with deprecated algorithms or features becomes unusable, and that anyone on a version before v10 must upgrade to v10 first so existing data is migrated.
| Version | Change | Effect on v8 data |
|---|---|---|
| v8 (defaults) | Key cipher RSA_ECB_PKCS1Padding, storage cipher AES_CBC_PKCS7Padding (or encryptedSharedPreferences: true if you enabled it) | This is how your data is stored |
| v10.0.0 | Defaults changed to RSA_ECB_OAEPwithSHA_256andMGF1Padding and AES_GCM_NoPadding. migrateOnAlgorithmChange migrates old data. resetOnError now defaults to true. Jetpack EncryptedSharedPreferences replaced with custom ciphers (auto-migrated). minSdk 19 → 23. | Migrated only if the app runs v10 |
| v10.1.0 | Added storageNamespace (full isolation) and migrateWithBackup (crash-resistant migration). sharedPreferencesName deprecated. | Still readable |
| v10.2.0 | RSA_ECB_PKCS1Padding and AES_CBC_PKCS7Padding deprecated. Auto-migrated when migrateOnAlgorithmChange is true. | Still readable |
| v10.3.x | Biometric options, iOS/Windows/Linux fixes | n/a |
| v11.0.0 | Removed RSA_ECB_PKCS1Padding, AES_CBC_PKCS7Padding, encryptedSharedPreferences, sharedPreferencesName. minSdk 24, compileSdk 37. | Old format cannot be read |
| v11.1.0 | Added checkUpgradeStatus(). Fixed moving the wrapped key when switching between sharedPreferencesName and storageNamespace. | Detection and partial recovery help |
| v11.2.0 | Android recovery fixes (namespace switch, biometric key recovery, deleteAll scoped to key prefix). Darwin: find keychain items across accessibility levels. | Recommended minimum for v11 |
resetOnErrorSince v10, resetOnError defaults to true. When v11 cannot decrypt old v8 data, it treats this as an unrecoverable error and wipes the storage and keys. That is why the data may be permanently gone for users who already made the direct jump.
pubspec.yaml:
dependencies:
flutter_secure_storage: ^10.3.1 # latest 10.x listed in the changelog; confirm on the Versions tab
Initialization:
const storage = FlutterSecureStorage(
aOptions: AndroidOptions(
migrateOnAlgorithmChange: true, // default, set explicitly
migrateWithBackup: true, // added in 10.1.0, backup if migration crashes
// resetOnError: false, // optional: avoid wiping data during the bridge release
),
);
Force migration on every launch of the bridge release:
Future<void> triggerMigration() async {
await storage.readAll(); // initialization and migration happen on first access
}
Call it early in main(). Keep this release live long enough for most active users to open it.
Ship only after the bridge release has been out for a while.
Checklist:
encryptedSharedPreferences and sharedPreferencesName from AndroidOptions (the build fails otherwise).sharedPreferencesName, switch to storageNamespace. v11.1.0 added a fix to move the wrapped key when switching. The changelog does not describe the exact behaviour, so test this path on a real upgrade.minSdk to 24 and align compileSdk/targetSdk with the plugin. Use Java 17.dependencies:
flutter_secure_storage: ^11.2.0
Keychain data normally survives upgrades, but v10 merged iOS and macOS into the flutter_secure_storage_darwin package. To keep data readable:
IOSOptions as in v8: accountName (kSecAttrService), accessibility, groupId, synchronizable. A mismatch makes items look missing.kSecUseDataProtectionKeychain is only set when useDataProtectionKeychain is explicitly enabled. If you did not set it before, do not set it now.v11.1.0 added checkUpgradeStatus(), which reports data lost on a direct major upgrade. Call it on first launch after upgrading to decide whether to prompt the user to log in again. The exact return type was not verified, so check the API reference before coding against it.
Recommended fallback: if a required key reads as null after upgrade, treat the user as logged out and re-authenticate instead of crashing.
resetOnError: true wipes data when decryption fails.IOSOptions.checkUpgradeStatus() (v11.1.0+).No comments yet. Start the conversation below.