Native Secured Storage
native_secured_storage is a Flutter plugin designed for securely storing and retrieving primitive data types (int, double, bool, and String) using Android Keystore and iOS Keychain. This ensures data is encrypted at rest and protected against unauthorized access, even if the device is compromised.
Why Use native_secured_storage?
Security Features
-
Android Keystore with EncryptedSharedPreferences:
- Uses AES256 encryption with a unique key stored securely in the Android Keystore.
- Ensures encryption and decryption happen only within the device.
-
iOS Keychain:
- Integrates directly with Apple's Keychain, providing system-level security for sensitive data.
- Protects data using the device’s secure enclave, if available.
-
No Plaintext Storage:
- All data is encrypted before being saved, ensuring that sensitive information is never stored in plaintext.
-
Platform Isolation:
- Separate implementations for Android and iOS ensure data is stored securely in each platform's native environment.
Use Cases
- Authentication: Securely store tokens, passwords, or sensitive user credentials.
- Preferences: Save encrypted app preferences such as user settings or feature flags.
- Secure Identifiers: Store unique identifiers or keys for app logic.
Features
- Cross-platform: Supports both Android and iOS.
- Type Support: Securely handles
int,double,bool, andStringtypes. - Easy-to-use API: Simplified Dart API for saving, retrieving, and deleting data.
- Error Handling: Comprehensive error handling to catch issues during encryption or storage operations.
Installation
Add native_secured_storage to your pubspec.yaml:
dependencies:
native_secured_storage: ^1.0.0
Run the following command to install the package:
flutter pub get
Usage
Import the Package
import 'package:native_secured_storage/native_secured_storage.dart';
Save a Value
await NativeSecuredStorage.save('key_name', 123); // Save an integer
await NativeSecuredStorage.save('key_name', 45.67); // Save a double
await NativeSecuredStorage.save('key_name', true); // Save a boolean
await NativeSecuredStorage.save('key_name', 'Hello World'); // Save a string
Retrieve a Value
final value = await NativeSecuredStorage.retrieve('key_name');
if (value != null) {
print('Retrieved value: $value');
} else {
print('No value found for the given key.');
}
Delete a Value
await NativeSecuredStorage.delete('key_name');
print('Value deleted successfully.');
Example
Below is a complete example using native_secured_storage:
import 'package:flutter/material.dart';
import 'package:native_secured_storage/native_secured_storage.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Native Secured Storage Example',
theme: ThemeData(primarySwatch: Colors.blue),
home: SecureStorageDemo(),
);
}
}
class SecureStorageDemo extends StatefulWidget {
@override
_SecureStorageDemoState createState() => _SecureStorageDemoState();
}
class _SecureStorageDemoState extends State<SecureStorageDemo> {
final TextEditingController _keyController = TextEditingController();
final TextEditingController _valueController = TextEditingController();
String? _retrievedValue;
Future<void> _saveValue() async {
final key = _keyController.text;
final value = _valueController.text;
if (key.isEmpty || value.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Key and value cannot be empty')),
);
return;
}
dynamic parsedValue;
if (value.toLowerCase() == 'true' || value.toLowerCase() == 'false') {
parsedValue = value.toLowerCase() == 'true';
} else if (double.tryParse(value) != null) {
parsedValue = value.contains('.') ? double.parse(value) : int.parse(value);
} else {
parsedValue = value;
}
try {
await NativeSecuredStorage.save(key, parsedValue);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Value saved successfully')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error saving value: $e')),
);
}
}
Future<void> _retrieveValue() async {
final key = _keyController.text;
if (key.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Key cannot be empty')),
);
return;
}
try {
final value = await NativeSecuredStorage.retrieve(key);
setState(() {
_retrievedValue = value?.toString();
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Value retrieved successfully')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error retrieving value: $e')),
);
}
}
Future<void> _deleteValue() async {
final key = _keyController.text;
if (key.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Key cannot be empty')),
);
return;
}
try {
await NativeSecuredStorage.delete(key);
setState(() {
_retrievedValue = null;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Value deleted successfully')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error deleting value: $e')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Secure Storage Example'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: _keyController,
decoration: InputDecoration(labelText: 'Key'),
),
TextField(
controller: _valueController,
decoration: InputDecoration(labelText: 'Value'),
),
SizedBox(height: 16),
Row(
children: [
ElevatedButton(
onPressed: _saveValue,
child: Text('Save'),
),
SizedBox(width: 8),
ElevatedButton(
onPressed: _retrieveValue,
child: Text('Retrieve'),
),
SizedBox(width: 8),
ElevatedButton(
onPressed: _deleteValue,
child: Text('Delete'),
),
],
),
SizedBox(height: 16),
if (_retrievedValue != null)
Text('Retrieved Value: $_retrievedValue'),
],
),
),
);
}
}
Testing
Run the example app and perform the following actions:
- Save values of different types (
int,double,bool,String). - Retrieve and verify the values.
- Delete a value and confirm it’s removed.
License
This plugin is released under the MIT License.