Using NoSQL and Secure Storage
1. Using Hive Package
Hive is a lightweight and fast NoSQL database for Flutter applications. It provides a simple API for storing and retrieving data, with support for various data types including strings, numbers, booleans, lists, maps, and custom objects.
| Feature | Description | Notes |
|---|---|---|
| Hive.init() | Initializes Hive with a directory path | Must be called before using any Hive functionality |
| Hive.openBox() | Opens a box with a given name | Returns a Box instance for data operations |
| Box.put() | Stores a key-value pair in the box | Keys and values can be any supported type |
| Box.get() | Retrieves a value by key from the box | Returns null if key does not exist |
| Box.delete() | Deletes a key-value pair from the box | Does not throw an error if key does not exist |
| Box.clear() | Deletes all key-value pairs from the box | Does not close the box |
Example: Initializing Hive and Storing Data
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart';
void initHive() async {
final dir = await getApplicationDocumentsDirectory();
Hive.init(dir.path);
await Hive.openBox('myBox');
}
void saveData() {
final box = Hive.box('myBox');
box.put('name', 'John Doe');
box.put('age', 30);
box.put('isActive', true);
}
Example: Retrieving Data from Hive
void loadData() {
final box = Hive.box('myBox');
String? name = box.get('name');
int? age = box.get('age');
bool? isActive = box.get('isActive');
print('Name: $name, Age: $age, Active: $isActive');
}
Note: Hive requires initialization before use, and data is persisted to the device's file system.
Warning: Hive does not provide encryption by default. For secure storage, consider using
HiveSecureBox or other encryption libraries.
Hive Key Features
- Fast and lightweight database
- Supports various data types
- Easy to integrate into Flutter apps
- No external dependencies required
- Can be used for local data persistence
2. Initializing Hive
Example: Basic Hive Setup
// Import Hive
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
// Initialize Hive
await Hive.initFlutter();
// Register adapters (if needed)
// Hive.registerAdapter(MyAdapter());
// Open a box
var box = await Hive.openBox('myBox');
| Method | Description | Notes |
|---|---|---|
| Hive.initFlutter() | Initializes Hive for Flutter apps | Must be called before opening any boxes |
| Hive.openBox(name) | Opens or creates a box with the given name | Returns a Future<Box> |
| Hive.registerAdapter() | Registers a custom adapter for a class | Required for complex objects |
Note: Hive requires
Hive.initFlutter() to be called before any other Hive operations. This should typically be done in main().Warning: Hive boxes are stored in the app's local storage. Ensure sensitive data is encrypted before storing.
3. Opening Hive Boxes
| Method | Description | Parameters | Notes |
|---|---|---|---|
| openBox() | Opens a Hive box with the given name | name (String) |
Creates the box if it doesn't exist |
| openBox<T>() | Opens a typed Hive box | name (String), adapter (Adapter<T>) |
Used for type-safe access to box contents |
Example: Opening a Hive Box
import 'package:hive_flutter/hive_flutter.dart';
void main() async {
await Hive.initFlutter();
final box = await Hive.openBox('myBox');
}
Example: Opening a Typed Hive Box
import 'package:hive_flutter/hive_flutter.dart';
class User {
final String name;
final int age;
User(this.name, this.age);
}
void main() async {
await Hive.initFlutter();
final box = await Hive.openBox<User>('userBox', UserAdapter());
}
Note: Hive boxes are stored in the app's file system and can be accessed across app sessions.
4. Storing and Retrieving Data
Example: Using Hive for Local Storage
// Initialize Hive
await Hive.initFlutter();
// Open a box
final box = await Hive.openBox('myBox');
// Store data
await box.put('name', 'John Doe');
await box.put('age', 30);
// Retrieve data
final name = box.get('name');
final age = box.get('age');
| Method | Description | Return Type |
|---|---|---|
| Hive.initFlutter() | Initializes Hive for Flutter apps | Future<void> |
| Hive.openBox(name) | Opens or creates a box with the given name | Future<Box> |
| box.put(key, value) | Stores a key-value pair in the box | Future<void> |
| box.get(key) | Retrieves a value by key from the box | dynamic |
| box.delete(key) | Deletes a key-value pair from the box | Future<void> |
Note: Hive stores data in a binary format and supports encryption.
Example: Using SharedPreferences for Simple Key-Value Storage
// Get instance
final prefs = await SharedPreferences.getInstance();
// Store data
await prefs.setString('username', 'john_doe');
await prefs.setInt('userAge', 30);
// Retrieve data
final username = prefs.getString('username');
final age = prefs.getInt('userAge');
| Method | Description | Return Type |
|---|---|---|
| SharedPreferences.getInstance() | Gets the instance of SharedPreferences | Future<SharedPreferences> |
| prefs.setString(key, value) | Stores a string value | Future<void> |
| prefs.getInt(key) | Retrieves an integer value | int? |
| prefs.getString(key) | Retrieves a string value | String? |
| prefs.remove(key) | Removes a key-value pair | Future<void> |
Warning: SharedPreferences is not suitable for large or complex data structures.
Comparison: Hive vs SharedPreferences
- Hive: Fast, binary storage, supports encryption, good for complex data
- SharedPreferences: Simple key-value storage, limited to basic types, not secure
5. Using Type Adapters
Type adapters in Flutter are used to convert data between your application's domain objects and the format required by storage mechanisms, such as JSON or database records. They are particularly useful when working with databases like Hive or Room.
| Property | Description | Usage |
|---|---|---|
| type | The type to be adapted. | type: DateTime |
| fromMap | Function to convert a map into the target type. | fromMap: (map) => DateTime.parse(map['date']) |
| toMap | Function to convert the target type into a map. | toMap: (date) => {'date': date.toIso8601String()} |
Example: Using TypeAdapter with Hive
@HiveType
class User {
@HiveField(0)
String name;
@HiveField(1)
DateTime birthday;
User(this.name, this.birthday);
}
class DateTimeAdapter extends TypeAdapter<DateTime> {
@override
DateTime read(BinaryReader reader) {
return DateTime.parse(reader.readString());
}
@override
void write(BinaryWriter writer, DateTime obj) {
writer.writeString(obj.toIso8601String());
}
@override
int get hashCode => 0;
@override
bool operator ==(Object other) => other is DateTimeAdapter;
}
Note: Type adapters are essential when working with complex types like
DateTime or custom objects in Hive. They ensure proper serialization and deserialization.6. Using Isar Database
| Feature | Description | Notes |
|---|---|---|
| Isar | High-performance NoSQL database for Flutter | Supports Android, iOS, Web, and Desktop platforms |
| Schema Definition | Define collections using Dart classes | Uses annotations for database mapping |
| Queries | Supports complex queries with filtering and sorting | Async operations required for database access |
Example: Define a collection
@Collection()
class User {
Id? id;
String name;
int age;
User({required this.name, required this.age});
}
Example: Insert data into Isar
final user = User(
name: 'John Doe',
age: 30,
);
await isar.users.put(user);
Example: Query data from Isar
final users = await isar.users
.filter()
.age(lessThan: 40)
.findAll();
Note: Isar requires the
isar and isar_flutter_libs packages to be added to your pubspec.yaml.
Warning: Ensure that Isar is initialized before any database operations. Failure to do so will result in runtime errors.
7. Using flutter_secure_storage Package
| Property | Description | Values |
|---|---|---|
| Flutter Secure Storage | Package for secure local storage in Flutter apps | flutter_secure_storage |
Example: Basic Usage
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
final storage = FlutterSecureStorage();
// Write data
await storage.write(key: 'username', value: 'john_doe');
// Read data
String? username = await storage.read(key: 'username');
Example: Using with Platform-Specific Options
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
final storage = FlutterSecureStorage(
iOptions: IOsSecureStorageOptions(
accessibility: KeychainAccessibility.whenUnlocked,
),
aOptions: AndroidSecureStorageOptions(
encryptedFieldsAreValidated: true,
),
);
Example: Delete and Clear Operations
// Delete a specific key
await storage.delete(key: 'username');
// Clear all stored data
await storage.clear();
Note: flutter_secure_storage uses platform-specific secure storage mechanisms (Keychain on iOS, Keystore on Android) to ensure data is encrypted and protected from unauthorized access.
8. Storing Sensitive Data Securely
Example: Using Flutter Secure Storage
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
final storage = FlutterSecureStorage();
// Write sensitive data
await storage.write(key: 'api_token', value: 'abc123xyz');
// Read sensitive data
String? token = await storage.read(key: 'api_token');
| Method | Description | Notes |
|---|---|---|
| write(key: value:) | Stores data securely | Overwrites existing key |
| read(key:) | Retrieves data by key | Returns null if not found |
| delete(key:) | Deletes a key-value pair | Does not throw if key doesn't exist |
| deleteAll() | Deletes all stored data | Use with caution |
Note: Flutter Secure Storage uses platform-specific secure storage (Keychain on iOS, Keystore on Android) to ensure sensitive data is encrypted and protected.
Warning: Never store sensitive data in plain text or shared preferences. Always use secure storage for credentials, tokens, or any sensitive user data.
9. Retrieving Secure Data
| Method | Description | Return Type | Notes |
|---|---|---|---|
| getSecureData(key) | Retrieves encrypted data from secure storage | Future<String> |
Throws exception if key does not exist or decryption fails |
| getSecureDataList(key) | Retrieves a list of encrypted data from secure storage | Future<List<String>> |
Useful for storing multiple values under one key |
Example: Retrieving Secure Data
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
final storage = FlutterSecureStorage();
Future<void> retrieveData() async {
try {
String? value = await storage.read(key: 'user_token');
if (value != null) {
print('Token: $value');
} else {
print('No token found');
}
} catch (e) {
print('Error retrieving data: $e');
}
}
Note: Ensure that all keys used for secure storage are consistent across app versions to avoid data loss.
10. Understanding Platform-Specific Security
Note: Flutter apps can access platform-specific security features through
MethodChannel or platform-specific plugins. These features vary between Android and iOS, and understanding their differences is crucial for secure app development.| Feature | Android | iOS | Description |
|---|---|---|---|
| KeyStore | Android Keystore System |
Keychain Services |
Secure storage for cryptographic keys and sensitive data. |
| EncryptedSharedPreferences | EncryptedSharedPreferences |
Keychain Services |
Encrypted storage for key-value pairs on Android. |
| Secure Enclave | Not natively supported |
Secure Enclave |
Hardware-based secure storage on iOS devices. |
| Biometric Authentication | BiometricManager |
LocalAuthentication |
Platform-specific biometric authentication APIs. |
Example: Using Platform-Specific Security on Android
import 'package:flutter/services.dart';
Future<String> getEncryptedValue(String key) async {
const MethodChannel channel = MethodChannel('secure_storage');
try {
final String value = await channel.invokeMethod('getEncryptedValue', {
'key': key,
});
return value;
} on PlatformException catch (e) {
print("Error: ${e.message}");
return '';
}
}
Example: Using Keychain on iOS
import 'package:flutter/services.dart';
Future<String> getSecureValue(String key) async {
const MethodChannel channel = MethodChannel('secure_storage');
try {
final String value = await channel.invokeMethod('getSecureValue', {
'key': key,
});
return value;
} on PlatformException catch (e) {
print("Error: ${e.message}");
return '';
}
}
Platform Security Considerations
- Android: Uses
KeystoreandEncryptedSharedPreferencesfor secure storage. - iOS: Relies on
Keychain ServicesandSecure Enclavefor secure data handling. - Cross-platform: Use
MethodChannelor plugins likeflutter_secure_storageto abstract platform differences.
Warning: Never store sensitive data in plain text or shared preferences on Android without encryption.
11. Storing Authentication Tokens
| Storage Method | Description | Security Level | Notes |
|---|---|---|---|
| SharedPreferences | Key-value storage for small data like tokens | Low | Not encrypted by default; use with caution for sensitive data |
| Flutter Secure Storage | Encrypted local storage for sensitive data | High | Uses platform-specific secure storage (Keychain on iOS, Keystore on Android) |
| SQLite | Local database for structured data including tokens | Medium | Requires manual encryption or use of SQLCipher for enhanced security |
Example: Using Flutter Secure Storage
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
final storage = FlutterSecureStorage();
// Save token
await storage.write(key: 'auth_token', value: 'abc123xyz');
// Read token
String? token = await storage.read(key: 'auth_token');
// Delete token
await storage.delete(key: 'auth_token');
Note: Always clear tokens from storage when logging out or after token expiration.
Warning: Never store authentication tokens in plain text or unencrypted formats.