Appearance
📦 Caching & Offline Strategy Standards
Table of Contents
- Core Standards
- Cache Configuration
- Cache Policies
- Implementation Guidelines
- Offline Handling
- Common Violations
Core Standards
Cache Setup Rules
- Always use
dio_cache_interceptorfor HTTP caching - Configure cache store with appropriate size limits
- Set cache policies based on data sensitivity
- Use cache keys for manual cache management
- Implement cache cleanup strategies
- Handle cache errors gracefully
dart
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cache_interceptor_mem_store/dio_cache_interceptor_mem_store.dart';
// Memory cache store configuration
final cacheStore = MemCacheStore(
maxSize: 10485760, // 10MB cache size
maxEntrySize: 1048576, // 1MB per entry
);
// Cache options
final cacheOptions = CacheOptions(
store: cacheStore,
policy: CachePolicy.request,
hitCacheOnErrorExcept: [401, 403],
maxStale: const Duration(hours: 2),
priority: CachePriority.normal,
keyBuilder: (request) => request.uri.toString(),
);Dio Configuration Rules
- Add cache interceptor to Dio instance
- Configure cache options per request or globally
- Use appropriate cache policies for different endpoints
- Handle cache headers properly
- Implement cache invalidation when needed
dart
import 'package:dio/dio.dart';
final dio = Dio();
// Add cache interceptor
dio.interceptors.add(DioCacheInterceptor(options: cacheOptions));
// Per-request cache configuration
final response = await dio.get(
'/api/users',
options: CacheOptions(
policy: CachePolicy.cacheFirst,
maxStale: const Duration(hours: 1),
).toOptions(),
);Cache Configuration
Cache Store Setup
- Use MemCacheStore for fast in-memory caching
- Configure appropriate cache size based on app needs
- Set cache expiration policies
- Implement cache cleanup mechanisms
- Handle cache storage errors
dart
// Configure memory cache store
final cacheStore = MemCacheStore(
maxSize: 10485760, // 10MB total cache size
maxEntrySize: 1048576, // 1MB per cache entry
);
// Cache options with store
final cacheOptions = CacheOptions(
store: cacheStore,
policy: CachePolicy.request,
maxStale: const Duration(hours: 4),
hitCacheOnErrorExcept: [401, 403, 500],
);Cache Size Management
- Set maximum cache size based on available memory
- Implement LRU eviction for cache management
- Monitor cache usage and cleanup when needed
- Use appropriate cache priorities for different data types
dart
// Memory cache size configuration
final cacheStore = MemCacheStore(
maxSize: 20971520, // 20MB total cache size
maxEntrySize: 2097152, // 2MB per cache entry
);
final cacheOptions = CacheOptions(
store: cacheStore,
policy: CachePolicy.request,
maxStale: const Duration(hours: 6),
priority: CachePriority.high, // For critical data
keyBuilder: (request) {
// Custom cache key generation
return '${request.method}_${request.uri.path}_${request.queryParameters}';
},
);Cache Policies
Policy Selection Rules
- Use
CachePolicy.requestfor most API calls - Use
CachePolicy.cacheFirstfor static data - Use
CachePolicy.networkFirstfor real-time data - Use
CachePolicy.cacheOnlyfor offline-only data - Use
CachePolicy.noCachefor sensitive data
dart
// Different cache policies for different data types
class CachePolicyConfig {
// Static data - cache first
static const staticData = CacheOptions(
policy: CachePolicy.cacheFirst,
maxStale: Duration(days: 30),
);
// User data - network first
static const userData = CacheOptions(
policy: CachePolicy.networkFirst,
maxStale: Duration(hours: 1),
);
// Real-time data - no cache
static const realTimeData = CacheOptions(
policy: CachePolicy.noCache,
);
// Offline data - cache only
static const offlineData = CacheOptions(
policy: CachePolicy.cacheOnly,
);
}Cache Duration Rules
- Use short durations for frequently changing data
- Use long durations for static content
- Use medium durations for user-specific data
- Consider data freshness requirements
- Implement cache refresh strategies
dart
// Memory cache duration configuration
final cacheOptions = CacheOptions(
store: MemCacheStore(maxSize: 10485760), // 10MB cache
policy: CachePolicy.request,
maxStale: const Duration(hours: 2), // Data can be 2 hours old
hitCacheOnErrorExcept: [401, 403], // Use cache on network errors
priority: CachePriority.normal,
);Implementation Guidelines
Repository Integration
- Configure cache options in repository methods
- Use appropriate cache policies per endpoint
- Handle cache hits and misses properly
- Implement cache invalidation when data changes
- Monitor cache performance
dart
class UserRepository {
final Dio _dio;
UserRepository(this._dio);
Future<List<User>> getUsers() async {
try {
final response = await _dio.get(
'/api/users',
options: CacheOptions(
policy: CachePolicy.networkFirst,
maxStale: const Duration(minutes: 30),
keyBuilder: (request) => 'users_list',
).toOptions(),
);
return (response.data as List)
.map((json) => User.fromJson(json))
.toList();
} catch (e) {
// Handle cache miss or network error
throw Exception('Failed to fetch users: $e');
}
}
Future<void> createUser(User user) async {
await _dio.post('/api/users', data: user.toJson());
// Invalidate users cache after creation
await _dio.delete(
'/api/users',
options: CacheOptions(
policy: CachePolicy.noCache,
keyBuilder: (request) => 'users_list',
).toOptions(),
);
}
}Cache Key Management
- Use descriptive cache keys for easy identification
- Include request parameters in cache keys
- Use consistent key patterns across the app
- Implement key versioning for cache invalidation
- Handle key conflicts properly
dart
// Cache key builder
String buildCacheKey(String endpoint, Map<String, dynamic>? params) {
final key = StringBuffer(endpoint);
if (params != null && params.isNotEmpty) {
key.write('_');
key.write(params.entries
.map((e) => '${e.key}=${e.value}')
.join('&'));
}
return key.toString();
}
// Usage in repository
final cacheKey = buildCacheKey('/api/users', {'page': 1, 'limit': 10});Offline Handling
Offline Detection Rules
- Check network connectivity before making requests
- Use cached data when offline
- Show appropriate UI states for offline mode
- Implement offline indicators for users
- Handle offline data synchronization
dart
import 'package:connectivity_plus/connectivity_plus.dart';
class NetworkService {
final Connectivity _connectivity = Connectivity();
Future<bool> isOnline() async {
final result = await _connectivity.checkConnectivity();
return result != ConnectivityResult.none;
}
Future<T> getDataWithOfflineSupport<T>(
String endpoint,
T Function(Map<String, dynamic>) fromJson,
CacheOptions cacheOptions,
) async {
final isConnected = await isOnline();
if (!isConnected) {
// Force cache-only policy when offline
cacheOptions = cacheOptions.copyWith(
policy: CachePolicy.cacheOnly,
);
}
final response = await _dio.get(
endpoint,
options: cacheOptions.toOptions(),
);
return fromJson(response.data);
}
}Offline Data Management
- Cache critical data for offline access
- Implement offline-first strategies for key features
- Use background sync when connection is restored
- Handle offline data conflicts properly
- Provide offline indicators in UI
dart
class OfflineDataManager {
final Dio _dio;
final CacheOptions _offlineCacheOptions;
OfflineDataManager(this._dio)
: _offlineCacheOptions = CacheOptions(
store: MemCacheStore(maxSize: 5242880), // 5MB for offline data
policy: CachePolicy.cacheOnly,
maxStale: const Duration(hours: 24), // Keep data for 24 hours
);
Future<List<T>> getOfflineData<T>(
String endpoint,
T Function(Map<String, dynamic>) fromJson,
) async {
try {
final response = await _dio.get(
endpoint,
options: _offlineCacheOptions.toOptions(),
);
return (response.data as List)
.map((json) => fromJson(json))
.toList();
} catch (e) {
// Return empty list if no cached data
return [];
}
}
}Common Violations
❌ DO NOT Violate These Rules
- Don't cache sensitive data without encryption
- Don't use excessive cache durations for real-time data
- Don't ignore cache invalidation after data updates
- Don't use
CachePolicy.noCachefor static data - Don't forget to handle cache errors gracefully
- Don't use inconsistent cache keys across the app
- Don't ignore offline scenarios in cache configuration
- Don't set excessive memory cache sizes that could cause OOM
- Don't use cache for authentication tokens without expiration
- Don't forget that memory cache is lost on app restart
✅ ALWAYS Follow These Rules
- Use appropriate cache policies based on data type
- Set reasonable cache durations for different data
- Implement cache invalidation after data mutations
- Use descriptive cache keys for easy management
- Handle offline scenarios with proper cache configuration
- Monitor cache performance and usage
- Use cache priorities to manage storage efficiently
- Set appropriate memory limits for cache size
- Test cache behavior in offline/online scenarios
- Remember that memory cache is temporary and app-scoped