diff --git a/docs/docs/basics/DynamicMutations.md b/docs/docs/basics/DynamicMutations.md new file mode 100644 index 0000000..91e06ca --- /dev/null +++ b/docs/docs/basics/DynamicMutations.md @@ -0,0 +1,4 @@ +--- +title: Dynamic Mutations +sidebar_position: 9 +--- \ No newline at end of file diff --git a/docs/docs/basics/DynamicQueries.md b/docs/docs/basics/DynamicQueries.md new file mode 100644 index 0000000..52eaf37 --- /dev/null +++ b/docs/docs/basics/DynamicQueries.md @@ -0,0 +1,4 @@ +--- +title: Dynamic Queries +sidebar_position: 8 +--- \ No newline at end of file diff --git a/docs/docs/basics/LazyQuery.md b/docs/docs/basics/LazyQuery.md new file mode 100644 index 0000000..0ab9f63 --- /dev/null +++ b/docs/docs/basics/LazyQuery.md @@ -0,0 +1,4 @@ +--- +title: Lazy Query +sidebar_position: 7 +--- \ No newline at end of file diff --git a/docs/docs/basics/MutationJob.md b/docs/docs/basics/MutationJob.md new file mode 100644 index 0000000..803d74b --- /dev/null +++ b/docs/docs/basics/MutationJob.md @@ -0,0 +1,4 @@ +--- +title: Mutation Job +sidebar_position: 5 +--- \ No newline at end of file diff --git a/docs/docs/basics/Mutations.md b/docs/docs/basics/Mutations.md new file mode 100644 index 0000000..27b9d51 --- /dev/null +++ b/docs/docs/basics/Mutations.md @@ -0,0 +1,4 @@ +--- +title: Mutations +sidebar_position: 6 +--- \ No newline at end of file diff --git a/docs/docs/basics/Queries.md b/docs/docs/basics/Queries.md new file mode 100644 index 0000000..dad0569 --- /dev/null +++ b/docs/docs/basics/Queries.md @@ -0,0 +1,120 @@ +--- +title: Queries +sidebar_position: 3 +--- + +### QueryBuilder + +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 + +```dart +class Example extends StatelessWidget { + const Example({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return QueryBuilder( + job: job, + externalData: null, + builder: (context, query) { + if (!query.hasData) { + return const CircularProgressIndicator(); + } + return Text(query.data!); + }, + ); + } +} +``` + +> Here `job` is the same `QueryJob` defined at the first snippet in the [Query Job](/docs/basics/QueryJob) tutorial + +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 + +### Query +The passed query from the `builder` callback is the appropriate `Query` created based on the logic & configuration defined in the passed `QueryJob` + +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` + +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 + +You can access them as follows: +- Progressive status of Query + - `isSuccess`: When the task function returned data successfully + - `isError`: When the task function returned an error + - `isLoading`: When the task function is running + - `isRefetching`: When new data is being fetched or simply the `refetch` method is executing + - `isIdle`: When there's no data & `Query`'s task has not been yet run +- Data availability status of Query + - `hasData`: When query contains data (expired or not) + - `hasError`: When the query contains error + + +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 + +:::info +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 +::: + +Another important part of this is `refetch`. Well, you can use it to manually trigger refetch or want the query to get newest data + +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 + +:::tip +You can learn more about Optimistic Updates in the [Mutation Tutorial](/docs/basics/mutations) +::: + +Here's an real-world example of `Query` & `QueryBuilder` + + +```dart +final anotherJob = QueryJob( + queryKey: "another-unique-key", + task: (queryKey, httpClient){ + return httpClient + .get("https://jsonplaceholder.typicode.com/todos/1") + .then((response) => response.body);; + } +); + +class Example extends StatelessWidget { + const Example({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + // getting the instance of Client provided by the [provider] package + final client = Provider.of(context); + + return QueryBuilder( + job: job, + // passing the client as externalData + externalData: client, + builder: (context, query) { + // checking if data availability along with progressive status + if (!query.hasData || query.isLoading) { + return const CircularProgressIndicator(); + } + // remember to always show a fallback widget/screen for errors too. + // It keeps the user aware of status of the application their using + // & saves their time + else if(query.hasError && query.isError){ + return Text( + "My disappointment is immeasurable & my day is ruined for this stupid error: $error", + ); + } + return Row( + children: [ + Text(query.data["title"]), + ElevatedButton( + child: const Text("Refetch"), + onPressed: () async { + await query.refetch(); + }, + ), + ], + ); + }, + ); + } +} +``` \ No newline at end of file diff --git a/docs/docs/basics/QueryBowlScope.md b/docs/docs/basics/QueryBowlScope.md new file mode 100644 index 0000000..00f48de --- /dev/null +++ b/docs/docs/basics/QueryBowlScope.md @@ -0,0 +1,48 @@ +--- +title: QueryBowl Scope +sidebar_position: 1 +--- + +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 + +You must use wrap your `MaterialApp` or `CupertinoApp` or `FluentApp` or `MacosApp` with `QueryBowlScope` + +```dart +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return QueryBowlScope( + child: MaterialApp( + title: 'Fl-Query Example', + home: const MyHomePage(), + ), + ); + } +} + +``` + +`QueryBowlScope` has many properties that can be configured. You can configure refetch behaviors, thresholds, delays etc along with cache time + +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 + +```dart +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return QueryBowlScope( + staleTime: Duration(seconds: 10), + child: MaterialApp( + title: 'Fl-Query Example', + home: const MyHomePage(), + ), + ); + } +} +``` + +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 \ No newline at end of file diff --git a/docs/docs/basics/QueryJob.md b/docs/docs/basics/QueryJob.md new file mode 100644 index 0000000..02da82e --- /dev/null +++ b/docs/docs/basics/QueryJob.md @@ -0,0 +1,87 @@ +--- +title: Query Job +sidebar_position: 2 +--- + +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 + +Here's a simple example + +```dart +final job = QueryJob( + queryKey: "a-unique-key", + task: (queryKey, externalData){ + return Future.delayed(Duration(seconds: 1), () => "Hello World"); + } +); +``` + +The `queryKey` must be unique. It is used to identify the job + +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 + +:::info +If `externalData` is of an `Iterable` type (`Map`, `List`, `Set` etc), it will be compared [shallowly](https://medium.com/nerdjacking/shallow-deep-comparison-9fd74ac0f3d2) +::: + +### External Data + +A more real-world example of `QueryJob` with `externalData` + +```dart +import 'package:fl_query/fl_query.dart'; +import 'package:http/http.dart'; + +final anotherJob = QueryJob( + queryKey: "another-unique-key", + task: (queryKey, httpClient){ + return httpClient.get("https://jsonplaceholder.typicode.com/todos/1").then((response) => response.body);; + } +); +``` + +Here `externalData` is a configured `Client` from the `http` package. + +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` + + +```dart +import 'package:fl_query/fl_query.dart'; +import 'package:http/http.dart'; + +final anotherJob = QueryJob( + queryKey: "another-unique-key", + refetchOnExternalDataChange: true, + task: (queryKey, httpClient){ + return httpClient.get("https://jsonplaceholder.typicode.com/todos/1").then((response) => response.body);; + } +); +``` + +Now every time when the externalData changes the query will refetched. + +### Retries + +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` + +- `retries`: is amount of times the query will be retried before setting the status as `QueryStatus.error`. If its zero, it will not retry. + +- `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. + +By default `retries` is `3` and `retryDelay` is `Duration(milliseconds: 200)` + + +```dart +final job = QueryJob( + queryKey: "exceptional-query", + retries: 10, + retryDelay: Duration(milliseconds: 200), + task: (queryKey, _) async { + throw Exception("I'm an evil Exception"); + } +); +``` + +Now the query will be retried 10 times with a delay of 200ms between each retry + +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) \ No newline at end of file diff --git a/docs/docs/basics/congratulations.md b/docs/docs/basics/congratulations.md deleted file mode 100644 index 9ef99bb..0000000 --- a/docs/docs/basics/congratulations.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -sidebar_position: 6 ---- - -# Congratulations! - -You have just learned the **basics of Docusaurus** and made some changes to the **initial template**. - -Docusaurus has **much more to offer**! - -Have **5 more minutes**? Take a look at **[versioning](../tutorial-extras/manage-docs-versions.md)** and **[i18n](../tutorial-extras/translate-your-site.md)**. - -Anything **unclear** or **buggy** in this tutorial? [Please report it!](https://github.com/facebook/docusaurus/discussions/4610) - -## What's next? - -- Read the [official documentation](https://docusaurus.io/). -- Add a custom [Design and Layout](https://docusaurus.io/docs/styling-layout) -- Add a [search bar](https://docusaurus.io/docs/search) -- Find inspirations in the [Docusaurus showcase](https://docusaurus.io/showcase) -- Get involved in the [Docusaurus Community](https://docusaurus.io/community/support) diff --git a/docs/docs/basics/create-a-blog-post.md b/docs/docs/basics/create-a-blog-post.md deleted file mode 100644 index 0d50aaf..0000000 --- a/docs/docs/basics/create-a-blog-post.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -sidebar_position: 3 ---- - -# Create a Blog Post - -Docusaurus creates a **page for each blog post**, but also a **blog index page**, a **tag system**, an **RSS** feed... - -## Create your first Post - -Create a file at `blog/2021-02-28-greetings.md`: - -```md title="blog/2021-02-28-greetings.md" ---- -slug: greetings -title: Greetings! -authors: - - name: Joel Marcey - title: Co-creator of Docusaurus 1 - url: https://github.com/JoelMarcey - image_url: https://github.com/JoelMarcey.png - - name: Sébastien Lorber - title: Docusaurus maintainer - url: https://sebastienlorber.com - image_url: https://github.com/slorber.png -tags: [greetings] ---- - -Congratulations, you have made your first post! - -Feel free to play around and edit this post as much you like. -``` - -A new blog post is now available at `http://localhost:3000/blog/greetings`. diff --git a/docs/docs/basics/create-a-document.md b/docs/docs/basics/create-a-document.md deleted file mode 100644 index a9bb9a4..0000000 --- a/docs/docs/basics/create-a-document.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -sidebar_position: 2 ---- - -# Create a Document - -Documents are **groups of pages** connected through: - -- a **sidebar** -- **previous/next navigation** -- **versioning** - -## Create your first Doc - -Create a markdown file at `docs/hello.md`: - -```md title="docs/hello.md" -# Hello - -This is my **first Docusaurus document**! -``` - -A new document is now available at `http://localhost:3000/docs/hello`. - -## Configure the Sidebar - -Docusaurus automatically **creates a sidebar** from the `docs` folder. - -Add metadata to customize the sidebar label and position: - -```md title="docs/hello.md" {1-4} ---- -sidebar_label: 'Hi!' -sidebar_position: 3 ---- - -# Hello - -This is my **first Docusaurus document**! -``` - -It is also possible to create your sidebar explicitly in `sidebars.js`: - -```js title="sidebars.js" -module.exports = { - tutorialSidebar: [ - { - type: 'category', - label: 'Tutorial', - // highlight-next-line - items: ['hello'], - }, - ], -}; -``` diff --git a/docs/docs/basics/create-a-page.md b/docs/docs/basics/create-a-page.md deleted file mode 100644 index e112b00..0000000 --- a/docs/docs/basics/create-a-page.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Create a Page - -Add **Markdown or React** files to `src/pages` to create a **standalone page**: - -- `src/pages/index.js` -> `localhost:3000/` -- `src/pages/foo.md` -> `localhost:3000/foo` -- `src/pages/foo/bar.js` -> `localhost:3000/foo/bar` - -## Create your first React Page - -Create a file at `src/pages/my-react-page.js`: - -```jsx title="src/pages/my-react-page.js" -import React from 'react'; -import Layout from '@theme/Layout'; - -export default function MyReactPage() { - return ( - -

