flutter_cache_store 0.8.1 flutter_cache_store: ^0.8.1 copied to clipboard
More configurable cache manager for Flutter. Cache http get requests to mobile devices file system.
flutter_cache_store #
A flexible cache manager for Flutter.
This package is highly inspired by flutter_cache_manager. Can be easily switched to each other.
Quick Start #
import 'package:flutter_cache_store/flutter_cache_store.dart';
void demo(String url) async {
final store = await CacheStore.getInstance();
final file = await store.getFile(url);
// do something with file...
}
APIs #
CacheStore #
void api() async {
// get store instance
CacheStore store = await CacheStore.getInstance(
namespace: 'unique_name', // default: null - valid filename used as unique id
policy: LeastFrequentlyUsedPolicy(), // default: null - will use `LessRecentlyUsedPolicy()`
clearNow: true, // default: false - whether to clean up immediately
fetch: myFetch, // default: null - a shortcut of `CacheStore.fetch`
);
// You can change custom fetch method at anytime.
// Set it to `null` will simply use `http.get`
store.fetch = myFetch;
// fetch a file from an URL and cache it
File file = await store.getFile(
'url', // GET method
key: null, // use custom string instead of URL
headers: {}, // same as http.get
fetch: myFetch, // Optional: CustomFunction for making custom request
// Optional: Map<String, dynamic> any custom you want to pass to your custom fetch function.
custom: {'method': 'POST', 'body': 'test'},
flushCache: false, // whether to re-download the file
);
// flush specific files by keys
await store.flush([
'key', // key (default is the URL) passed to `getFile`
]);
// remove all cached files
await store.clearAll();
}
// Custom fetch function.
// A demo of how you can achieve a fetch supporting POST with body
Future<Response> myFetch(url,
{Map<String, String> headers, Map<String, dynamic> custom}) {
final data = custom ?? {};
switch (data['method'] ?? '') {
case 'POST':
{
return post(url, headers: headers, body: data['body']);
}
default:
return get(url, headers: headers);
}
}
Cache File Structure #
By default, the cached files will be saved under $TEMP/cache_store
. $TEMP
is generated by path_provider. The default temp filenames are timestamp-based 11 chars.
You can customize your own file structure under $TEMP/cache_store
by overriding Policy. There is an example:
Let's suppose your are developing a reader for novels, and your app will cache chapters of books. Your API returns some IDs of books and chapters, and an ID only contains letters and digits.
// Extends a Policy class and override `generateFilename`
class LRUCachePolicy extends LessRecentlyUsedPolicy {
LRUCachePolicy({int maxCount}) : super(maxCount: maxCount);
@override
String generateFilename({final String key, final String url}) =>
key; // use key as the filename
}
void customizedCacheFileStructure() async {
// get store instance
CacheStore store = await CacheStore.getInstance(
policy: LRUCachePolicy(maxCount: 4096),
namespace: 'my_store',
);
// fetch a file from an URL and cache it
String bookId = 'book123';
String chapterId = 'ch42';
String chapterUrl = 'https://example.com/book123/ch42';
File file = await store.getFile(
chapterUrl,
key: '$bookId/$chapterId', // use IDs as path and filename
);
// Your file will be cached as `$TEMP/cache_store__my_store/book123/ch42`
}
Cache Policy #
-
LessRecentlyUsedPolicy
LRU policy. Less Recently Used files will be removed when reached
maxCount
. Each time you access a file will update its used timestamp.new LessRecentlyUsedPolicy( maxCount: 999, );
-
LeastFrequentlyUsedPolicy
LFU policy. Least Frequently Used files will be removed when reached
maxCount
. Each time you access a file will increase its hit count. AfterhitAge
time the hit will expire. It will fallback to LRU policy if files have same hit count.new LeastFrequentlyUsedPolicy( maxCount: 999, hitAge: Duration(days: 30), );
-
CacheControlPolicy
Cache-Control
header policy. This policy generally followsmax-age=<seconds>
ors-maxage=<seconds>
rules of http response header Cache-Control. If the max-age-seconds not been set, it will useminAge
unless you set it tonull
. The age will not be longer thanmaxAge
.new CacheControlPolicy( maxCount: 999, minAge: Duration(seconds: 30), // nullable maxAge: Duration(days: 30), // nullable );
-
FifoPolicy
First-In, First-Out policy, super simple and maybe for example only.
new FifoPolicy( maxCount: 999, );
Performance Warning #
-
The implementation maintains all key-item in memory to improve the speed. So
maxCount
must between 1 and 100000 (100k) due to the cost of RAM and file system. -
Currently, all the policies simply sort all items to expire files. It may hit performance due to
O(N*logN)
complexity.Will switch to priority queue which has
O(N*logK)
whileK
usually is a very small number.
How to implement your own policy #
The interface is a simple abstract class. You only have to implement a few methods.
abstract class CacheStorePolicy {
// IT'S THE ONLY METHOD YOU HAVE TO IMPLEMENT.
// `store` will invoke this method from time to time.
// Make sure return all expired items at once.
// then `store` will manage to remove the cached files.
// you also have to save your data if need to persist some data.
Future<Iterable<CacheItem>> cleanup(Iterable<CacheItem> allItems);
// will be invoked when store.clearAll called.
Future<void> clearAll(Iterable<CacheItem> allItems) async {}
// will invoke only once when the `store` is created and load saved data.
// you need to load persistent data and restore items' payload.
// only returned items will be cached. others will be recycled later.
Future<Iterable<CacheItem>> restore(List<CacheItem> allItems) async => allItems;
// event when a new `CacheItem` has been added to the cache.
// you may need to attach a `CacheItemPayload` instance to it.
Future<void> onAdded(final CacheItem addedItem) async {}
// event when an item just been accessed.
// you may need to attach or update item's payload.
Future<void> onAccessed(final CacheItem accessedItem, bool flushed) async {}
// event when a request just finished.
// the response headers will be passed as well.
Future<void> onDownloaded(final CacheItem item, final Map<String, String> headers) async {}
// event when `store.flush` has called
Future<void> onFlushed(final Iterable<CacheItem> flushedItems) async {}
// filename (including path) relative to `CacheItem.rootPath`.
// usually ignore this unless need a better files structure.
// you must provide valid filenames.
String generateFilename({final String key, final String url}) =>
Utils.genName(); // timestamp based random filename
}
-
Tips
You don't have to implement all of the
onAdded
,onAccessed
andonDownloaded
.