Flutter widget for fetching, caching and refetching asynchronous data.

Overview

Flutter library for fetching, caching and invalidating asynchronous data

Quick Features

  • Fetch asynchronous data
  • Data invalidation
  • Optimistic response
  • Reset cache

Motivation

How to do API calls in Flutter? Probably, majority would answer by using Dio.
But the real question would be, how to integrate API calls in Flutter arhitecture seamless? One way would be to use FutureBuilder or maybe Bloc like most of the community do.

The thing is, both FutureBuilder and Bloc have flaws.
For example, FutureBuilder is too simple. It does provide status of your query, but communicating with some FutureBuilder is impossible outside of the scope of the current screen.
Problem with Bloc is that will include so many boilerplate code for a simple API call but again, talking to the Bloc is impossible from other screens. Bloc should be number one choice for complicated workflows, but what if you don't have any complex business logic?

flutter_requery to the rescue!

Example

Let's start with this simple example.

> _getData() async { await Future.delayed(Duration(seconds: 1)); return data; } /// later, used in the build method Query >( cacheKey, future: _getData, builder: (context, response) { if (response.error != null) { return Text('Error'); } if (response.loading) { return Text('Loading...'); } final children = response.data.map((str) => Text(str)).toList(); return ListView( children: children ); }, ); /// and later when you want to invalidate your data void _onPress() async { await Future.delayed(Duration(seconds: 1)); data.add("World"); queryCache.invalidateQueries(cacheKey); } ">
/// define data and cache key
final List<String> data = ["Hello"];
final String cacheKey = 'strKey';

/// simulates API call, therefore response is delayed
Future<List<String>> _getData() async {
  await Future.delayed(Duration(seconds: 1));
  return data;
}

/// later, used in the build method
Query<List<String>>(
  cacheKey,
  future: _getData,
  builder: (context, response) {
    if (response.error != null) {
      return Text('Error');
    }
    if (response.loading) {
      return Text('Loading...');
    }
    final children = response.data.map((str) => Text(str)).toList();
    return ListView(
      children: children
    );
  },
);

/// and later when you want to invalidate your data
void _onPress() async {
  await Future.delayed(Duration(seconds: 1));
  data.add("World");
  queryCache.invalidateQueries(cacheKey);
}

And that's the gist of it. You can find complete example on pub.dev.

Usage

Cache key

Every query needs a cache key.
Under this key your data will be stored in cache. It can be string, int or list of strings and ints.

/// good
const k1 = 'myKey';
const k2 = 1;
const k3 = ["data", 1]

/// bad
const k1 = 1.0;
const k2 = true;
const k3 = ["data", true];

Idea behind having keys specified as list is that you can invalidate your queries more intelligently.
Take a look at invalidation chapter for more details.

Query

Once the cache key is defined, next step is to write the query.
Query takes 3 arguments:

  • cacheKey - look here for more info.
  • future - async action which will be executed.
  • builder - follows the Flutter builder pattern. First parameter is BuildContext followed by QueryResponse object.

QueryResponse manages query status. It also has 3 properties:

  • data - response received from the future or null if the exception occured.
  • loading - boolean, true if the query is running. Otherwise, it's false.
  • error - represents an error received from the future. If the future was successful, error will be null.
Text(str)).toList(); return ListView( children: children ); }, ); ">
// use Query widget in the build method
Query<List<String>>(
  'myCacheKey',
  future: ()async {
    await Future.delayed(Duration(seconds: 1));
    return ["Hello"]
  }
  builder: (context, QueryResponse response) {
    /// error state
    if (response.error != null) {
      return Text('Error');
    }
    /// loading state
    if (response.loading) {
      return Text('Loading...');
    }
    final children = response.data.map((str) => Text(str)).toList();
    return ListView(
      children: children
    );
  },
);

Invalidation

Data invalidation can come in two different forms.
You can either afford to wait for the API response or you simply need to show the newest data as soon as possible. If you are interested in following, check the next chapter.

Waiting for the API response is more common and flutter_requery supports this by using the queryCache instance. It's global and already defined by the library. Invalidate your query by passing the cache keys.

