arc_query 0.1.1
arc_query: ^0.1.1 copied to clipboard
A lightweight async data toolkit for Flutter with loading, error, cache, refresh, retry, and pagination.
arc_query #
The easiest way to handle async data in Flutter
arc_query is a lightweight React Query-style toolkit for Flutter. It gives you a simple, strongly typed API for loading, error, success, retry, refresh, caching, and pagination without forcing Bloc, Riverpod, Provider, or any specific app architecture.
Why This Exists #
Async UI code in Flutter usually turns into repeated isLoading, try/catch, and manual refresh logic scattered across widgets and services.
arc_query keeps that flow in one place:
- one query object
- one execute call
- one source of truth for loading, error, and data
- built-in cache and stale handling
- built-in pagination support
Quick Start #
import 'package:arc_query/arc_query.dart';
final userQuery = Query<User>(
key: 'user',
fetcher: () => api.getUser(),
);
await userQuery.execute();
if (userQuery.isLoading) {
// show spinner
}
if (userQuery.hasError) {
// show error
}
final user = userQuery.data;
Core Concepts #
Every query exposes:
isLoadingisRefreshingisFetchingMorehasErrorhasDatadataerrorupdatedAtisStale
Under the hood, queries move through immutable states:
QueryIdleQueryLoadingQuerySuccessQueryErrorQueryRefreshingQueryFetchingMore
Basic Example #
final profileQuery = Query<Profile>(
key: 'profile',
staleDuration: const Duration(minutes: 5),
fetcher: () => api.fetchProfile(),
);
await profileQuery.execute();
await profileQuery.refresh();
await profileQuery.retry();
QueryBuilder #
Use QueryBuilder when you want a minimal widget wrapper around a query:
QueryBuilder<User>(
query: userQuery,
loadingBuilder: (_) => const CircularProgressIndicator(),
errorBuilder: (context, error, _) => Text(error.toString()),
dataBuilder: (context, user) => Text(user.name),
emptyBuilder: (_) => const Text('No user'),
)
You can also use the generic builder:
QueryBuilder<User>(
query: userQuery,
builder: (context, state) {
if (state is QueryLoading<User>) {
return const CircularProgressIndicator();
}
if (state is QueryError<User>) {
return Text(state.error.toString());
}
return Text(state.data?.name ?? 'Missing user');
},
)
Caching #
Queries cache successful results in memory by key.
final productsQuery = Query<List<Product>>(
key: 'products',
staleDuration: const Duration(minutes: 5),
fetcher: () => api.fetchProducts(),
);
Behavior:
- if cached data is fresh,
execute()returns cached data immediately - if cached data is stale, the query keeps the previous value available and refetches
QueryControllercan invalidate one query or clear all cached entries
final controller = QueryController();
final productsQuery = Query<List<Product>>(
key: 'products',
controller: controller,
fetcher: () => api.fetchProducts(),
);
controller.invalidate('products');
await controller.refetch('products');
controller.clearCache();
Pagination #
Use PaginatedQuery for list endpoints:
final postsQuery = PaginatedQuery<Post, int>(
key: 'posts',
initialPageParam: 1,
pageFetcher: (page) => api.fetchPosts(page),
);
await postsQuery.execute();
await postsQuery.fetchMore();
Your fetcher returns a PageResult:
Future<PageResult<List<Post>, int>> fetchPosts(int page) async {
final response = await api.fetchPosts(page: page);
return PageResult<List<Post>, int>(
data: response.items,
nextPageParam: response.hasMore ? page + 1 : null,
hasMore: response.hasMore,
);
}
Pagination gives you:
- automatic list append
hasMoreisFetchingMore- cached paginated results
Refresh And Retry #
Refresh keeps previous data available while a request is running:
await query.refresh();
Retry re-runs the latest failed request:
await query.retry();
Comparison vs Manual Async Handling #
Manual UI state usually means:
- local booleans for loading and errors
- duplicate
try/catchblocks - repeated pull-to-refresh wiring
- ad hoc cache maps
- custom pagination state per screen
With arc_query, one object owns that behavior and the widget reads from a single typed source.
Example App #
The package includes a full Flutter example in example/lib/main.dart showing:
- user fetch
- retry after failure
- pull to refresh
- paginated list loading
- loading more pages
Testing #
Run:
flutter test
License #
MIT