Skip to main content

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 Keystore and EncryptedSharedPreferences for secure storage.
  • iOS: Relies on Keychain Services and Secure Enclave for secure data handling.
  • Cross-platform: Use MethodChannel or plugins like flutter_secure_storage to 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.

Was this article helpful?