bosc_flutter_loacl_storage_package (1.0.0)

Published 2026-02-02 07:58:18 +00:00 by mansi.kansara

Installation

dart pub add bosc_flutter_loacl_storage_package:1.0.0 --hosted-url=

About this package

A robust, production-ready Local Storage Client for Flutter using GetStorage.

BOSC Flutter Local Storage Package

A robust, production-ready Local Storage Client for Flutter using GetStorage. This package provides a clean, type-safe API for local data persistence with comprehensive error handling, serialization, and optional encryption support.

pub package
License: MIT

Features

  • Type-Safe Operations - Generic read/write methods with full type safety
  • Clean Architecture - Separation of concerns with clear component boundaries
  • Custom Object Serialization - Register serializers for complex objects
  • Optional Encryption - Built-in support for encrypting sensitive data
  • Comprehensive Error Handling - Custom exceptions for granular error management
  • Batch Operations - Efficient multi-key read/write operations
  • Reactive Updates - Stream-based change notifications
  • Backup & Restore - Easy data backup and restoration
  • Testing Support - Isolated test configurations
  • Well Documented - Extensive documentation and examples
  • Production Ready - Battle-tested error handling and edge cases

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  bosc_flutter_loacl_storage_package: ^0.0.1

Then run:

flutter pub get

Quick Start

1. Initialize in main()

import 'package:bosc_flutter_loacl_storage_package/bosc_flutter_loacl_storage_package.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Initialize storage
  await LocalStorageClient.instance.initialize();
  
  runApp(MyApp());
}

2. Basic Usage

final storage = LocalStorageClient.instance;

// Write data
await storage.write<String>(StorageKeys.authToken, 'abc123');
await storage.write<int>(StorageKeys.userId, 12345);
await storage.write<bool>(StorageKeys.isDarkMode, true);

// Read data
final token = storage.read<String>(StorageKeys.authToken);
final userId = storage.read<int>(StorageKeys.userId, defaultValue: 0);

// Check existence
if (storage.containsKey(StorageKeys.authToken)) {
  print('User is authenticated');
}

// Remove data
await storage.remove(StorageKeys.authToken);

// Clear all
await storage.clear();

Architecture

The package follows clean architecture principles with clear separation of concerns:

lib/
├── src/
│   ├── core/
│   │   ├── storage_exceptions.dart    # Custom exceptions
│   │   └── storage_keys.dart          # Type-safe keys
│   ├── models/
│   │   └── storage_config.dart        # Configuration
│   ├── services/
│   │   └── storage_service.dart       # Core service layer
│   ├── utils/
│   │   ├── storage_serializer.dart    # Serialization
│   │   └── storage_encryption.dart    # Encryption hooks
│   └── storage_client.dart            # Main API
└── bosc_flutter_loacl_storage_package.dart  # Barrel file

Detailed Usage

Configuration

// Development configuration
final storage = LocalStorageClient(
  config: StorageConfig.development(
    boxName: 'dev_storage',
  ),
);

// Production configuration
final storage = LocalStorageClient(
  config: StorageConfig.production(
    boxName: 'prod_storage',
    enableAutoBackup: true,
  ),
);

// Custom configuration
final storage = LocalStorageClient(
  config: StorageConfig(
    boxName: 'my_storage',
    enableLogging: true,
    throwOnError: true,
    maxRetries: 3,
  ),
);

Custom Object Storage

// 1. Create your model with toJson/fromJson
class User {
  final int id;
  final String name;
  final String email;

  User({required this.id, required this.name, required this.email});

  Map<String, dynamic> toJson() => {
    'id': id,
    'name': name,
    'email': email,
  };

  factory User.fromJson(Map<String, dynamic> json) => User(
    id: json['id'],
    name: json['name'],
    email: json['email'],
  );
}

// 2. Register the serializer
StorageSerializer.register<User>(
  serializer: (user) => user.toJson(),
  deserializer: (json) => User.fromJson(json),
);

// 3. Store and retrieve
final user = User(id: 1, name: 'John Doe', email: 'john@example.com');
await storage.write<User>(StorageKeys.userProfile, user);

final storedUser = storage.read<User>(StorageKeys.userProfile);
print(storedUser?.name); // John Doe

Encryption

// Implement your encryption handler
class MyEncryptionHandler implements StorageEncryptionHandler {
  final String key;
  
  MyEncryptionHandler(this.key);
  
