get_query

A TanStack Query-inspired async caching and pagination package for Flutter using GetX.

get_query helps manage asynchronous data fetching, caching, and pagination in a reactive and efficient way. It's ideal for Flutter apps using the GetX ecosystem and looking for clean, declarative async state management.

Features

  • πŸ” Auto caching and background refetching
  • πŸ“¦ Built-in pagination support
  • ⚑ Reactive state integration with GetX
  • 🧠 Centralized async state and error handling
  • πŸš€ Clean API inspired by TanStack Query (React Query)

Getting started

Add this package to your pubspec.yaml:

dependencies:
  get_query: ^1.0.0+11

Then import it:

import 'package:get_query/get_query.dart';

Usage

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await setupGetQuery(); // βœ… initialize everything
  runApp(const MyApp());
}
import 'package:get/get.dart';
import 'package:get_query/get_query.dart';
import 'package:tanstak_flutter_demo/models/post.dart';
import 'package:tanstak_flutter_demo/repositories/post_repositories.dart';

class PostsController extends GetxController {
  final posts = Rxn<UseQueryResult<List<Post>>>();


  @override
  void onInit() {
    super.onInit();
    posts.value = useQuery(
      queryKey: 'posts',
      queryFn: () async => await PostRepositories().getAllPosts(),
      // staleTime: Duration(minutes: 30),
      // retry: 1,
      // gcTime: Duration(seconds: 10),
    );
  }
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tanstak_flutter_demo/screens/posts/posts_controller.dart';

class PostScreen extends GetView<PostsController> {
  const PostScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Posts')),
      body: Obx(() {
        final result = controller.posts.value;

        if (result!.isLoading.value) {
          return const Center(child: CircularProgressIndicator());
        }

        if (result.error.value != null) {
          return Center(child: Text('Error: ${result.error}'));
        }

        final allPosts = result.data.value;

        return ListView.builder(
          itemCount: allPosts?.length ?? 0,
          itemBuilder: (context, index) {
            final post = allPosts![index];
            return ListTile(
              leading: CircleAvatar(backgroundImage: NetworkImage(post.images)),
              title: Text(post.username),
              subtitle: Text(post.comment),
            );
          },
        );
      }),
    );
  }
}

πŸ“„ Infinite Pagination Example

import 'package:get/get.dart';
import 'package:ta_query/models/posts.dart';
import 'package:ta_query/repo/paginated_response.dart';
import 'package:ta_query/repo/post_repo.dart';
import 'package:ta_query/ta_query/core/cache/infinite_query_options.dart';
import 'package:ta_query/ta_query/core/cache/query.dart';
import 'package:ta_query/ta_query/core/cache/query_observer.dart';
import 'package:ta_query/ta_query/core/client/query_client.dart';
import 'package:ta_query/ta_query/flutter_query/use_infinite_query.dart';
import 'package:ta_query/ta_query/flutter_query/use_query.dart';

class PostController extends GetxController {

  Rx<UseInfiniteQueryResult<PaginatedResponse<Post>, int>?> posts = Rx(null);
  late QueryClient queryClient;

  @override
  void onInit() {
    super.onInit();

    final p = useInfiniteQuery<PaginatedResponse<Post>, int, Post>(
      options: InfiniteQueryOptions<PaginatedResponse<Post>, int>(
        queryKey: 'posts',
        initialPageParam: 1,
        // staleTime: Duration(minutes: 1),
        queryFn: (ctx) => PostRepo().getPosts(page: ctx.pageParam),
        getNextPageParam: (lastPage, pages, lastParam, allParams) =>
            lastPage.pagination.nextPage,
        getPreviousPageParam: (firstPage, pages, firstParam, allParams) =>
            firstPage.pagination.prevPage,
      ),
    );
  }
}
class PostScreen extends GetView<PostController> {
  const PostScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Posts')),
      body: Obx(() {
        final result = controller.posts.value;

        if (result!.isLoading.value) {
          return const Center(child: CircularProgressIndicator());
        }

        if (result.isError.value) {
          return Center(child: Text('Error: ${result.error}'));
        }

        final allPosts = result.data.value?.pages
            .expand((page) => page.data)
            .toList();

        return NotificationListener<ScrollNotification>(
          onNotification: (scroll) {
            if (scroll.metrics.pixels >= scroll.metrics.maxScrollExtent - 200 &&
                result.hasNextPage.value && result.isFetching.value == false ) {
              result.fetchNextPage();
            }
            return false;
          },
          child: ListView.builder(
            itemCount: allPosts?.length ?? 0,
            itemBuilder: (context, index) {
              final post = allPosts![index];
              return ListTile(
                leading: CircleAvatar(
                  backgroundImage: NetworkImage(post.images),
                ),
                title: Text(post.username),
                subtitle: Text(post.comment),
              );
            },
          ),
        );
      }),
    );
  }
}

Additional information

This package is inspired by TanStack Query (React Query) and adapts its core principles to Flutter and GetX.

Contributing / Feedback

We welcome feedback, bug reports, and pull requests!

Libraries

get_query