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 dataMutationJob
: 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 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)