In flutter_secure_storage 10, what replaced Android's encryptedSharedPreferences option, and how do the new cipher defaults and migration flags behave?
answer
- Jetpack Security deprecated
- RSA OAEP wraps, AES-GCM encrypts
- migrateOnAlgorithmChange defaults to true
- migrateWithBackup and storageNamespace
- minSdk 23; option removed in v11
basics
~10 sVersion 10 deprecated encryptedSharedPreferences because Jetpack Security is deprecated, and switched to custom ciphers: an RSA-OAEP key cipher wrapping an AES-GCM storage cipher. Data migrates automatically because migrateOnAlgorithmChange defaults to true.
solid answer
~30 s`AndroidOptions(encryptedSharedPreferences: true)` relied on Jetpack Security, which Google deprecated. flutter_secure_storage 10 rewrote the Android side with its own ciphers: `KeyCipherAlgorithm.RSA_ECB_OAEPwithSHA_256andMGF1Padding` wraps the data key and `StorageCipherAlgorithm.AES_GCM_NoPadding` encrypts values, and the minimum SDK rose to 23. The old parameter is deprecated, ignored and slated for removal in v11. `migrateOnAlgorithmChange` (default true) re-encrypts existing data on first access, `migrateWithBackup` (default false, added in 10.1) keeps a backup copy during that migration, and `resetOnError` now defaults to true. Version 10.2 also deprecated the legacy `RSA_ECB_PKCS1Padding` and `AES_CBC_PKCS7Padding` choices, and 10.1 added `storageNamespace` to replace `sharedPreferencesName`.
code
dart · 13 linesimport 'package:flutter_secure_storage/flutter_secure_storage.dart';
// Before (9.x): relied on deprecated Jetpack Security.
// const storage = FlutterSecureStorage(
// aOptions: AndroidOptions(encryptedSharedPreferences: true),
// );
// After (10.x): default ciphers, crash-safe one-time migration.
const storage = FlutterSecureStorage(
aOptions: AndroidOptions(
migrateWithBackup: true,
),
);go deeper
Recall that version 10 dropped Jetpack Security on Android and that old data migrates automatically by default.
Explain the RSA-OAEP key cipher and AES-GCM storage cipher defaults and what migrateOnAlgorithmChange, migrateWithBackup and resetOnError each do.
Plan the upgrade release: test from the versions users actually run, choose the reset policy, and avoid renaming storage.
Weigh relying on a community plugin's custom crypto against platform-native storage, including how you would audit and pin it.
## Why the Android implementation was rewritten Before version 10, many Flutter apps passed `AndroidOptions(encryptedSharedPreferences: true)` to **flutter_secure_storage**, which delegated encryption to Jetpack Security's `EncryptedSharedPreferences`. Google deprecated that library, so version 10 replaced it with the plugin's **own cipher implementation**. For a crypto-wallet app upgrading the package, the practical questions are what the new defaults are, what happens to tokens already on users' phones, and which options now do nothing. ## The new defaults | Option | Default in 10.x | Role | |---|---|---| | `keyCipherAlgorithm` | `RSA_ECB_OAEPwithSHA_256andMGF1Padding` | wraps the AES data key with a Keystore RSA key | | `storageCipherAlgorithm` | `AES_GCM_NoPadding` | encrypts each stored value | | `resetOnError` | `true` (was false) | delete unreadable data instead of throwing | | `migrateOnAlgorithmChange` | `true` | re-encrypt old data with the new ciphers | | `migrateWithBackup` | `false` | keep a copy of encrypted data during migration | | `encryptedSharedPreferences` | deprecated, ignored | to be removed in v11 | Other version-10 platform changes that matter to a release: `minSdkVersion` is 23, the plugin's Android code moved to Java 17, and its target SDK moved to 36. `AndroidOptions.biometric()` is the other named constructor. It fixes both ciphers to `AES_GCM_NoPadding`, so the data key lives directly in the Keystore where user authentication can gate it. ## How migration works 1. On first access after the upgrade, the plugin compares the algorithms recorded for the stored data with the configured ones. 2. If they differ and `migrateOnAlgorithmChange` is true, it decrypts with the old configuration and re-encrypts with the new one. Data written through `encryptedSharedPreferences` in version 9 is migrated the same way. 3. With `migrateWithBackup: true`, it first copies the encrypted data so a crash mid-migration can be recovered. 4. If migration fails and `resetOnError` is true, it deletes all data and keys and continues with an empty store; if `resetOnError` is false, it reports an error telling you to enable one of the two flags or call `deleteAll()`. The 10.3.x changelog shows why testing the upgrade matters: several releases fixed data-loss cases for apps upgrading directly from 9.2.4 or switching storage names. For a wallet, test the real upgrade path, from the version your users have, on a device that holds a token. ## Deprecations inside 10.x - **10.1** added `storageNamespace`, which isolates data preferences, algorithm markers, Keystore aliases and wrapped-key storage per instance, and deprecated `sharedPreferencesName`, which isolated only the data file. Use a namespace when two storage instances need different cipher settings. - **10.2** deprecated `KeyCipherAlgorithm.RSA_ECB_PKCS1Padding` and `StorageCipherAlgorithm.AES_CBC_PKCS7Padding`; data using them migrates to the defaults while `migrateOnAlgorithmChange` is on. - **10.3** added `biometricType` and `biometricPromptNegativeButton` for biometric stores. ## How to reason about a failed upgrade report Suppose some Android users report being signed out after the wallet's upgrade to version 10. A structured investigation looks like this: 1. Check which package version those users came from; the 10.3.x changelog lists fixed data-loss paths from 9.2.4 and from renamed storage. 2. Look for the plugin's log lines: messages saying a migration failed and that `resetOnError` deleted all data point to the reset path, not to your own code. 3. Confirm whether the old release used `encryptedSharedPreferences`, a non-default cipher or a custom `sharedPreferencesName`, since each takes a different migration route. 4. Decide whether a sign-out was acceptable. For a wallet it usually is, because the server can re-issue a session after sign-in, but losing a locally generated key would not be. That last point is the real design lesson: secrets that cannot be re-issued from a server need a recovery story that does not depend on a plugin migration succeeding. ## Upgrade checklist - Remove `encryptedSharedPreferences: true` from your options; it no longer changes anything. - Decide deliberately about `resetOnError`: the new default favours a working app over keeping unreadable data. - Consider `migrateWithBackup: true` for the release that performs the migration. - Do not change `sharedPreferencesName` or `preferencesKeyPrefix` casually; the package warns that changing them makes saved values unreachable, and a new `storageNamespace` likewise starts a separate store. - Raise the app's `minSdk` to at least 23 if it was lower.
- In flutter_secure_storage 10, when should you use storageNamespace instead of sharedPreferencesName?Whenever two FlutterSecureStorage instances need separate storage, especially with different cipher settings, such as one standard and one biometric store. `storageNamespace` isolates Keystore aliases and wrapped-key storage too, while `sharedPreferencesName`, now deprecated, only separated the data file.
- What happens in flutter_secure_storage 10 if you set migrateOnAlgorithmChange: false on an app upgraded from 9.x?The stored data no longer matches the configured algorithms. With resetOnError true, the plugin deletes the data and keys and starts empty; with resetOnError false, calls fail with an error suggesting you enable migration, enable reset or call deleteAll().
saying these in an interview costs you the question
- encryptedSharedPreferences: true still switches Android to Jetpack Security.
- Version 10 keeps resetOnError false by default, like 9.x.
- Upgrading to 10 silently wipes all existing Android secrets.
- AES-CBC is still the default storage cipher in version 10.
- Changing storageNamespace later keeps previously saved values visible.