# flutter_cache_store **Repository Path**: ext-project/flutter_cache_store ## Basic Information - **Project Name**: flutter_cache_store - **Description**: More configurable cache manager for Flutter - **Primary Language**: Unknown - **License**: BSD-3-Clause - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-03-04 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # flutter_cache_store A flexible cache manager for Flutter. This package is highly inspired by [flutter_cache_manager](https://pub.dartlang.org/packages/flutter_cache_manager). Can be easily switched to each other. ## Quick Start ```dart 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 ```dart 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 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 myFetch(url, {Map headers, Map 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](https://pub.dartlang.org/packages/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. ```dart // 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. ```dart 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. After `hitAge` time the hit will expire. It will fallback to LRU policy if files have same hit count. ```dart new LeastFrequentlyUsedPolicy( maxCount: 999, hitAge: Duration(days: 30), ); ``` - `CacheControlPolicy` `Cache-Control` header policy. This policy generally follows `max-age=` or `s-maxage=` rules of http response header [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control). If the max-age-seconds not been set, it will use `minAge` unless you set it to `null`. The age will not be longer than `maxAge`. ```dart 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. ```dart 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)` while `K` 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. ```dart 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> cleanup(Iterable allItems); // will be invoked when store.clearAll called. Future clearAll(Iterable 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> restore(List 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 onAdded(final CacheItem addedItem) async {} // event when an item just been accessed. // you may need to attach or update item's payload. Future onAccessed(final CacheItem accessedItem, bool flushed) async {} // event when a request just finished. // the response headers will be passed as well. Future onDownloaded(final CacheItem item, final Map headers) async {} // event when `store.flush` has called Future onFlushed(final Iterable 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` and `onDownloaded`.