home / skills / kaakati / rails-enterprise-dev / get-storage-patterns
This skill helps you implement robust local storage and caching with GetStorage, enabling offline-first apps and seamless state persistence.
npx playbooks add skill kaakati/rails-enterprise-dev --skill get-storage-patternsReview the files below or copy the command above to add this skill to your agents.
---
name: "GetStorage Patterns"
description: "Local storage with GetStorage for preferences, caching, and offline-first patterns"
version: "1.0.0"
---
# GetStorage Patterns
## Initialization
```dart
// main.dart
void main() async {
await GetStorage.init();
runApp(MyApp());
}
```
## Storage Service Pattern
```dart
class StorageService {
final GetStorage _box;
StorageService() : _box = GetStorage();
// Token management
String? get token => _box.read<String>('auth_token');
Future<void> setToken(String token) => _box.write('auth_token', token);
Future<void> clearToken() => _box.remove('auth_token');
// User data
Map<String, dynamic>? get userData => _box.read<Map<String, dynamic>>('user_data');
Future<void> setUserData(Map<String, dynamic> data) => _box.write('user_data', data);
// Preferences
bool get isDarkMode => _box.read<bool>('dark_mode') ?? false;
Future<void> setDarkMode(bool value) => _box.write('dark_mode', value);
String get locale => _box.read<String>('locale') ?? 'en';
Future<void> setLocale(String locale) => _box.write('locale', locale);
// Clear all
Future<void> clearAll() => _box.erase();
// Listen to changes
void listenKey(String key, Function(dynamic) callback) {
_box.listenKey(key, callback);
}
}
```
## Local Data Source Pattern
```dart
class UserLocalDataSource {
final GetStorage _storage;
static const String _usersKey = 'cached_users';
static const String _userKeyPrefix = 'cached_user_';
static const Duration _cacheDuration = Duration(hours: 24);
UserLocalDataSource(this._storage);
Future<void> cacheUser(UserModel user) async {
final cacheData = {
'user': user.toJson(),
'timestamp': DateTime.now().toIso8601String(),
};
await _storage.write('$_userKeyPrefix${user.id}', cacheData);
}
Future<UserModel?> getCachedUser(String id) async {
final cacheData = _storage.read<Map<String, dynamic>>('$_userKeyPrefix$id');
if (cacheData == null) return null;
// Check cache expiration
final timestamp = DateTime.parse(cacheData['timestamp']);
if (DateTime.now().difference(timestamp) > _cacheDuration) {
await _storage.remove('$_userKeyPrefix$id');
return null;
}
return UserModel.fromJson(cacheData['user']);
}
Future<void> cacheUsers(List<UserModel> users) async {
final cacheData = {
'users': users.map((u) => u.toJson()).toList(),
'timestamp': DateTime.now().toIso8601String(),
};
await _storage.write(_usersKey, cacheData);
}
Future<List<UserModel>?> getCachedUsers() async {
final cacheData = _storage.read<Map<String, dynamic>>(_usersKey);
if (cacheData == null) return null;
final timestamp = DateTime.parse(cacheData['timestamp']);
if (DateTime.now().difference(timestamp) > _cacheDuration) {
await _storage.remove(_usersKey);
return null;
}
final List<dynamic> usersList = cacheData['users'];
return usersList.map((json) => UserModel.fromJson(json)).toList();
}
Future<void> clearCache() async {
await _storage.erase();
}
}
```
## GetX Service Integration
```dart
class CacheService extends GetxService {
final GetStorage _storage;
CacheService() : _storage = GetStorage();
Future<CacheService> init() async {
await GetStorage.init();
return this;
}
// Reactive cache
final _cachedData = <String, dynamic>{}.obs;
T? get<T>(String key) {
return _storage.read<T>(key);
}
Future<void> put<T>(String key, T value, {Duration? expiry}) async {
if (expiry != null) {
final expiryData = {
'value': value,
'expiry': DateTime.now().add(expiry).toIso8601String(),
};
await _storage.write(key, expiryData);
} else {
await _storage.write(key, value);
}
_cachedData[key] = value;
}
Future<void> remove(String key) async {
await _storage.remove(key);
_cachedData.remove(key);
}
bool has(String key) {
return _storage.hasData(key);
}
}
```
## Best Practices
- Initialize GetStorage before running app
- Use type-safe reads (`read<String>`, `read<int>`, etc.)
- Implement cache expiration for time-sensitive data
- Clear cache on logout
- Use separate keys for different data types
- Listen to changes for reactive updates
This skill presents proven GetStorage patterns for local persistence in Flutter apps, focused on preferences, caching, and offline-first flows. I provide service and local data source patterns, reactive cache integration with GetX, and practical examples for tokens, user data, and expirations. The content helps teams adopt a consistent, type-safe approach to local storage and cache invalidation.
Initialize GetStorage at app startup, then use a dedicated StorageService for common keys (tokens, preferences, locale) to centralize reads/writes and listeners. For domain data, use a UserLocalDataSource that stores payloads alongside timestamps and enforces cache expiration before returning stale data. Optionally wrap storage in a GetxService CacheService to maintain a reactive in-memory mirror and support per-key expiry objects.
Do I need to encrypt sensitive data stored with GetStorage?
Yes—GetStorage is not encrypted by default. For sensitive tokens or PII, layer platform encryption or use a secure storage plugin.
How do I handle migrations when changing stored shapes?
Version your keys or include a schema version in stored objects. On init, detect mismatches and transform or clear old entries.
Can I combine GetStorage with remote sync?
Yes—use cache timestamps to decide when to fetch fresh data, and write back successful remote responses to local storage for offline access.