GithubHelp home page GithubHelp logo

2coo / fquery Goto Github PK

View Code? Open in Web Editor NEW

This project forked from 41y08h/fquery

0.0 0.0 0.0 1.79 MB

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

Home Page: https://pub.dev/packages/fquery

License: MIT License

Dart 94.15% HTML 5.85%

fquery's Introduction

Banner

⚡Are you ready to supercharge your Flutter app development?

Introducing fquery - an easy-to-use, yet efficient and reliable asynchronous state management solution for Flutter! It effortlessly caches, updates, and fully manages asynchronous data in your Flutter apps.

With this powerful tool at your disposal, managing server state (REST API, GraphQL, etc), local databases like SQLite, or anything async has never been easier. Just provide a Future and watch the magic unfold.

Trusted & Used by

UC San Diego

UC San Diego

The University of California, San Diego has shifted to fquery, moving away from traditional state management solutions like provider, bloc, etc, as the backbone of their mobile application, which has over 30,000 users and serves as the app used by the generations of students. With fquery's efficient and easy-to-use async state management, the developers are now enjoying the comfort of seamless state management by refactoring sphagetti block of codes, even files with 200 lines to just 20 lines. They also noticed a significant reduction in the hot reload.

All of this is only to have more time, and easy-to-manage structure to develop the features that matter the most. They are confident that the codebase will continue to be manageable, and provide the team with a better structure.

Stargazers and others

GitHub Repo stars

The project's growth has almost been completely organic, it has grown popular in the developer community and is growing by the day, consider starring it if you've found it useful.

As a developer, you too can leverage the power of this tool to create a high-quality mobile application that provides an exceptional user experience. fquery is a reliable and efficient solution that has already been proven successful in UC San Diego's app. So, why not choose it for your next project and take advantage of its powerful features to deliver a seamless experience to your users?

🌌 Features

  • Easy to use
  • Powerful and fully customizable
  • No boilerplate code required
  • Data fetching logic agnostic
  • Automatic caching and garbage collection
  • Automatic re-fetching of stale data
  • State data invalidation
  • Manual updates available
  • Dependent queries and parallel queries supported

❔Defining the problem

Have you ever wondered how to effectively manage server state in your Flutter apps? Many developers resort to using Riverpod, Bloc, `FutureBuilder``, or any other general-purpose state management solution. However, these solutions often lead to writing repetitive code that handles data fetching, caching, and other logic.

The truth is, general-purpose state management solutions are not the best choice when it comes to handling asynchronous server state. This is due to the unique nature of server state - it is asynchronous and requires specific APIs for fetching and updating. Additionally, the server state is stored in a remote location, which means it can be modified without your knowledge from anywhere in the world. This alone requires a lot of effort to keep the data synchronized and ensure that it is up-to-date.

How does ⚡fquery tackle this problem?

fquery is powered by flutter_hooks. It is very similar to swr and react-query. With fquery, you can make use of easy-to-use hooks to retrieve data from a Future and the rest of the process is automated. fquery is highly configurable, allowing you to customize it to meet your specific needs. You can configure every aspect of it to make it work optimally for your use case.

📄 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 package.

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

  • A Query key, 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 the 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 re-fetch by default,
  refetchOnMount: RefetchOnMount.stale,
  staleDuration: const Duration(seconds: 10),
  onData: (data) {
    // callback with the fetched data
  },
  onError: (error) {
    // callback with the encountered error
  }
);
  • enabled - specifies if the query fetcher function is automatically called when the widget renders and can be used for dependent queries.
  • cacheDuration - specifies the duration unused/inactive cache data remains in memory; the cached data will be garbage collected after this duration. The longest duration will be used when different values are specified in multiple instances of the query.
  • refetchInterval - specifies the time interval in which all queries will re-fetch the data, setting it to null (default) will turn off re-fetching.
  • refetchOnMount - specifies the behavior of the query instance when the widget is first built and the data is already available.
    • RefetchOnMount.always - will always re-fetch when the widget is built.
    • RefetchOnMount.stale - will fetch the data if it is stale (see staleDuration).
    • RefetchOnMount.never - will never re-fetch.
  • staleDuration - specifies the duration until the data becomes stale. This value applies to each query instance individually.
  • onData - callback that supplies data when query is successfully fetched/re-fetched.
  • onError - callback fired when query encounters an error while fetching.

Dependent Query

A dependent query is a query that depends on another variable for execution, or even any other query. Probably you want to run a query only after some other query, or data in a query that you don't have, e.g. a Future, or to fetch data only when a variable takes a certain value, e.g. a bool like isAuthenticated, for all of this or similar, dependent query can ease your load. To use this, simply pass the enabled option.

final user = useQuery(['users', email], getUserByEmail);

// This query will not execute until the above is successful and the username is available
final username = user.data?.username;
final posts = useQuery(['posts', ], getPosts, enabled: !username);


final isAuthenticated = session != null;
final keys = useQuery(['keys', session.id], enabled: isAuthenticated)

Query invalidation

This technique can be used to manually mark the cached data as stale and potentially even re-fetch 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 the 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']);

// here, 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);

// here, 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 re-fetched, otherwise, it will be re-fetched 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 re-fetch the whole data again. You can set it manually using the 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 used 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(

Contributing

If you've ever wanted to contribute to open source, and a great cause, now is your chance ✨, feel free to open an issue or submit a PR at the GitHub repo. See Contribution guide for more details.

Contributors ✨

Thanks go to these wonderful people:

Piyush
Piyush

🐛 💻 📖 🎨 🚧 👀
Cynthia
Cynthia

📖
Rajvir Singh
Rajvir Singh

🎨
Kollin Murphy
Kollin Murphy

📖 💻
Tuvshinbayar Tuvshinzul
Tuvshinbayar Tuvshinzul

🐛 💻 🤔

This project follows the all-contributors specification. Contributions of any kind are welcome!

fquery's People

Contributors

41y08h avatar 2coo avatar allcontributors[bot] avatar cynthiakonar avatar rajvirsingh1313 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.