Loading...
Loading...
Cache data in a Flutter app
npx skill4agent add flutter/skills flutter-cachingshared_preferencessqflitepath_providercached_network_imageImageCacheshared_preferencespath_providerdart:ioimport 'dart:io';
import 'package:path_provider/path_provider.dart';
class FileCacheService {
Future<String> get _localPath async {
// Use getTemporaryDirectory() for system-cleared cache
// Use getApplicationDocumentsDirectory() for persistent data
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/cached_data.json');
}
Future<File> writeData(String data) async {
final file = await _localFile;
return file.writeAsString(data);
}
Future<String?> readData() async {
try {
final file = await _localFile;
return await file.readAsString();
} catch (e) {
return null; // Cache miss
}
}
}sqfliteimport 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DatabaseService {
late Database _database;
Future<void> initDB() async {
_database = await openDatabase(
join(await getDatabasesPath(), 'app_cache.db'),
onCreate: (db, version) {
return db.execute(
'CREATE TABLE cache_data(id INTEGER PRIMARY KEY, key TEXT, payload TEXT)',
);
},
version: 1,
);
}
Future<void> insertCache(String key, String payload) async {
await _database.insert(
'cache_data',
{'key': key, 'payload': payload},
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<String?> getCache(String key) async {
final List<Map<String, Object?>> maps = await _database.query(
'cache_data',
where: 'key = ?',
whereArgs: [key], // MUST use whereArgs to prevent SQL injection
);
if (maps.isNotEmpty) {
return maps.first['payload'] as String;
}
return null;
}
}Stream<UserProfile> getUserProfile() async* {
// 1. Check local cache
final localData = await _databaseService.getCache('user_profile');
if (localData != null) {
yield UserProfile.fromJson(localData);
}
// 2. Fetch remote data
try {
final remoteData = await _apiClient.fetchUserProfile();
// 3. Update cache
await _databaseService.insertCache('user_profile', remoteData.toJson());
// 4. Yield fresh data
yield remoteData;
} catch (e) {
// Handle network failure; local data has already been yielded
}
}FlutterEngineclass MyApplication : Application() {
lateinit var flutterEngine : FlutterEngine
override fun onCreate() {
super.onCreate()
flutterEngine = FlutterEngine(this)
// Optional: Configure initial route before executing entrypoint
flutterEngine.navigationChannel.setInitialRoute("/cached_route");
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache.getInstance().put("my_engine_id", flutterEngine)
}
}// For Activity
startActivity(
FlutterActivity
.withCachedEngine("my_engine_id")
.backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
.build(this)
)
// For Fragment
val flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
.renderMode(FlutterView.RenderMode.texture)
.shouldAttachEngineToActivity(false)
.build()containsKeyclass CustomImageCache extends ImageCache {
bool containsKey(Object key) {
// Check if cache is tracking this key
return super.containsKey(key);
}
}ScrollCacheExtentcacheExtentcacheExtentStyleListView(
// Use ScrollCacheExtent.pixels for pixel-based caching
scrollCacheExtent: const ScrollCacheExtent.pixels(500.0),
children: [ ... ],
)
Viewport(
// Use ScrollCacheExtent.viewport for fraction-based caching
scrollCacheExtent: const ScrollCacheExtent.viewport(0.5),
slivers: [ ... ],
)saveLayer()OpacityShaderMaskColorFilterClip.antiAliasWithSaveLayerOpacityFadeInImageoperator ==operator ==WidgetListViewGridViewListView.builderwhereArgssqflitewhereFlutterEngineActivityFragmentFlutterEngine.destroy()ImageCache.maxByteSizeoperator ==constcacheExtentcacheExtentStyleScrollCacheExtentisolates