Server state is not app state.
A selected tab or a half-typed form is yours: it stays where you left it. The orders on a server are not. Someone else can change them, your copy ages while the user reads it, three screens want it at once, and the network drops at the worst moment.
A FutureBuilder gets the first load on screen. What comes after is the real work: caching, sharing one request between readers, refreshing when the app comes back, retrying, cancelling, invalidating after a write, paging. query_kit does that work the way TanStack Query does it on the web, so a screen only says what it shows.
A write on screen before the server answers.
The showcase's optimistic-updates screen, running in this page against an in-memory backend. Add a todo and it is in the list at once. Then switch on Refuse next write, choose Via cache and add another: the row appears, the server refuses it, and the cache rolls back.
The work after the first load, done by the cache.
The cache
QueryKeyStaleTimeGcTime
- Caching and deduplication
- Five widgets reading one key make one request and share its answer.
- Stale-while-revalidate
- A second visit renders from the cache at once, and refetches behind it when the data is older than its
staleTime. - Refetch on focus and reconnect
- The app returning to the foreground, or the network returning, refreshes what is on screen.
- Infinite and paginated lists
- Pages fetched in either direction, kept under one key, refetched in order.
Writes
MutationOptionsonMutateinvalidateQueries
- Mutations and invalidation
- A write names the keys it made stale, and whatever shows them fetches again.
- Optimistic updates
- Show the write before the server answers, and roll it back when the server refuses.
The network
RetryPolicycontext.signalNetworkMode
- Retries and cancellation
- Failed fetches retry with backoff; a fetch whose function reads
context.signalis cancelled when its last reader leaves. - Offline-aware
- Told when the device is offline, queries wait for the network instead of failing, and writes made offline pause until it is back.
In your code
context.queryQueryBuilderQueryMixin
- Four equal ways to read
context.query,QueryBuilder,QueryMixinor aValueListenable. None of them is the default.- Sealed results
- A
switchoverQueryPending,QueryErrorandQuerySuccessis exhaustive, so there is nodata!. - A pure-Dart core
- The cache runs without Flutter: in a CLI, on a server, in a shared package.
- Nothing but Flutter
- No hooks, signals or connectivity package required. Connectivity is a
Stream<bool>you bring yourself.
Upstream's own tests say it behaves the same.
TanStack Query's test suite is ported alongside the code, under upstream's own test names, so the two files read side by side. Every case that is not ported is listed with its reason, and where the Dart port differs on purpose, the differences are written down as behaviour.
| Test name | Upstream | query_kit |
|---|---|---|
| should go through a pending state even when the queryFn returns synchronously | queryObserver.test.tsx | query_observer_test.dart |
| should continue retry after reconnect and resolve all promises | query.test.tsx | query_test.dart |
| should apply the maxPages option to limit the number of pages | infiniteQueryBehavior.test.tsx | infinite_query_behavior_test.dart |
Credits
Thank you to Tanner Linsley and to everyone who has built and maintained TanStack Query. Every good idea here is theirs, published under their MIT licence, whose notice each package carries in LICENSE-TANSTACK.
It is not theirs. Not affiliated with, endorsed by, reviewed by, or connected in any way to Tanner Linsley, the TanStack team, or the TanStack organisation. Please do not take problems with this package to them; they belong here.
Two packages on pub.dev.
query_kit_flutter
For a Flutter app: the provider, controllers, builder widgets, a State mixin and context.query, with the core re-exported.
flutter pub add query_kit_flutterFlutter 3.27 or later. MIT licence.
query_kit
The pure-Dart core, for a CLI, a server, or a package shared with one.
dart pub add query_kitDart 3.6 or later. MIT licence.