"Fl-Query Logo"

Fl-Query

Asynchronous data caching, refetching & invalidation library for Flutter. FL-Query lets you manage & distribute your async data without touching any global state

Fl-Query makes asynchronous server state management a breeze in flutter

Features

  • Async data caching & management
  • Smart + effective refetching
  • Optimistic updates
  • Automatically cached data invalidation & unneeded query/mutation garbage collection
  • Infinite data pagination via InfiniteQuery
  • Easy to write & understand code. Follows DRY (Don't repeat yourself) convention
  • Compatible with both vanilla Flutter & elite flutter_hooks

Installation

Regular installation:

$ flutter pub add fl_query

For elite flutter_hooks user:

$ flutter pub add flutter_hooks
$ flutter pub add fl_query_hooks

Docs

You can find the documentation (WIP) of fl-query at https://fl-query.vercel.app/

Basic Usage

First wrap your MaterialApp with with QueryBowlScope widget

  Widget build(BuildContext context) {
    return QueryBowlScope(
      bowl: QueryBowl(),
      child: MaterialApp(
        title: 'Fl-Query Example App',
        theme: ThemeData(
          useMaterial3: true,
          primarySwatch: Colors.blue,
        ),
        home: const MyHomePage(),
      ),
    );
  }

Fl-Query has two types of jobs

  • QueryJob: Used for storing GET requests or for storing changeable yet readonly async data
  • MutationJob: Used for POST/PUT/DELETE requests or for mutating/changing data in services or a store asynchronously

You can write all your query or mutation logic a method parameter named task & identify the query uniquely by passing a unique queryKey

Example of a QueryJob:

final exampleQueryJob = QueryJob<Map, void>(
  queryKey: "example", // have to be unique
  task: (queryKey, externalData) async {
    final res = await http.get("/api/example-data");
    return jsonDecode(res.body);
  }
);

Store the QueryJob somewhere globally accessible in your project so you can reuse it later

Now you can use this QueryJob anywhere inside your flutter app inside the build method using a QueryBuilder widget

Widget build(BuildContext context){
  return QueryBuilder<String, void>(
      job: exampleQueryJob,
      externalData: null,
      builder: (context, query) {
        if (!query.hasData || query.isLoading) {
          return const CircularProgressIndicator();
        }
        return Row(
          children: [
            Text(query.data!),
            ElevatedButton(
              child: const Text("Refetch"),
              onPressed: () async {
                // refetches the query
                await query.refetch();
              },
            ),
          ],
        );
      },
    );
}

Or if you're an elite flutter_hooks user you can use the useQuery hook which is exported from the package:fl_query_hooks/fl_query_hooks.dart to do the same thing as above

/* other imports */
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fl_query_hooks/fl_query_hooks.dart'; // importing the fl-query hook package

class Example extends HookWidget{
  Example(super.key);

  Widget build(BuildContext context) {
    final query = useQuery(job: exampleQueryJob, externalData: null);

    if (!query.hasData || query.isLoading) {
      return const CircularProgressIndicator();
    }
    return Row(
      children: [
        Text(query.data!),
        ElevatedButton(
          child: const Text("Refetch"),
          onPressed: () async {
            // refetches the query
            await query.refetch();
          },
        ),
      ],
    );
  }
}

Why?

The hell, why?

The main purpose of Fl-Query is providing the easiest way to manage the messy server-state part requiring the least amount of code with code reusability & performance

Isn't FutureBuilder good? Yes but it is only if your commercial server has huge load of power & you're made of money or your app is simple or mostly offline & barely requires internet connection FutureBuilder isn't good for data persistency & its impossible to share data across the entire application using it

So FutureProvider from riverpod or provider not enough? Yeah, indeed its more than enough for many applications but what if your app needs Optimistic Updates & proper server-state synchronization or simply want a custom cacheTime? Although FutureProvider is a viable solution most of the Future related stuff, why not kick it up a notch with smart refetching capabilities with proper server-state synchronization? Riverpod is definitely a inspiration for Fl-Query & the QueryJob is actually inspired by riverpod & imo is the best state management solution any library has ever provided but that's still a client state manager just like other client state manager or synchronous data manager

Notice Board

This project is currently under heavy development & not yet production ready. There are lot of features to cover. If anyone encounters any unintended behavior or any bug please report it. Also we're open to improvement suggestions & feature requests

Important!: The project needs Dart-Flutter developers who are willing to contribute to the project by writing Tests. (I'm the worst example for tester)

Libraries

fl_query