// invalidates strKey query
queryCache.invalidateQueries('strKey');

// support for bulk invalidation
queryCache.invalidateQueries(['strKey1', 'strKey2']);

// if your keys are lists, end result would be similar to
queryCache.invalidateQueries([
  ['strKey', 1],
  ['strKey2', 2]
]);

Once query is invalidated, every Query widget subscribed for that query will execute future again and rebuild its children with the new data.

For cache-level invalidation use:

// invalidate every query stored in cache
queryCache.invalidateAll()

Invalidation works in pair with the keys defined as lists. Cache keys defined as list must be looked upon in a hierarchical manner where the list elements defined before are ancestors of the elements that come after. For example:

// requests is ancestor of 1
const key1 = ["requests", 1]

Reasoning behind this is to support hierarchical invalidation. Sometimes it can get cumbersome managing invalidations and therefore developer can decide to cleverly name keys to support this. For example:

const k1 = ["requests", 1]
const k2 = ["requests", 2]
const k3 = "requests"

// without hierarchical invalidation you need to call
queryCache.invalidateQueries([
  ["requests", 1], ["requests", 2], "requests"
]);

// but with hierarchical invalidation you only need to call
queryCache.invalidateQueries("requests");

Optimistic response

Sometimes waiting period for the API response to be available is too long. Therefore you can immediately update the cache data and rebuild your widget tree by using the optimistic response. Make sure to remove await keyword before the API call since this will block the thread.

  queryCache.setOptimistic("requests", [...oldData, newData]);

Reset

In short, reset can be explained as cache-level invalidation without rebuilding the widget tree.
Also, async actions won't be ran immediately but only when the new Query widget is mounted or the cacheKey has changed. This is particularly useful for the log out action.

queryCache.reset();

Summary

API Description
Query Flutter widget used for data-fetching operations.
queryCache.invalidateQueries Invalidates query specified by cache key and rebuilds the widget tree.
queryCache.invalidateAll Invalidates every query stored in cache and rebuilds the widget tree.
queryCache.setOptimistic Set cache data manually and rebuild the widget tree.
queryCache.reset Invalidates every query stored in cache without rebuilding the widget tree.
You might also like...

Data Tables for Flutter

Data Tables for Flutter

data_tables Full Screen Paginated Data Tables for Tablets/Desktops Mobile ListView with Action Buttons for Sorting and Selecting All Supports Dark Mod

Dec 27, 2022

The SpannableGrid is a Flutter widget that allows its cells to span columns and rows and supports moving cells inside the grid.

The SpannableGrid is a Flutter widget that allows its cells to span columns and rows and supports moving cells inside the grid.

Spannable Grid The SpannableGrid is a Flutter widget that allows its cells to span columns and rows and supports moving cells inside the grid. Feature

Nov 7, 2022

A widget that can be dragged and scrolled in a single gesture and snapped to a list of extents.

A widget that can be dragged and scrolled in a single gesture and snapped to a list of extents.

Sliding Sheet A widget that can be dragged and scrolled in a single gesture and snapped to a list of extents. Click here to view the full example. Ins

Mar 10, 2022

React hooks for Flutter. Hooks are a new kind of object that manages a Widget life-cycles. They are used to increase code sharing between widgets and as a complete replacement for StatefulWidget.

English | Português Flutter Hooks A Flutter implementation of React hooks: https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889 Ho

Dec 29, 2022

Flutter debug helper widget with common and custom actions

Flutter debug helper widget with common and custom actions

Flutter debug helper widget with common and custom actions

Dec 7, 2022

A draggable Flutter widget that makes implementing a Sliding up and fully-stretchable much easier.

A draggable Flutter widget that makes implementing a Sliding up and fully-stretchable much easier.

Draggable Home A draggable Flutter widget that makes implementing a Sliding up and fully-stretchable much easier! Based on the Scaffold and Sliver. Us

Dec 12, 2022

An extensive snap tool/widget for Flutter that allows very flexible snap management and snapping between your widgets.

