Skip to content
The Fuery logo, three rounded tiles forming an FThe Fuery logo, three rounded tiles forming an F

Fuery

Fetch, cache, and keep server data fresh in Flutter.
// A QueryObserver<List<Todo>>, with no type arguments.
final todos = Query.use(
queryKey: ['todos'],
queryFn: (_) => api.getTodos(),
);
QueryBuilder(
query: todos,
builder: (context, state) => switch (state.data) {
final data? => TodoList(data), // a List<Todo>
null => const CircularProgressIndicator(),
},
)

Cached and fresh

Every widget that uses a key shares one cache entry and one request. Stale data stays on screen while it refetches in the background, and a refetch that returns the same items keeps them identical, so list rows don’t rebuild.

Drops into your app

Start with one screen. Creating a query needs no BuildContext, and there is nothing to replace.

Runs where your code runs

A pure Dart core, so widgets, cubits, services, CLIs, and servers use the same query object.

No type arguments

Types are inferred from your query and mutation functions, all the way to widgets, results, and callbacks. No code generation either.

Built for real networks

Retries with backoff, refetches when the app resumes, pauses while offline, and cancels requests nobody needs anymore without reporting them as errors. Polls until a condition holds, and folds a stream into the cache.

Persisted and inspectable

Keep queries across restarts with any key-value storage, and inspect the cache inside the running app, on a device.

Tested. Both packages have 100% line coverage, enforced in CI on the oldest supported Flutter and the latest. The core’s tests run under fake time, and a regression suite keeps fixed edge cases fixed. Every timer is cancelled on its destroy path and time comes from package:clock, so your own fake_async and widget tests don’t leak timers. See Testing.

Fuery’s caching and refetching model is inspired by TanStack Query.