Skip to content

Commit 115b9e5

Browse files
committed
Docs added for Query QueryJob & QueryBowlScope
Configured favicon
1 parent 88841e7 commit 115b9e5

24 files changed

+308
-339
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
title: Dynamic Mutations
3+
sidebar_position: 9
4+
---

docs/docs/basics/DynamicQueries.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
title: Dynamic Queries
3+
sidebar_position: 8
4+
---

docs/docs/basics/LazyQuery.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
title: Lazy Query
3+
sidebar_position: 7
4+
---

docs/docs/basics/MutationJob.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
title: Mutation Job
3+
sidebar_position: 5
4+
---

docs/docs/basics/Mutations.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
title: Mutations
3+
sidebar_position: 6
4+
---

docs/docs/basics/Queries.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
---
2+
title: Queries
3+
sidebar_position: 3
4+
---
5+
6+
### QueryBuilder
7+
8+
The defined logic in [QueryJob](/docs/basics/QueryJob) is bind to the Flutter UI using the `QueryBuilder` Widget. It's basically a `Builder` that takes a `QueryJob` through the `job` named parameter & creates/retrieves the appropriate `Query` and passes it down to the `builder` method
9+
10+
```dart
11+
class Example extends StatelessWidget {
12+
const Example({Key? key}) : super(key: key);
13+
14+
@override
15+
Widget build(BuildContext context) {
16+
return QueryBuilder<String, void>(
17+
job: job,
18+
externalData: null,
19+
builder: (context, query) {
20+
if (!query.hasData) {
21+
return const CircularProgressIndicator();
22+
}
23+
return Text(query.data!);
24+
},
25+
);
26+
}
27+
}
28+
```
29+
30+
> Here `job` is the same `QueryJob` defined at the first snippet in the [Query Job](/docs/basics/QueryJob) tutorial
31+
32+
The `externalData` parameter of the `QueryBuilder` is passed to the `task` function of the `QueryJob`. It was discussed previously in [Query Job#External Data](/docs/basics/QueryJob#external-data) section
33+
34+
### Query
35+
The passed query from the `builder` callback is the appropriate `Query` created based on the logic & configuration defined in the passed `QueryJob`
36+
37+
The `query` parameter aka `Query` contains all the useful getters, properties & methods for rendering data from the query. It contains the state of the current query, the data, the error, the loading status etc along with useful methods such as `refetch` and `setQueryData`
38+
39+
But more importantly, it contains the status of the current `Query`. It has to types of status one is Query Progression status & another is data availability status
40+
41+
You can access them as follows:
42+
- Progressive status of Query
43+
- `isSuccess`: When the task function returned data successfully
44+
- `isError`: When the task function returned an error
45+
- `isLoading`: When the task function is running
46+
- `isRefetching`: When new data is being fetched or simply the `refetch` method is executing
47+
- `isIdle`: When there's no data & `Query`'s task has not been yet run
48+
- Data availability status of Query
49+
- `hasData`: When query contains data (expired or not)
50+
- `hasError`: When the query contains error
51+
52+
53+
Now the most important part of query: Data and Error. You can access the data returned from the task using `query.data` or the error `query.error`. Both the data can be null. So always check if the data/error is null before accessing it
54+
55+
:::info
56+
Don't use only `query.isLoading` to check if the data is available or not as the query can be failed & at this time `data` which can cause UI Exceptions. So use `query.hasData` always to check if `data` is available yet or not or use both together
57+
:::
58+
59+
Another important part of this is `refetch`. Well, you can use it to manually trigger refetch or want the query to get newest data
60+
61+
Finally, you can use `setQueryData` to manually set the data of the query. This is useful when you want to refresh the query but the newest data is already available in the application. It can be used to reduce network traffic by saving network calls to the server. Or you can use it with `Mutations` to optimistically set data before the Mutation is executed & then update the query with actual data
62+
63+
:::tip
64+
You can learn more about Optimistic Updates in the [Mutation Tutorial](/docs/basics/mutations)
65+
:::
66+
67+
Here's an real-world example of `Query` & `QueryBuilder`
68+
69+
70+
```dart
71+
final anotherJob = QueryJob<String, Client>(
72+
queryKey: "another-unique-key",
73+
task: (queryKey, httpClient){
74+
return httpClient
75+
.get("https://jsonplaceholder.typicode.com/todos/1")
76+
.then((response) => response.body);;
77+
}
78+
);
79+
80+
class Example extends StatelessWidget {
81+
const Example({Key? key}) : super(key: key);
82+
83+
@override
84+
Widget build(BuildContext context) {
85+
// getting the instance of Client provided by the [provider] package
86+
final client = Provider.of<Client>(context);
87+
88+
return QueryBuilder<String, void>(
89+
job: job,
90+
// passing the client as externalData
91+
externalData: client,
92+
builder: (context, query) {
93+
// checking if data availability along with progressive status
94+
if (!query.hasData || query.isLoading) {
95+
return const CircularProgressIndicator();
96+
}
97+
// remember to always show a fallback widget/screen for errors too.
98+
// It keeps the user aware of status of the application their using
99+
// & saves their time
100+
else if(query.hasError && query.isError){
101+
return Text(
102+
"My disappointment is immeasurable & my day is ruined for this stupid error: $error",
103+
);
104+
}
105+
return Row(
106+
children: [
107+
Text(query.data["title"]),
108+
ElevatedButton(
109+
child: const Text("Refetch"),
110+
onPressed: () async {
111+
await query.refetch();
112+
},
113+
),
114+
],
115+
);
116+
},
117+
);
118+
}
119+
}
120+
```

docs/docs/basics/QueryBowlScope.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
title: QueryBowl Scope
3+
sidebar_position: 1
4+
---
5+
6+
The first thing needed for storing any form of data is a store. QueryBowlScope is basically a `StatefulWidget` which wraps around the actual store `QueryBowl`. It is similar to `ProviderScope` in riverpod & `MultiProvider` provider. But it can be used only once at the very top level of the application
7+
8+
You must use wrap your `MaterialApp` or `CupertinoApp` or `FluentApp` or `MacosApp` with `QueryBowlScope`
9+
10+
```dart
11+
class MyApp extends StatelessWidget {
12+
const MyApp({Key? key}) : super(key: key);
13+
14+
@override
15+
Widget build(BuildContext context) {
16+
return QueryBowlScope(
17+
child: MaterialApp(
18+
title: 'Fl-Query Example',
19+
home: const MyHomePage(),
20+
),
21+
);
22+
}
23+
}
24+
25+
```
26+
27+
`QueryBowlScope` has many properties that can be configured. You can configure refetch behaviors, thresholds, delays etc along with cache time
28+
29+
Here I'm increasing the staleTime to 10 seconds. This means that if the data is outdated after 10 seconds & will be refetched in the background smartly when needed. The default value is 1 seconds
30+
31+
```dart
32+
class MyApp extends StatelessWidget {
33+
const MyApp({Key? key}) : super(key: key);
34+
35+
@override
36+
Widget build(BuildContext context) {
37+
return QueryBowlScope(
38+
staleTime: Duration(seconds: 10),
39+
child: MaterialApp(
40+
title: 'Fl-Query Example',
41+
home: const MyHomePage(),
42+
),
43+
);
44+
}
45+
}
46+
```
47+
48+
For more information on how to use QueryBowlScope, please refer to the [QueryBowlScope](https://pub.dev/documentation/fl_query/latest/fl_query/QueryBowlScope-class.html) API Reference

docs/docs/basics/QueryJob.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
title: Query Job
3+
sidebar_position: 2
4+
---
5+
6+
Query Jobs are what you use to define the logic how or from where the data is fetched/queried. It is where the `task` function is defined. `QueryJob` is reusable throughout application
7+
8+
Here's a simple example
9+
10+
```dart
11+
final job = QueryJob<String, void>(
12+
queryKey: "a-unique-key",
13+
task: (queryKey, externalData){
14+
return Future.delayed(Duration(seconds: 1), () => "Hello World");
15+
}
16+
);
17+
```
18+
19+
The `queryKey` must be unique. It is used to identify the job
20+
21+
The `task` callback has to be asynchronous. When the `task` is run by `Query` the `queryKey` & the `externalData` passed from `QueryBuilder` is passed to it as parameters. The externalData can be anything. You can provide a Generic Type parameter for it too
22+
23+
:::info
24+
If `externalData` is of an `Iterable` type (`Map`, `List`, `Set` etc), it will be compared [shallowly](https://medium.com/nerdjacking/shallow-deep-comparison-9fd74ac0f3d2)
25+
:::
26+
27+
### External Data
28+
29+
A more real-world example of `QueryJob` with `externalData`
30+
31+
```dart
32+
import 'package:fl_query/fl_query.dart';
33+
import 'package:http/http.dart';
34+
35+
final anotherJob = QueryJob<String, Client>(
36+
queryKey: "another-unique-key",
37+
task: (queryKey, httpClient){
38+
return httpClient.get("https://jsonplaceholder.typicode.com/todos/1").then((response) => response.body);;
39+
}
40+
);
41+
```
42+
43+
Here `externalData` is a configured `Client` from the `http` package.
44+
45+
By default when `externalData` changes or updates the query is not refetched but if you want it to refetch when the `externalData` changes, you can set `refetchOnExternalDataChange` property of `QueryJob` to `true`. If you want this behavior globally to be enabled then you can set `refetchOnExternalDataChange` property of [QueryBowlScope](/docs/basics/QueryBowlScope) to `true`
46+
47+
48+
```dart
49+
import 'package:fl_query/fl_query.dart';
50+
import 'package:http/http.dart';
51+
52+
final anotherJob = QueryJob<String, Client>(
53+
queryKey: "another-unique-key",
54+
refetchOnExternalDataChange: true,
55+
task: (queryKey, httpClient){
56+
return httpClient.get("https://jsonplaceholder.typicode.com/todos/1").then((response) => response.body);;
57+
}
58+
);
59+
```
60+
61+
Now every time when the externalData changes the query will refetched.
62+
63+
### Retries
64+
65+
When a query returns an `Exception` or in other word, fails, the query is re-run multiple times in the background until it succeeds or the retry limit is reached. You can configure the retry behavior of query by modifying `retries` & `retryDelay` properties of `QueryJob`
66+
67+
- `retries`: is amount of times the query will be retried before setting the status as `QueryStatus.error`. If its zero, it will not retry.
68+
69+
- `retryDelay`: is the `Duration` between retries. That means after what amount of duration the retries will take place until it succeeds or the retry limit is reached.
70+
71+
By default `retries` is `3` and `retryDelay` is `Duration(milliseconds: 200)`
72+
73+
74+
```dart
75+
final job = QueryJob<String, Client>(
76+
queryKey: "exceptional-query",
77+
retries: 10,
78+
retryDelay: Duration(milliseconds: 200),
79+
task: (queryKey, _) async {
80+
throw Exception("I'm an evil Exception");
81+
}
82+
);
83+
```
84+
85+
Now the query will be retried 10 times with a delay of 200ms between each retry
86+
87+
There are more properties of `QueryJob` that you can configure. See the API reference of [QueryJob](https://pub.dev/documentation/fl_query/latest/fl_query/QueryJob-class.html)

docs/docs/basics/congratulations.md

Lines changed: 0 additions & 21 deletions
This file was deleted.

docs/docs/basics/create-a-blog-post.md

Lines changed: 0 additions & 34 deletions
This file was deleted.

0 commit comments

Comments
 (0)