My React page

-

This is a React page

-
- ); -} -``` - -A new page is now available at `http://localhost:3000/my-react-page`. - -## Create your first Markdown Page - -Create a file at `src/pages/my-markdown-page.md`: - -```mdx title="src/pages/my-markdown-page.md" -# My Markdown page - -This is a Markdown page -``` - -A new page is now available at `http://localhost:3000/my-markdown-page`. diff --git a/docs/docs/basics/deploy-your-site.md b/docs/docs/basics/deploy-your-site.md deleted file mode 100644 index 492eae0..0000000 --- a/docs/docs/basics/deploy-your-site.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -sidebar_position: 5 ---- - -# Deploy your site - -Docusaurus is a **static-site-generator** (also called **[Jamstack](https://jamstack.org/)**). - -It builds your site as simple **static HTML, JavaScript and CSS files**. - -## Build your site - -Build your site **for production**: - -```bash -npm run build -``` - -The static files are generated in the `build` folder. - -## Deploy your site - -Test your production build locally: - -```bash -npm run serve -``` - -The `build` folder is now served at `http://localhost:3000/`. - -You can now deploy the `build` folder **almost anywhere** easily, **for free** or very small cost (read the **[Deployment Guide](https://docusaurus.io/docs/deployment)**). diff --git a/docs/docs/basics/markdown-features.mdx b/docs/docs/basics/markdown-features.mdx deleted file mode 100644 index 6b3aaaa..0000000 --- a/docs/docs/basics/markdown-features.mdx +++ /dev/null @@ -1,146 +0,0 @@ ---- -sidebar_position: 4 ---- - -# Markdown Features - -Docusaurus supports **[Markdown](https://daringfireball.net/projects/markdown/syntax)** and a few **additional features**. - -## Front Matter - -Markdown documents have metadata at the top called [Front Matter](https://jekyllrb.com/docs/front-matter/): - -```text title="my-doc.md" -// highlight-start ---- -id: my-doc-id -title: My document title -description: My document description -slug: /my-custom-url ---- -// highlight-end - -## Markdown heading - -Markdown text with [links](./hello.md) -``` - -## Links - -Regular Markdown links are supported, using url paths or relative file paths. - -```md -Let's see how to [Create a page](/create-a-page). -``` - -```md -Let's see how to [Create a page](./create-a-page.md). -``` - -**Result:** Let's see how to [Create a page](./create-a-page.md). - -## Images - -Regular Markdown images are supported. - -You can use absolute paths to reference images in the static directory (`static/img/docusaurus.png`): - -```md -![Docusaurus logo](/img/docusaurus.png) -``` - -![Docusaurus logo](/img/docusaurus.png) - -You can reference images relative to the current file as well, as shown in [the extra guides](../tutorial-extras/manage-docs-versions.md). - -## Code Blocks - -Markdown code blocks are supported with Syntax highlighting. - - ```jsx title="src/components/HelloDocusaurus.js" - function HelloDocusaurus() { - return ( -

