Mobile and desktop apps often need to retain short-lived credentials such as refresh tokens or session identifiers. The flutter_secure_storage package stores small values through platform security services instead of ordinary preferences. It is useful for app-specific secrets, but it cannot make a secret safe if the device or application process is fully compromised.
Current package and platform support
As of August 2026, the current stable release is flutter_secure_storage 11.0.0. It supports Android, iOS, Linux, macOS, web, and Windows, with platform-specific setup requirements. The package metadata declares Dart 3.8, but its current Windows implementation dependency can require Dart 3.10 during resolution. Use a current Flutter SDK and verify flutter pub get in your project before upgrading.
dependencies:
flutter_secure_storage: ^11.0.0
Use a real, reviewed version constraint in pubspec.yaml; ^latest is not valid dependency syntax and does not make upgrades safer.
Write, read, and delete a value
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
const storage = FlutterSecureStorage();
Future<void> saveRefreshToken(String token) async {
await storage.write(key: 'refresh_token', value: token);
}
Future<String?> readRefreshToken() {
return storage.read(key: 'refresh_token');
}
Future<void> clearRefreshToken() async {
await storage.delete(key: 'refresh_token');
}
Keep key names centralized and delete credentials on logout or account removal. Avoid printing tokens in logs, crash reports, or analytics events.
How storage differs by platform
- Android: version 11 uses RSA-OAEP and AES-GCM by default and requires Android API 24 or later.
- Apple platforms: values use Keychain; Keychain sharing and accessibility options require the appropriate entitlements and product decision.
- Web: secure operation requires HTTPS (localhost is the development exception). Browser storage cannot provide the same threat boundary as native Keychain or Keystore.
- Linux and Windows: follow the package’s platform setup and runtime dependency instructions before shipping.
Important migration note for version 11
Version 10 introduced a security-focused Android rewrite and migration path. Version 11 removes deprecated RSA-PKCS1 and AES-CBC support. If an existing app stores data with a pre-v10 release, upgrade through version 10 and verify migration before moving to version 11. Jumping directly can leave old values unreadable.
Test upgrades on representative devices and retain a safe re-authentication path. A user should be able to log in again if secure storage is cleared, corrupted, or no longer decryptable.
What belongs in secure storage?
- Refresh tokens or session credentials that the server can revoke
- Small encryption keys generated for app-specific local data
- Sensitive user preferences that require platform-backed protection
Do not use it to hide a privileged server API key inside a distributed application. Any static secret shipped with a client can eventually be extracted. Put privileged credentials on a backend and issue scoped, revocable tokens to the app.
Error handling
Future<String?> tryReadRefreshToken() async {
try {
return await storage.read(key: 'refresh_token');
} catch (error, stackTrace) {
// Report the failure without including token values.
await recordStorageFailure(error, stackTrace);
return null;
}
}
Treat a read failure differently from “no value exists” when the distinction matters. In an authentication flow, the safest recovery is usually to clear invalid state and ask the user to authenticate again.
Testing checklist
- Fresh install, logout, reinstall, and device backup/restore behavior
- Upgrade from the oldest supported package version
- Android minSdk and backup configuration
- Apple Keychain entitlements and accessibility behavior
- HTTPS enforcement for web builds
- Recovery when a value is missing or cannot be decrypted


Comment