⚡FQuery is a powerful async state management solution for flutter. It caches, updates and fully manages asynchronous data in your flutter apps.

Overview

FQuery is a powerful async state management solution for flutter. It caches, updates and fully manages asynchronous data in your flutter apps.

It can be used for managing server state (REST API, GraphQL, etc), local databses like SQLite, or just anything that is async, just give it a Future and you are good to go.

🌌 Features

  • Fully customizable
  • No boilerplate code and easy to use
  • Data feching logic agnostic
  • Automatic caching
  • Garbage collection
  • Auto refetching stale data
  • State data invalidation
  • Manual updates
  • Dependant queries
  • Parallel queries

Problem definition

Let me ask you a simple question, How do you manage server state in your flutter apps? Majority developers will answer that they use Riverpod, Bloc, FutureBuilder, or any other general purpose state management solution. This usually results in writing a lot of boilerplate code and repeating data fetching, caching, and other logic over and over again.

The thing is, existing state management solutions are very general and are suited for anything that's a global state in you app and hence the term "general", but do not work great when used for asynchronous state like server state, this is because server state is way too different. Server state is -

  • Asynchrounous state and requires asynchronous APIs for fetching and updating)
  • Stored in a remote location and can be changed without your knowledge, from just anywhere in the world and this alone means a lot, staying synchronized with the data and making sure that it is not stale

How does FQuery tackle this problem?

FQuery is powerd by flutter_hooks. It is very similar to swr and react-query. It provides you easy to use hooks. Just tell it where to get the data by giving it a Future and the rest is automatic. It can be fully configured to match your needs, you can configure each and every thing.

📄 Example

Here's a very simple widget that makes use of the useQuery hook:

class Posts extends HookWidget {
  const Posts({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final posts = useQuery(['posts'], getPosts);

    return Builder(
      builder: (context) {
        if (posts.isLoading) {
          return const Center(child: CircularProgressIndicator());
        }

        if (posts.isError) {
          return Center(child: Text(posts.error!.toString()));
        }

        return ListView.builder(
          itemCount: posts.data!.length,
          itemBuilder: (context, index) {
            final post = posts.data![index];
            return ListTile(
              title: Text(post.title),
            );
          },
        );
      },
    );
  }
}

??‍💻 Usage

You'll need to install flutter_hooks before you can start using this library. You'll need to wrap your entire app inside a QueryClientProvider and you are good to go.

void main() {
  runApp(
    QueryClientProvider(
      queryClient: queryClient,
      child: CupertinoApp(

Queries

To query data in your widgets, you'll need to extend the widget using HookWidget or StatefulHookWidget(for stateful widgets). These classes are exported from the flutter_hooks pacakge.

A query instance is a subscription to an asynchronous data stored in the cache. Every query needs -

  • A Query key, it uniquely identifies the query stored in the cache.
  • A Future that either resolves or throws an error

The same query key can be used in multiple instances of useQuery hook and the data will be shared throughout the app.

Future<List<Post>> getPosts() async {
  final res = await Dio().get('https://jsonplaceholder.typicode.com/posts');
  return (res.data as List)
      .map((e) => Post.fromJson(e as Map<String, dynamic>))
      .toList();
}

class Posts extends HookWidget {
  const Posts({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final posts = useQuery(['posts'], getPosts);

The returned value of the useQuery hook is an instance of UseQueryResult and contains all the information related to that query. A Builder widget comes in handy when rendering the results.

// The query has no data to display
if (posts.isLoading) {
  return const Center(child: CircularProgressIndicator());
}

// An error has occurred
if (posts.isError) {
  return Center(child: Text(posts.error!.toString()));
}

// Success, data is ready to display
return ListView.builder(
  itemCount: posts.data!.length,
  itemBuilder: (context, index) {
    final post = posts.data![index];
    return ListTile(
      title: Text(post.title),
    );
  },
);

Query configuration

A query is fully customizable to match your needs, these configurations can be passed as named parameters into the useQuery hook

// These are default configurations
final posts = useQuery(
  ['posts'],
  getPosts,
  enabled: true,
  cacheDuration: const Duration(minutes: 5),
  refetchInterval: null // The query will not refetch by default,
  refetchOnMount: RefetchOnMount.stale,
  staleDuration: const Duration(seconds: 10),
);
  • enabled - specifies if the query fetcher function is automatically called when the widget renders, can be used for dependant queries
  • cacheDuration - specifies the duration unused/inactive cache data remains in memory, the cached data will be garbage collected after this duration. The longest one will be used when different values are specified in multiple instances of the query.
  • refetchInterval - specifies the time interval in which all queries will refetch the data, setting it to null (default) will turn off refetching
  • refetchOnMount - specifies the behavior of the query instance when the widget is first built and the data is already available.
    • RefetchOnMount.always - will always refetch when the widget is built.
    • RefetchOnMount.stale - will fetch the data if it is stale (see staleDuration)
    • RefetchOnMount.never - will never refetch
  • staleDuration - specifies the duration until the data becomes stale. This value applies to each query instance individually

Query invalidation

This technique can be used to manually mark the cached data as stale and potentially even refetch them. This is especially useful when you know that the data has been changed. QueryClient (see below) has an invalidateQueries() method that allows you to do that. You can make use of useQueryClient hook to obtain the instance of QueryClient that you passed with QueryClientProvider.

final queryClient = useQueryClient();

// Invalidate every query with a key that starts with `post`
queryClient.invalidateQueries(['posts']);

// Both queries will be invalidated
final posts = useQuery(['posts'], getPosts);
final post = useQuery(['posts', 1], getPosts);

// Use `exact: true` to exactly match the query
queryClient.invalidateQueries(['posts'], exact: true);

// Only this will invalidate
final posts = useQuery(['posts'], getPosts);

When a query is invalidated, two things will happen:

  • It marks it as stale and this overrides any staleDuration configuration passed to useQuery.
  • If the query is being used in a widget, it will be refetched, otherwise it will be refetched when it is used by a widget at a later point in time.

Manual updates

You probably already know how the data is changed and don't want to refetch the whole data again. You can set it manually using setQueryData() method on the QueryClient. It takes a query key and an updater function. If the query data doesn't exist already in the cache (that's why previous is nullable), it'll be created.

final queryClient = useQueryClient();

// The `Type` of returned data must match the `Type` of data
// stored in the cache, otherwise an error will be thrown
queryClient.setQueryData<List<Post>>(['posts'], (previous) {
  return previous?.map((post) {
    return post.copyWith(
      title: "lorem ipsum"
    );
  }).toList() ?? <Post>[]
})

QueryClient

A QueryClient is uesd to interact with the query cache. It is made available throughout the app using a QueryClientProvider. It can be configured to change the default configurations of the queries.

final queryClient = QueryClient(
  defaultQueryOptions: DefaultQueryOptions(
    cacheDuration: Duration(minutes: 20),
    refetchInterval: Duration(minutes: 5),
    refetchOnMount: RefetchOnMount.always,
    staleDuration: Duration(minutes: 3),
  ),
);

void main() {
  runApp(
    QueryClientProvider(
      queryClient: queryClient,
      child: CupertinoApp(

Bugs and suggestions

Feel free to open an issue or suggest an idea at the GitHub repo.

You might also like...

A fully functional Furniture App Clone made using Flutter, Supabase and Getx State Management.

A fully functional Furniture App Clone made using Flutter, Supabase and Getx State Management.

🛌 Flutter Furniture App 🪑 Timberr is a fully functional Furniture App Clone Developed using Flutter, Supabase and Getx State Management which is bas

Sep 11, 2022

Cubit is a lightweight state management solution

Cubit is a lightweight state management solution

Cubit is a lightweight state management solution. It is a subset of the bloc package that does not rely on events and instead uses methods to emit new states.

Sep 8, 2022

This repo is an example of clean architecture using the GetX state-management solution.

This repo is an example of clean architecture using the GetX state-management solution.

GetX Clean Architecture A Flutter Clean Architecture Using GetX. This repo is forked from: https://github.com/phamdinhduc795397/flutter-getx-clean-arc

Sep 24, 2022

Push Notification service for anime episodes and news. The episode updates will be based on actual upload on the internet and NOT Japan tv schedule as other apps do.

Push Notification service for anime episodes and news. The episode updates will be based on actual upload on the internet and NOT Japan tv schedule as other apps do.

Quantz Push Notification service for anime episodes and news. Features Sub and dub - get notified with latest anime episodes on the internet. Ongoing

Aug 3, 2022

a simple yet powerful state management technique for Flutter

a simple yet powerful state management technique for Flutter

States_rebuilder `states_rebuilder` is Flutter state management combined with a dependency injection solution and an integrated router to get the best

Sep 21, 2022

Simple yet powerful form state management

Simple yet powerful form state management

formini Working with forms shouldn't be so hard in Flutter. Please note that the schemani/formini packages are under development. There are still some

Dec 12, 2019

Now UI Flutter is a fully coded app template built for Flutter which will allow you to create powerful and beautiful e-commerce mobile applications

Now UI Flutter is a fully coded app template built for Flutter which will allow you to create powerful and beautiful e-commerce mobile applications

Now UI Flutter is a fully coded app template built for Flutter which will allow you to create powerful and beautiful e-commerce mobile applications. We have redesigned all the usual components to make it look like our Now UI Design, minimalistic and easy to use.

Jul 7, 2022

Nexus is a state management library that makes it easy to create and consume your application's reactive data to the user interface.

Nexus 🚀 Nexus is a state management library that makes it easy to create and consume your application's reactive data to the user interface. With nex

Sep 7, 2022

A powerful plugin that fully uses the native image library's ability to display images on the flutter side.

PowerImage A powerful plugin that fully uses the native image library's ability to display images on the flutter side. 中文文档 Features: Supports the abi

Sep 21, 2022
Releases(v1.0.0-beta.1)
Owner
Piyush
The technological breakthrough of incredible proportions.
Piyush
Dart package for Async Data Loading and Caching. Combine local (DB, cache) and network data simply and safely.

Stock is a dart package for loading data from both remote and local sources. It is inspired by the Store Kotlin library.

xmartlabs 20 Sep 27, 2022
State Persistence - Persist state across app launches. By default this library store state as a local JSON file called `data.json` in the applications data directory. Maintainer: @slightfoot

State Persistence Persist state across app launches. By default this library store state as a local JSON file called data.json in the applications dat

Flutter Community 70 Sep 28, 2022
A powerful state machine for MobX management, that can be used in almost any application state.

A powerful state machine for MobX management, which can be used in almost any application state. It has 3 states - loading, success, error - and is pe

Daniel Magri 6 Sep 6, 2022
Leverages libphonenumber to allow for asynchronous and synchronous formatting of phone numbers in Flutter apps

Leverages libphonenumber to allow for asynchronous and synchronous formatting of phone numbers in Flutter apps. Includes a TextInputFormatter to allow real-time AsYouType formatting.

Bottlepay 43 May 19, 2022
Shader manages the compilation of your GLSL shaders into SPIR-V byte code and Dart code

shader Shader manages the compilation of your GLSL shaders into SPIR-V byte code and Dart code. Quickstart # Install cli dart pub global activate shad

Felix Blaschke 23 Aug 17, 2022
QUICKNOTES is a simple Note taking app, you can easily manages your TODOs

QUICKNOTES is a simple Note taking app, you can easily manages your TODOs. It has a simple UI with Dark & Light Themes.

Abdul Basit 2 May 2, 2022
Practice building basic animations in apps along with managing app state by BLoC State Management, Flutter Slider.

Practice building basic animations in apps along with managing app state by BLoC State Management including: Cubit & Animation Widget, Flutter Slider.

TAD 1 Jun 8, 2022
Flutter Control is complex library to maintain App and State management. Library merges multiple functionality under one hood. This approach helps to tidily bound separated logic into complex solution.

Flutter Control is complex library to maintain App and State management. Library merges multiple functionality under one hood. This approach helps to

Roman Hornak 23 Feb 23, 2022
A simple state management solution that combine the power of inherited widget and rxdart

Inherited RxDart is a state management library that combine the power of InheritedWidget and RxDart, a simple and elegant state management solution for apps of any scale

Nguyễn Ngọc Phước 12 Sep 21, 2022
GetX - one of the most popular state management solution in flutter

GteX Tutorial - Udemy GetX is one of the most popular state management solution in flutter. In addition to state management, GetX provides easy way to

Md. Siam 3 May 18, 2022