sling_gql 0.1.1
sling_gql: ^0.1.1 copied to clipboard
GraphQL client for Flutter where the widget is the query — read a field, get the query. Proof of concept.
sling_gql #
A GraphQL client for Flutter where the widget is the query. Inspired by GQty. Proof of concept — APIs will change.
Docs: https://tpucci.github.io/sling_gql/ · Source: https://github.com/tpucci/sling_gql
Read a field, get the query. No operation documents, no fragments, no builder-per-query boilerplate: widgets read typed accessors during
build(), sling_gql records what was read, batches everything read in the frame into one GraphQL document, fetches it, fills a normalized cache, and rebuilds only the widgets that read the affected data.
QueryBuilder<Query>(
builder: (context, query, state) {
final launch = query.latestLaunch;
return ListTile(
title: Text(launch?.name ?? '…'),
subtitle: Text(launch?.rocket?.name ?? '…'),
);
},
)
…sends, once, at the end of the frame:
query {
latestLaunch { __typename name rocket { __typename name } }
}
Getting started #
-
Add the runtime and the generator:
flutter pub add sling_gql flutter pub add --dev sling_gql_gen -
Generate typed accessors from your schema's introspection JSON (the standard
{"__schema": …}result):dart run sling_gql_gen --schema graphql/schema.json \ --out lib/generated/schema.dart --scalar DateTime=DateTime -
Create a client and provide it to the tree:
import 'package:sling_gql/sling_gql.dart'; import 'generated/schema.dart'; void main() { final client = SlingClient<Query>( endpoint: Uri.parse('https://example.com/graphql'), rootFactory: Query.root, ); runApp(SlingScope<Query>( client: client, schema: slingSchema, child: const MyApp(), )); } -
Read fields in
build()withQueryBuilder; mutate withMutationBuilderorclient.mutate((m) => m.toggleFavorite(id: id)?.favorite).
What's in the box #
- Selection recording during
build(), including accessors handed to child widgets and lazily built sliver rows. - One HTTP request per frame, arguments turned into variables.
- Normalized cache (
Launch:launch-181), per-field rebuild notifications,evict,gc,snapshot/onChangefor persistence. - Skeleton state (
nullscalars, one-element lists) before data arrives;state.isSkeleton,state.isLoading,state.error,state.refetch(). prepare:to fetch fields hidden behind conditionals in the first round trip; dev-mode waterfall warnings.- Mutations with optimistic writes (journaled, rolled back on failure).
- Partial
errors[]handling, sticky errors, retry cooldown. - Cursor pagination helpers,
CacheScope.listfor list membership.
Not yet: subscriptions, unions/interfaces, SWR/expiry.
Rules of thumb #
- Read every field you need at the top of
build. A field read only inside anifon fetched data or in a callback costs a second round trip. Useprepare:for the rest. - All generated getters are nullable, even for
!schema types: a value can always be absent from cache. Tell a servernullfrom "not fetched yet" withstate.hasMissingData.
See the documentation site for the
full guide and the example/ app in the repository.