  @override
  String encrypt(String plainText) {
    // Use a real encryption library like 'encrypt' package
    // This is just a placeholder
    return encryptData(plainText, key);
  }
  
  @override
  String decrypt(String encrypted) {
    return decryptData(encrypted, key);
  }
}

// Use encrypted storage
final storage = LocalStorageClient(
  encryptionHandler: MyEncryptionHandler('my_secret_key'),
);

// Write encrypted data
await storage.writeEncrypted(StorageKeys.authToken, 'sensitive_token');

// Read automatically decrypts
final token = storage.read<String>(StorageKeys.authToken);

Batch Operations

// Write multiple values at once
await storage.writeBatch({
  StorageKeys.userId: 123,
  StorageKeys.userName: 'John Doe',
  StorageKeys.userEmail: 'john@example.com',
  StorageKeys.isDarkMode: true,
});

// Remove multiple keys
await storage.removeAll([
  StorageKeys.authToken,
  StorageKeys.refreshToken,
  StorageKeys.userId,
]);

Reactive Updates

// Listen to changes on a key
storage.listen<String>(StorageKeys.authToken).listen((token) {
  print('Auth token changed: $token');
  
  if (token == null) {
    // Handle logout
    Navigator.pushReplacementNamed(context, '/login');
  }
});

// Listen to user profile changes
storage.listen<User>(StorageKeys.userProfile).listen((user) {
  if (user != null) {
    print('User updated: ${user.name}');
  }
});

Backup & Restore

// Create backup
final backup = storage.createBackup();

// ... perform operations ...

// Restore if needed
await storage.restoreBackup(backup);

// Export to JSON string
final jsonBackup = storage.exportToJson();
// Save to file or cloud

// Import from JSON
await storage.importFromJson(jsonBackup);

Error Handling

try {
  // This will throw if key doesn't exist
  final token = storage.readRequired<String>(StorageKeys.authToken);
  print('Token: $token');
} on StorageReadException catch (e) {
  print('Read error: ${e.message}');
  print('Key: ${e.key}');
}

// With default values (won't throw)
final token = storage.read<String>(
  StorageKeys.authToken,
  defaultValue: 'default_token',
);

Custom Storage Keys

Define your keys in one place for type safety and consistency:

abstract class AppStorageKeys {
  // Authentication
  static const String authToken = 'auth_token';
  static const String refreshToken = 'refresh_token';
  
  // User Data
  static const String userProfile = 'user_profile';
  static const String userId = 'user_id';
  
  // Settings
  static const String isDarkMode = 'is_dark_mode';
  static const String languageCode = 'language_code';
}

// Usage
await storage.write<String>(AppStorageKeys.authToken, 'token');

The package includes a comprehensive StorageKeys class with common keys. You can:

  • Use the predefined keys
  • Extend it with your own
  • Create a completely custom keys class

Key Extensions

// Create namespaced keys for multi-tenant apps
final tenantKey = StorageKeys.authToken.withNamespace('tenant_123');
await storage.write<String>(tenantKey, 'tenant_token');

// Create versioned keys for migrations
final keyV2 = StorageKeys.userProfile.withVersion(2);

// Check if a key is sensitive
if (StorageKeys.authToken.isSensitive) {
  // Use encryption
  await storage.writeEncrypted(StorageKeys.authToken, token);
}

Testing

The package provides excellent testing support:

testWidgets('storage test', (tester) async {
  // Create isolated test storage
  final storage = LocalStorageClient.test();
  await storage.initialize();
  
  // Run your tests
  await storage.write<String>('test_key', 'test_value');
  expect(storage.read<String>('test_key'), 'test_value');
  
  // Clean up
  await storage.clear();
  storage.dispose();
});

API Reference

Main Methods

Write Operations

  • write<T>(String key, T value) - Write any type
  • writeEncrypted<T>(String key, T value) - Write with encryption
  • writeBatch(Map<String, dynamic> entries) - Write multiple values

Read Operations

  • read<T>(String key, {T? defaultValue}) - Read with optional default
  • readRequired<T>(String key) - Read or throw if missing
  • readOrWrite<T>(String key, {required T defaultValue}) - Read or initialize

Query Operations

  • containsKey(String key) - Check if key exists
  • getKeys() - Get all keys
  • getAll() - Get all key-value pairs
  • isEmpty - Check if storage is empty
  • length - Get item count