Hello, Docusaurus!

- ) - } - ``` - -```jsx title="src/components/HelloDocusaurus.js" -function HelloDocusaurus() { - return

Hello, Docusaurus!

; -} -``` - -## Admonitions - -Docusaurus has a special syntax to create admonitions and callouts: - - :::tip My tip - - Use this awesome feature option - - ::: - - :::danger Take care - - This action is dangerous - - ::: - -:::tip My tip - -Use this awesome feature option - -::: - -:::danger Take care - -This action is dangerous - -::: - -## MDX and React Components - -[MDX](https://mdxjs.com/) can make your documentation more **interactive** and allows using any **React components inside Markdown**: - -```jsx -export const Highlight = ({children, color}) => ( - { - alert(`You clicked the color ${color} with label ${children}`) - }}> - {children} - -); - -This is Docusaurus green ! - -This is Facebook blue ! -``` - -export const Highlight = ({children, color}) => ( - { - alert(`You clicked the color ${color} with label ${children}`); - }}> - {children} - -); - -This is Docusaurus green ! - -This is Facebook blue ! diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index 7b08a0a..7c373ae 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -4,6 +4,7 @@ import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import Layout from '@theme/Layout'; import HomepageFeatures from '@site/src/components/HomepageFeatures'; import { FiGithub } from "react-icons/fi" +import Head from '@docusaurus/Head'; function HomepageHeader() { const { siteConfig } = useDocusaurusContext(); @@ -35,13 +36,36 @@ function HomepageHeader() { export default function Home(): JSX.Element { const { siteConfig } = useDocusaurusContext(); return ( - - -
- -
-
+ <> + + + + + + + + + +
+ +
+
+ ); } diff --git a/docs/static/android-chrome-192x192.png b/docs/static/android-chrome-192x192.png new file mode 100644 index 0000000..8cca04d Binary files /dev/null and b/docs/static/android-chrome-192x192.png differ diff --git a/docs/static/android-chrome-512x512.png b/docs/static/android-chrome-512x512.png new file mode 100644 index 0000000..87a1a2a Binary files /dev/null and b/docs/static/android-chrome-512x512.png differ diff --git a/docs/static/apple-touch-icon.png b/docs/static/apple-touch-icon.png new file mode 100644 index 0000000..fa1f8ae Binary files /dev/null and b/docs/static/apple-touch-icon.png differ diff --git a/docs/static/favicon-16x16.png b/docs/static/favicon-16x16.png new file mode 100644 index 0000000..00cbc34 Binary files /dev/null and b/docs/static/favicon-16x16.png differ diff --git a/docs/static/favicon-32x32.png b/docs/static/favicon-32x32.png new file mode 100644 index 0000000..69315a9 Binary files /dev/null and b/docs/static/favicon-32x32.png differ diff --git a/docs/static/favicon.ico b/docs/static/favicon.ico new file mode 100644 index 0000000..a46377e Binary files /dev/null and b/docs/static/favicon.ico differ diff --git a/docs/static/img/favicon.ico b/docs/static/img/favicon.ico index 7ee0c88..a46377e 100644 Binary files a/docs/static/img/favicon.ico and b/docs/static/img/favicon.ico differ diff --git a/docs/static/site.webmanifest b/docs/static/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/docs/static/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/packages/example/lib/main.dart b/packages/example/lib/main.dart index 78ff20c..07a480a 100644 --- a/packages/example/lib/main.dart +++ b/packages/example/lib/main.dart @@ -14,7 +14,6 @@ import 'package:example/components/query_external_data.dart'; import 'package:example/components/query_variable_key.dart'; import 'package:fl_query/fl_query.dart'; import 'package:flutter/material.dart'; -import 'dart:async'; void main() { runApp(const MyApp());