flutter_cached 3.2.0 flutter_cached: ^3.2.0 copied to clipboard
🧾 Flutter widget allowing cache-based data display featuring swipe-to-refresh and an error banner.
When building an app that caches data, there are many special cases to think about.
For example, according to the Material Design guidelines, you need to worry about displaying offline data, handling swipe to refresh, showing error banners and displaying empty states.
This package aims to make implementing cached Flutter apps as easy as possible.
Usually, the frontend asks the backend to fetch data and the backend then offers a continuous Stream
of data to your frontend.
When listening to bare-bone streams in Dart, you usually have to work with AsyncSnapshot
s quite often.
This package offers a new, augmented type of communication path between backend and frontend designed around the providing of cached data.
CacheUpdate
#
This type provides not only information about data and if it succeeded (like a normal AsyncSnapshot
), but also contains information about whether the original source is still fetching or not and a stack trace.
For comparison, here are the possible states of the AsyncSnapshot
:
no data | has data | |
---|---|---|
no error | nothing | with data |
has error | with error | - |
Here are the possible states of a CacheUpdate
:
no data | has data | ||
---|---|---|---|
fetching | no error | loading | cached |
fetching | has error | - | - |
not fetching | no error | initial | success |
not fetching | has error | error | error but cache |
Now that looks like an upgraded version of AsyncSnapshot
!
CacheController #
The CacheController
glues together the logic for fetching data with the UI.
You can call fetch
on a CacheController
to fetch data and call updates
to get a stream of CacheUpdate
s.
There are also more advanced CacheController
s like the PaginatedController
, which allow more actions like fetchMore()
.
Usage #
First, create a CacheController
. This will be the class that orchestrates the fetching of data.
var controller = CacheController<Item>(
// Does the actual work and returns a Future<Item>.
fetcher: _downloadData,
// Asynchronously saves an Item to the cache.
saveToCache: _saveToCache,
// Asynchronously loads a Item from the cache.
loadFromCache: _loadFromCache,
);
Then, you can use a CachedBuilder
and provide builders for all the special cases:
CachedBuilder(
controller: controller,
errorBannerBuilder: (context, error, stackTrace) => ...,
errorScreenBuilder: (context, error, stackTrace) => ...,
builder: (context, item) => ...,
),
And that's it!
Note: By default, the
CachedBuilder
assumes that thebuilder
returns a scrollable widget, like aListView
orGridView
. If that's not the case, you need to sethasScrollBody
tofalse
in order for the swipe to refresh to work.
If you want to create a CacheController
on the fly, you can also provide a controllerBuilder
. Then, you'll also won't have to worry about disposing it.
CachedBuilder(
controllerBuilder: () => CacheController(
saveToCache: ...,
loadFromCache: ...,
fetcher: ...,
),
errorBannerBuilder: (context, error, stackTrace) => ...,
errorScreenBuilder: (context, error, stackTrace) => ...,
builder: (context, item) => ...,
),
How does it work? #
The CacheController
offers an updates
stream of CacheUpdate
s which is filled with updates once controller.fetch()
is called.
If you want to react to every update manually, you can use CachedRawBuilder
, which takes a builder with context
and an update
.