← Back to posts Cover image for Mastering Secure Storage in Flutter: A Deep Dive into `flutter_secure_storage` for Cross-Platform Apps

Mastering Secure Storage in Flutter: A Deep Dive into `flutter_secure_storage` for Cross-Platform Apps

· 3 min read
Weekly Digest

The Flutter news you actually need

No spam, ever. Unsubscribe in one click.

Chris
By Chris

As Flutter developers, we often need to protect sensitive data like authentication tokens, API keys, and personal credentials. Storing this data in plaintext—even in something like SharedPreferences—is a security risk. For production apps, we need an encrypted solution that works across mobile and desktop platforms. This is where flutter_secure_storage comes in.

The Problem: Platform-Specific Security

Each operating system provides its own mechanism for storing secrets securely:

  • iOS/macOS: Keychain Services
  • Android: Encrypted SharedPreferences (via Android Keystore)
  • Windows: Data Protection API (DPAPI)
  • Linux: Freedesktop.org Secret Service API (via D-Bus)

flutter_secure_storage abstracts these complexities with a unified Dart API. Understanding what happens under the hood helps avoid pitfalls and build more reliable apps.

Setting Up and Basic Usage

First, add the package to your pubspec.yaml:

dependencies:
  flutter_secure_storage: ^9.0.0

For Linux desktop support, include the Linux-specific implementation:

flutter_secure_storage_linux: ^1.2.0

Create a service class to encapsulate the storage logic:

import 'package:flutter_secure_storage/flutter_secure_storage.dart';

class SecureStorageService {
  final FlutterSecureStorage _storage = const FlutterSecureStorage(
    aOptions: AndroidOptions(
      encryptedSharedPreferences: true,
    ),
    iOptions: IOSOptions(
      accessibility: KeychainAccessibility.passcode,
    ),
  );

  Future<void> writeSecureData(String key, String value) async {
    await _storage.write(key: key, value: value);
  }

  Future<String?> readSecureData(String key) async {
    return await _storage.read(key: key);
  }

  Future<void> deleteSecureData(String key) async {
    await _storage.delete(key: key);
  }

  Future<bool> containsKey(String key) async {
    return await _storage.containsKey(key: key);
  }
}

Use the service in your app:

final secureStorage = SecureStorageService();

// Save an auth token
await secureStorage.writeSecureData('auth_token', 'eyJhbGciOiJIUzI1NiIs...');

// Retrieve it later
String? token = await secureStorage.readSecureData('auth_token');
if (token != null) {
  // Use the token for authenticated requests
}

Platform Configuration: The Devil’s in the Details

Tailor options per platform for enhanced security.

Android: Leverage Encrypted Preferences

The encryptedSharedPreferences: true flag uses Android’s EncryptedSharedPreferences class, with encryption keys stored in the Android Keystore.

AndroidOptions(
  encryptedSharedPreferences: true,
)

iOS/macOS: Control Keychain Accessibility

Keychain accessibility rules determine when items can be accessed. passcode requires a device passcode and only allows access after the first device unlock. Other options include unlocked (accessible while device is unlocked) and unlockedThisDeviceOnly (same, but doesn’t sync to iCloud).

IOSOptions(
  accessibility: KeychainAccessibility.unlockedThisDeviceOnly,
)

Linux: The Recent Advancement

The modern approach uses the Freedesktop.org Secret Service API via D-Bus, which is standard in most Linux desktop environments (GNOME, KDE). The flutter_secure_storage_linux package stores secrets in the user’s default keyring (e.g., GNOME Keyring or KWallet).

Common Pitfalls and Best Practices

  1. Handling Null Returns Gracefully: Always assume read can return null (if the key doesn’t exist or the keychain is inaccessible). Never force-unwrap the result.
  2. Secure Initialization: Don’t store initial default secrets (like API keys) in your app’s assets or code. Fetch them from a secure backend service on first launch.
  3. Key Management: Use clear, consistent key names. Define them as constants.
    abstract class SecureStorageKeys {
      static const String authToken = 'auth_token';
      static const String refreshToken = 'refresh_token';
      static const String userId = 'user_id';
    }
  4. Testing: Write tests that mock FlutterSecureStorage. The package provides a MockFlutterSecureStorage class for unit tests.
  5. User Logout Flow: Clear all secure data on logout.
    Future<void> clearAllSecuredData() async {
      await _storage.deleteAll();
    }

Conclusion

flutter_secure_storage is essential for Flutter developers building production applications. By understanding its platform-specific implementations—especially the modern Linux integration via the Secret Service API—and configuring it appropriately, you can ensure sensitive data is stored securely on each platform. Implement a dedicated service class, configure platform options, handle edge cases, and you’ll have a robust secure storage foundation.

This blog is produced with the assistance of AI by a human editor. Learn more

Related Posts

Cover image for Flutter App Size Optimization: Strategies to Shrink Your APK/IPA Below 10MB

Flutter App Size Optimization: Strategies to Shrink Your APK/IPA Below 10MB

Flutter apps are often criticized for their larger-than-native binary sizes. This post will dive into practical, actionable strategies to significantly reduce your Flutter app's footprint, including build configurations, asset optimization, and dependency management, demonstrating how to achieve app sizes under 10MB.

Cover image for Mastering In-App Subscriptions in Flutter: A Guide to Avoiding Common Pitfalls

Mastering In-App Subscriptions in Flutter: A Guide to Avoiding Common Pitfalls

Implementing in-app subscriptions in Flutter can be surprisingly complex, especially when dealing with cross-platform nuances, receipt validation, and managing different store configurations. This post will delve into the common challenges developers face, compare solutions like RevenueCat with custom implementations, and provide best practices for setting up and maintaining a robust subscription system.

Cover image for Taming Android Emulator RAM: A Developer's Guide to Reducing Memory Usage for Flutter

Taming Android Emulator RAM: A Developer's Guide to Reducing Memory Usage for Flutter

Android emulators are notorious for hogging RAM, often pushing developer machines into swap and slowing down Flutter development. This post will explore the reasons behind high emulator memory consumption and provide practical, actionable strategies and tools (including a custom-built solution) to significantly reduce RAM usage, ensuring a smoother development experience without sacrificing functionality.