An extensive snap tool/widget for Flutter that allows very flexible snap management and snapping between your widgets.

Dec 16, 2022

Flutter Package: When your desired layout or animation is too complex for Columns and Rows, this widget lets you position/size/rotate/transform its child in complex ways.

Flutter Package: When your desired layout or animation is too complex for Columns and Rows, this widget lets you position/size/rotate/transform its child in complex ways.

align_positioned Widgets in this package: AlignPositioned AnimatedAlignPositioned AnimChain Why are these widgets an indispensable tool? When your des

Dec 12, 2022

Flutter custom widget to make a group buttons. Included Radio and CheckBox buttons.

Flutter custom widget to make a group buttons. Included Radio and CheckBox buttons.

Flutter widget to create a group of buttons fast 🚀 Included Radio and CheckBox buttons models with custom groping types 🤤 Show some ❤️ and star the

Dec 26, 2022
Comments
  • add hive or similar db and loading builder

    add hive or similar db and loading builder

    hi thanx for the package! I wander it is possible to add a permanent db than when you refresh the page it don't need to load the data from the server again, and another thing I saw that need to do something like that if (response.error != null) { return Text('Error'); } if (response.loading) { return Text('Loading...'); } if you want to handle loading, wander if you can add loadingBuilder or something like that is save to do the if statement every use if you want help with that I can do my best to help. thank you

    opened by hmbenhaim 2
Owner
null
A flutter carousel widget, support infinite scroll, and custom child widget.

carousel_slider A carousel slider widget. Features Infinite scroll Custom child widgets Auto play Supported platforms Flutter Android Flutter iOS Flut

Bart T 1 Nov 25, 2021
Progress Dialog widget for flutter projects with ability to customize loading widget, background color and background blur.

DISCONTINUED Checkout ArsDialog ars_progress_dialog Customizable progress dialog for Flutter applications with smooth animation for background dim col

Arsam 8 Apr 15, 2022
A simple Flutter widget to add in the widget tree when you want to show nothing, with minimal impact on performance.

nil A simple widget to add in the widget tree when you want to show nothing, with minimal impact on performance. Why? Sometimes, according to a condit

Romain Rastel 127 Dec 22, 2022
A Flutter Widget to make interactive timeline widget.

Bubble Timeline Package A Flutter Widget to make interactive timeline widget. This widget can be used to make Event Timelines, or Timelines for certai

Vansh Goel 12 Sep 22, 2022
📸 Easy to use yet very customizable zoomable image widget for Flutter, Photo View provides a gesture sensitive zoomable widget.

?? Easy to use yet very customizable zoomable image widget for Flutter, Photo View provides a gesture sensitive zoomable widget. Photo View is largely used to show interacive images and other stuff such as SVG.

Blue Fire 1.7k Jan 7, 2023
A widget lib that the widget in this lib can react to flutter ScrollController's offset

Language: English | 中文简体 linked_scroll_widgets A lib full of widgets that can react to the scrollController's offset change,to custom your UI effect.

WenJingRui 8 Oct 16, 2022
Full customable rolling switch widget for flutter apps based on Pedro Massango's 'crazy-switch' widget

lite_rolling_switch Full customable rolling switch widget for flutter apps based on Pedro Massango's 'crazy-switch' widget https://github.com/pedromas

Eduardo Muñoz 48 Dec 1, 2022
A Flutter widget that will give a Glitch Animation Effect to it's child widget.

GlitchEffect A Flutter widget that will give a Glitch Animation Effect to it's child widget. Installation Add the latest version of package to your pu

Sameer Singh 6 Nov 25, 2022
Widget, that can make any static located widget hidable

Installing See the official installing guidline from hidable/install Usage & Overview To start using Hidable widget, we have to create a ScrollControl

Anon 18 Dec 16, 2022
A widget that allow user resize the widget with drag

Flutter-Resizable-Widget A widget that allow user resize the widget with drag Note: this widget uses Getx Example bandicam.2021-11-11.12-34-41-056.mp4

MohammadAminZamani.afshar 22 Dec 13, 2022