Delete Operations

  • remove(String key) - Remove single key
  • removeAll(List<String> keys) - Remove multiple keys
  • clear() - Clear all data
  • clearAuthData() - Clear auth-related keys

Reactive Operations

  • listen<T>(String key) - Listen to key changes

Utility Operations

  • createBackup() - Create backup
  • restoreBackup(Map<String, dynamic> backup) - Restore from backup
  • exportToJson() - Export to JSON string
  • importFromJson(String json) - Import from JSON

Exception Types

  • StorageException - Base exception
  • StorageInitializationException - Initialization failed
  • StorageReadException - Read operation failed
  • StorageWriteException - Write operation failed
  • StorageDeleteException - Delete operation failed
  • StorageClearException - Clear operation failed
  • StorageSerializationException - Serialization/deserialization failed
  • StorageEncryptionException - Encryption/decryption failed

Best Practices

Do

  1. Initialize once in main()

    await LocalStorageClient.instance.initialize();
    
  2. Use type-safe keys

    await storage.write<String>(StorageKeys.authToken, token);
    
  3. Register serializers for custom objects

    StorageSerializer.register<User>(...);
    
  4. Encrypt sensitive data

    await storage.writeEncrypted(StorageKeys.authToken, token);
    
  5. Handle errors gracefully

    try {
      final value = storage.readRequired<String>(key);
    } on StorageReadException catch (e) {
      // Handle error
    }
    
  6. Use batch operations for multiple writes

    await storage.writeBatch({...});
    

Don't

  1. Don't store large files - Use file system instead
  2. Don't store sensitive data without encryption
  3. Don't forget to initialize
  4. Don't use arbitrary strings as keys - Use constants
  5. Don't ignore exceptions in production

Common Mistakes

Mistake 1: Not Initializing

// ❌ Wrong
final token = LocalStorageClient.instance.read<String>('token');

// ✅ Correct
await LocalStorageClient.instance.initialize();
final token = LocalStorageClient.instance.read<String>('token');

Mistake 2: Storing Sensitive Data Without Encryption

// ❌ Wrong
await storage.write<String>(StorageKeys.authToken, token);

// ✅ Correct
await storage.writeEncrypted(StorageKeys.authToken, token);

Mistake 3: Not Using Type Parameters

// ❌ Wrong - No type safety
await storage.write('userId', 123);
var userId = storage.read('userId');

// ✅ Correct - Type safe
await storage.write<int>('userId', 123);
int? userId = storage.read<int>('userId');

Mistake 4: Not Handling Missing Keys

// ❌ Wrong - May return null unexpectedly
final userId = storage.read<int>('userId');
print(userId + 1); // Runtime error if null

// ✅ Correct - Handle null or use default
final userId = storage.read<int>('userId', defaultValue: 0);
print(userId + 1);

Performance Considerations

  1. Batch Operations - Use writeBatch for multiple writes
  2. Lazy Initialization - Enable if you don't need storage immediately
  3. Avoid Frequent Clears - Clear is expensive; remove specific keys instead
  4. Serialization - Register serializers for complex objects once at startup
  5. Listeners - Dispose streams when no longer needed

Migration from Other Storage Solutions

From SharedPreferences

// Before (SharedPreferences)
final prefs = await SharedPreferences.getInstance();
await prefs.setString('token', token);
final token = prefs.getString('token');

// After (LocalStorageClient)
final storage = LocalStorageClient.instance;
await storage.initialize();
await storage.write<String>('token', token);
final token = storage.read<String>('token');

From Hive

// Before (Hive)
await Hive.initFlutter();
final box = await Hive.openBox('myBox');
await box.put('token', token);
final token = box.get('token');

// After (LocalStorageClient)
final storage = LocalStorageClient.instance;
await storage.initialize();
await storage.write<String>('token', token);
final token = storage.read<String>('token');

Examples

Check the /example directory for comprehensive examples including:

  • Basic operations
  • Custom object storage
  • Encryption
  • Batch operations
  • Reactive updates
  • Error handling
  • Testing setup

Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

If you find this package helpful, please give it a on GitHub!

For issues and feature requests, please use the GitHub issue tracker.

Changelog

See CHANGELOG.md for a list of changes.


Made with ❤️ for the Flutter community

Details
Pub
2026-02-02 07:58:18 +00:00
5
38 KiB
Assets (1)
1.0.0.tar.gz 38 KiB
Versions (2) View all
1.1.0 2026-02-13
1.0.0 2026-02-02