query_kit_flutter 1.0.0 copy "query_kit_flutter: ^1.0.0" to clipboard
query_kit_flutter: ^1.0.0 copied to clipboard

Flutter binding for query_kit: a provider, controllers, builder widgets, a State mixin and context.query. An AI-coded community port, not affiliated with TanStack.

example/lib/main.dart

// query_kit is an entirely AI-coded project: all code, tests and
// documentation were written by AI coding agents (Anthropic's Claude). A
// human maintainer set the goals and reviews releases, but did not write the
// code.
//
// A one-file tour: a provider, a query read two ways, and a mutation that
// invalidates it. No server — the "API" is a delay and a list.
//
//   flutter run
import 'package:flutter/material.dart';
import 'package:query_kit_flutter/query_kit_flutter.dart';

/// Stands in for an HTTP client.
class Api {
  final List<String> _tasks = ['Kitchen', 'Hallway', 'Garage'];

  Future<List<String>> list() async {
    await Future<void>.delayed(const Duration(milliseconds: 600));
    return List.unmodifiable(_tasks);
  }

  Future<void> add(String name) async {
    await Future<void>.delayed(const Duration(milliseconds: 400));
    _tasks.add(name);
  }
}

final api = Api();
final tasksKey = QueryKey(<Object?>['tasks']);

Future<List<String>> fetchTasks(QueryFunctionContext context) => api.list();

QueryObserverOptions<List<String>> tasksQuery() => QueryObserverOptions(
      queryKey: tasksKey,
      queryFn: fetchTasks,
      staleTime: const StaleTime.duration(Duration(seconds: 30)),
    );

/// The same entry, reduced to one flag. A `select` shares the cache entry;
/// its reader still rebuilds whenever the *result* changes — `fetchStatus`
/// and `dataUpdatedAt` are part of it, so every refetch is a rebuild, which
/// is exactly what a spinner needs. `select` keeps `data` stable, and
/// `buildWhen` on a builder is what would skip the rebuild.
QuerySelectOptions<List<String>, bool> fetchingQuery() => QuerySelectOptions(
      queryKey: tasksKey,
      queryFn: fetchTasks,
      staleTime: const StaleTime.duration(Duration(seconds: 30)),
      select: (_) => true,
    );

void main() {
  runApp(
    QueryClientProvider(
      client: QueryClient(),
      child: const MaterialApp(home: TasksScreen()),
    ),
  );
}

class TasksScreen extends StatelessWidget {
  const TasksScreen({super.key});

  @override
  Widget build(BuildContext context) {
    // One of the four equal call styles: read in build. The widget rebuilds
    // when the result changes.
    final tasks = context.query(tasksQuery());
    // The client, taken here in `build` rather than inside the callback
    // below. A mutation outlives the widget that started it — disposing its
    // controller does not cancel it — so `onSuccess` can run after this
    // element is gone, and looking an ancestor up from a deactivated element
    // throws. The cache work has to happen either way; the client is the
    // right thing to close over, the `BuildContext` is not.
    final client = QueryClientProvider.of(context);
    // A mutation, the same way. `MutationOptions.simple` is the form without
    // an `onMutate` step: its types come from `api.add`.
    final add = context.mutation(
      MutationOptions.simple(
        mutationFn: api.add,
        onSuccess: (_, __, ___) => client.invalidateQueries(
          filters: QueryFilters(queryKey: tasksKey),
        ),
      ),
    );

    return Scaffold(
      appBar: AppBar(
        title: const Text('Tasks'),
        actions: <Widget>[
          // Another, equally valid: a builder, for a leaf that only wants one
          // flag.
          QuerySelectBuilder<List<String>, bool>(
            options: fetchingQuery(),
            builder: (context, result) => result.isFetching
                ? const Padding(
                    padding: EdgeInsets.all(16),
                    child: SizedBox.square(
                      dimension: 18,
                      child: CircularProgressIndicator(strokeWidth: 2),
                    ),
                  )
                : IconButton(
                    icon: const Icon(Icons.refresh),
                    onPressed: () => result.refetch(),
                  ),
          ),
        ],
      ),
      body: switch (tasks) {
        QueryPending() => const Center(child: CircularProgressIndicator()),
        QueryError(:final error, staleData: null) =>
          Center(child: Text('$error')),
        QuerySuccess(:final data) ||
        QueryError(staleData: final data!) =>
          ListView(
            children: <Widget>[
              for (final name in data) ListTile(title: Text(name)),
            ],
          ),
      },
      floatingActionButton: FloatingActionButton(
        onPressed: add.value.isPending
            ? null
            : () => add.mutate('Task ${DateTime.now().second}'),
        child: const Icon(Icons.add),
      ),
    );
  }
}
0
likes
160
points
16
downloads

Documentation

API reference

Publisher

verified publisherdualmeta.io

Weekly Downloads

Flutter binding for query_kit: a provider, controllers, builder widgets, a State mixin and context.query. An AI-coded community port, not affiliated with TanStack.

Repository (GitHub)
View/report issues
Contributing

Topics

#cache #state-management #data-fetching #tanstack-query #async

License

MIT (license)

Dependencies

flutter, meta, query_kit

More

Packages that depend on query_kit_flutter