mk_graphql 1.2.3 copy "mk_graphql: ^1.2.3" to clipboard
mk_graphql: ^1.2.3 copied to clipboard

A clean, modern, simplified GraphQL client for Dart & Flutter with normalized caching, composable links, and stream-based operation execution.

1.2.3 #

  • Fix: token refresh could fail with "refresh token already revoked" because the same session was refreshed more than once with servers that rotate refresh tokens. Fixed all the ways that happened:
    • A 401 that arrives after a refresh has already completed (the request was sent with the pre-refresh access token) now retries with the refreshed token instead of starting another refresh and rotating the refresh token again.
    • Refreshes are coordinated across every GraphQLClient that uses a refreshCallback (they all share TokenStorage.i), so two clients hitting a 401 at once, or one client's late 401 after the other refreshed, no longer each refresh with the same refresh token. AuthLink accepts an optional RefreshCoordinator to share refresh state between links that refresh the same session.
    • A refresh that finishes after the session was revoked (e.g. after refreshTimeout fired) no longer writes its tokens back into storage and resurrects the session.
    • Persisting a refreshed TokenPair is retried up to 3 times; if storage still rejects it, the new pair is kept in memory and used for requests and the next refresh (the stored refresh token is already revoked server-side), and is flushed to storage on the next refresh. It is dropped on revoke or if the stored token changes (new login/logout).

1.2.2 #

  • Breaking: the built-in TokenStorage and TokenPair are removed in favour of the token_storage package, which mk_graphql now depends on and re-exports (import 'package:mk_graphql/mk_graphql.dart' still gives you both). mk_graphql no longer depends on flutter_secure_storage, shared_preferences or equatable directly.
    • Call await TokenStorage.init() once at startup (replaces clearOnInstall(), which also handled the fresh-install wipe).
    • TokenPair is no longer const (its createdAt/updatedAt default to DateTime.now() when omitted). On a refresh the client still keeps the stored createdAt and stamps updatedAt itself.
    • API renames: getToken() → token, deleteToken() → clearToken(). setTokenListener now takes a void Function(TokenPair?) (was the access token string) and holds a single listener (a second call replaces the first instead of adding one).
    • Storage layout changed (a single namespaced JSON entry instead of four keys), so existing sessions are not migrated: users are logged out once after upgrading.
    • New from the package: TokenPair.isExpired, TokenStorage.hasToken/isTokenExpired, and generic secure key-value storage (put/get/putObject/getObject/remove/watchKey).
    • Minimum SDK raised to Dart 3.9 / Flutter 3.35 to match token_storage.

1.2.1 #

  • New: mutations and subscriptions now force FetchPolicy.noCache unconditionally — enforced centrally in GraphQLClient.request(), regardless of the client's defaultFetchPolicy or a per-request fetchPolicy override (a hand-built request, or a future generator default, can no longer make a mutation/subscription read or write the normalized cache under its own operation key). GraphQLRequest.optimisticResponse is unaffected — it still writes to the optimistic layer other queries read from, which is how a mutation drives an optimistic UI update elsewhere in the cache.
  • New: GraphQLClient(refreshTimeout: ...) bounds how long a single token-refresh call may take before it's treated as a failed refresh (same outcome as it throwing — the session is revoked). null (the default) waits indefinitely, unchanged from before.
  • New: GraphQLClient(maxConcurrentBackgroundRefetches: ...) caps how many of notifyReconnected/notifyAppResumed/invalidateQueries's background refetches run at once. null (the default) runs them all concurrently, unchanged from before.
  • New: GraphQLPaginator.loadAll({interPageDelay}) — waits interPageDelay between pages (after the first) instead of requesting every page back-to-back as fast as each resolves. null (the default) is unchanged.
  • Fix: WebSocketLink now resends subscribe for every subscription that survives a socket drop, once the reconnect succeeds. Previously the socket reconnected but no subscription message was ever resent, so every subscriber silently stopped receiving data after any network blip, with no error and no indication anything was wrong.
  • Perf: InMemoryCache.readQuery memoizes its denormalized+parsed result per query key (invalidated by a version counter bumped on any write/evict/clear/optimistic change), so repeated reads of an unchanged key skip the full denormalize+parse walk — measured ~10x faster for the common case of several watchQuery subscribers attaching to the same key back-to-back. writeQuery now collects touched __typenames in the same pass that normalizes the response instead of denormalizing it again afterwards purely to re-walk it — measured ~23% faster on a 500-item response. HiveCache now diffs against its last-persisted snapshot and writes only changed/removed records instead of rewriting the entire box on every batch — measured ~28% faster per persist cycle. See benchmark/cache_benchmark_test.dart (flutter test benchmark/cache_benchmark_test.dart) for the numbers on your own machine.
  • Perf: minor allocation/computation cuts: computeRequestKey is now memoized per GraphQLRequest instance (was recomputed on every readQuery/writtenAt/writeQuery/dedup lookup); InMemoryCache._getRecord skips an allocation entirely when no optimistic response is in flight; DioLink no longer walks the whole variables tree building path strings just to conclude a request has no file upload.

1.2.1-dev.6 #

  • New: llms.txt and llms-full.txt (per the llmstxt.org convention), published alongside the package so an AI coding agent integrating mk_graphql into a consuming app has a self-contained reference — llms-full.txt inlines setup, auth wiring, caching, pagination, and common-mistakes guidance without requiring the rest of the monorepo.

1.2.1-dev.5 #

  • Fix: GraphQLClient(defaultHeaders: ...) can now override the transport's built-in Accept and Content-Type headers — previously they were silently ignored, because the headers only reached the request as the Dio's base options, which the link's own per-request headers beat. defaultHeaders are now passed to DioLink (and to AuthLink's internal refresh client) directly. A default Content-Type still never breaks a multipart upload: DioLink now strips it case-insensitively before Dio sets the boundary header.
  • Fix: GraphQLClient no longer modifies a caller-supplied Dio. It used to copy defaultHeaders into dio.options.headers, which overwrote the Dio's own same-named headers and leaked the GraphQL defaults into the caller's other (e.g. REST) requests. Behaviour change for anyone who relied on that side effect — set those headers on your own Dio instead.
  • New: AuthLink accepts defaultHeaders (applied to its internal refresh client's requests).

1.2.1-dev.4 #

  • New: silent token refresh. TokenPair gained optional createdAt (when the session's tokens were first issued — preserved across refreshes) and updatedAt (last write). TokenStorage persists both (new __token_created_at__/__token_updated_at__ keys; sessions stored before this change load with null timestamps and get createdAt backfilled on their next refresh) and gained updateToken(pair), which writes a refreshed pair without notifying token listeners. GraphQLClient now uses it when refreshCallback succeeds, so TokenStorage.i.setTokenListener(...) — typically what drives app routing — is no longer triggered by a refresh; the next request just reads the new tokens from storage. saveToken (login) and deleteToken (logout/revoke) still notify, the latter with null.
  • Breaking: TokenStorage.setTokenListener no longer fires after a token refresh. If you relied on it to observe refreshed tokens (e.g. to reconnect a websocket), read TokenStorage.i.getToken() instead — updatedAt tells you when they last changed.

1.2.1-dev.3 #

  • New: notifyAppPaused(). Pairs with the existing notifyAppResumed() — call it from your app-lifecycle observer when the app backgrounds. While paused, every opportunistic background refetch (staleness-on-attach, refetchInterval polling, invalidateQueries, notifyReconnected) is skipped instead of hitting the network for a screen nobody's watching; refetchInterval timers keep ticking on schedule (free), only the network call each tick would have made is skipped. notifyAppResumed() clears the pause and catches up everything currently watched in one shot. A request made directly (future/stream/refetch) is never affected. See Caching: Pausing background refetches.
  • New: RetryOptions.jitter (0.0–1.0, default 0.0) randomizes each computed retry delay by up to that fraction in either direction, so many client instances that failed at the same moment don't all retry in lockstep and re-cause the spike that failed them.
  • New: WebSocketLink reconnect delay now backs off exponentially (reconnectBackoffMultiplier, default 1.5) from reconnectInterval, capped at maxReconnectInterval (default 30s), with reconnectJitter (default 0.2) — previously it reconnected at a fixed reconnectInterval (default 3s) forever, which could hammer a dead server/absent network indefinitely. The attempt counter resets on every successful connection_ack.
  • New: exported cappedBackoffDelay({attempt, initialDelay, maxDelay, multiplier}), the exponential-backoff-with-cap helper WebSocketLink uses internally, for anyone scheduling their own bounded retry/reconnect logic.
  • Breaking: FetchPolicy.cacheFirst now background-revalidates on every cache hit by default. GraphQLClient.defaultStaleTime (how long a cached entry is considered fresh) changed from "always fresh once cached" to Duration.zero, matching TanStack Query's own default. A cacheFirst cache hit still returns the cached value instantly with no UI-visible delay, but now also immediately kicks off a background network refetch, the same emission shape cacheAndNetwork has always had. Apps that relied on cacheFirst never re-hitting the network for a given query need to set an explicit staleTime (per-request GraphQLRequest.staleTime, or client-wide defaultStaleTime) to keep the old behavior — see Caching: Stale-while-revalidate.
  • New: Request deduplication. Concurrent identical queries/subscriptions (same operation, document, and variables) now share a single in-flight network call automatically. Mutations and any request carrying an explicit CancelToken never dedup. No configuration needed.
  • New: Stale-while-revalidate caching + GC. Added GraphQLRequest.staleTime/cacheTime and GraphQLClient.defaultStaleTime/defaultCacheTime. cacheTime (unset by default — never GC) evicts a query's cache entry cacheTime after its last active watchQuery subscriber cancels, unless a new subscriber attaches first.
  • New: client.invalidateQueries({operationNames, types, refetchActive}) evicts cached queries matching an operation name and/or a __typename touched by their cached result, background-refetching any currently active watchQuery matches by default.
  • New: Retry with backoff. Added RetryOptions (maxAttempts, initialDelay, backoffMultiplier, retryIf, and RetryOptions.none), GraphQLClient.defaultRetry, and GraphQLRequest.retry. Queries/subscriptions retry automatically when defaultRetry is set; mutations never auto-retry unless they set their own retry.
  • New: Refetch triggers. Added client.notifyReconnected()/client.notifyAppResumed() (background-refetch every currently active watchQuery) and GraphQLRequest.refetchInterval (periodic background refetch while a request has an active watcher).
  • New: HiveCache, a hive_ce-backed Cache implementation that persists the normalized cache to disk across app restarts (write-through over an in-memory delegate; optimistic writes are never persisted). Requires the app to call Hive.initFlutter()/Hive.init(path) before HiveCache.open(boxName: ...). See Caching: Persistent cache HiveCache.
  • New: GraphQLClient.create({...}), an async counterpart to the existing synchronous factory whose cache parameter accepts a FutureOr<Cache>?, so cache: HiveCache.open(...) can be passed directly without a separate await.
  • Fix: InMemoryCache.writeQuery no longer chokes on a query result whose nested values are un-serialized model objects (e.g. a generated *Model holding another generated model). Nested non-JSON values are now coerced through toJson() and recursed into before normalization, so entities nested inside a wrapper model are normalized and their __typenames are collected for invalidateQueries(types: ...) — previously such a result could be stored un-normalized and be missed by type-based invalidation.
  • Dependency: equatable constraint raised from ^2.1.0 to ^3.0.0 (only TokenPair uses it, and it needed no code changes). Apps that pin equatable 2.x elsewhere must move to 3.x to use this release.

1.2.1-dev.2 #

  • Breaking: Reworked token storage. TokenStorage is now a concrete singleton (TokenStorage.i, backed by flutter_secure_storage + shared_preferences) instead of an injectable abstract interface — SecureTokenStorage and the tokenStorage: constructor parameter are removed; GraphQLClient always talks to TokenStorage.i. AuthToken is replaced by TokenPair (now Equatable). Removed GraphQLClient.setAuthToken(), .clearAuthToken(), and .onAuthTokenChange — there's no longer a change-notification stream; drive app-wide auth state off AuthLink.registerRevokeTokenListener(...) and TokenStorage.i.setTokenListener(...) instead. .isAuthenticated remains and now reads TokenStorage.i.accessToken.
  • Breaking: Renamed the tokenHeader parameter on GraphQLClient/AuthLink to accessTokenHeader. Added a new optional refreshTokenHeader callback — when provided, its headers are attached to every request sent through the internal refresh client (the one passed into onTokenRefresh/refreshCallback) instead of accessTokenHeader, for refresh endpoints that expect the refresh token itself as a header.
  • Breaking: Renamed AccessTokenCallback to TokenHeaderCallback and changed its signature from Function() to Function(String token) — accessTokenHeader/refreshTokenHeader/SseLink.tokenHeader now receive the current token (read from TokenStorage.i by the caller) as an argument instead of having to read TokenStorage.i themselves, and are only invoked when a token is actually stored (so token is never null in practice, and the callback's return type is now non-nullable Map<String, String> too). refreshTokenHeader still has no default — the refresh token normally reaches your refresh endpoint via the refreshCallback parameter instead (for a body/variable); pass refreshTokenHeader only if it should also/instead go out as a header.
  • Breaking: HttpLink and BatchLink now run on dio.Dio instead of package:http — their client: constructor parameter is now dio.Dio? instead of http.Client?, and the http package dependency is dropped entirely. HttpLink's multipart upload detection now matches DioLink's: pass Upload/dio.MultipartFile values (not http.MultipartFile, which the public Upload typedef never pointed to) as GraphQL variables. HttpLinkConnectionException/HttpLinkCanceledException are unchanged and still thrown for the equivalent failures. HttpLink keeps its manual best-effort cancellation race (rather than delegating to Dio's own cancelToken:) since that still depends on adapter cooperation it doesn't want to require.
  • Added isUnauthorized to GraphQLClient/AuthLink — an optional AuthFailure Function(UnauthorizedContext context) that classifies a possibly-unauthorized response/exception, so a custom API's own convention (a non-standard error code, message, or status field) can drive the client's behavior instead of the built-in default. UnauthorizedContext normalizes errors/statusCode/errorCode the same way whether the failure came from a streamed Response or a thrown ServerException, so detection logic doesn't need to juggle two nullable inputs. AuthFailure has three outcomes: none (pass through), refresh (attempt a refresh and retry once), and revoke (skip the refresh attempt entirely and revoke immediately — for failures a refresh wouldn't fix, like a valid token that simply lacks permission). The built-in default maps extensions.code/HTTP status of UNAUTHORIZED/UNAUTHENTICATED/401 to refresh and FORBIDDEN/403 to revoke (previously grouped with 401 and always attempted a refresh first).
  • Internal: Consolidated GraphQL error parsing/mapping into graphql_error_mapper.dart (extractErrorCode, mapGraphQLError(s)) — previously the extensions['code'] extraction and the gql_exec.GraphQLError → this package's GraphQLError mapping were each hand-rolled in three-plus places (GraphQLExceptionExtension, GraphQLClient._executeNetworkRequest, UnauthorizedContext.errorCode).
  • Fix: A revoked/failed-refresh request no longer surfaces to future()/stream() as a generic AppServerException('No response received from GraphQL server.'). AuthLink now always surfaces the real cause: the original FORBIDDEN/401 response or exception when there's nothing more specific, or a RevokeTokenException describing why the refresh attempt itself failed (wrapping whatever onTokenRefresh/refreshCallback threw) when that's more specific.
  • Fix: HttpLink and BatchLink now parse responses with the same ResponseParser DioLink/SseLink/WebSocketLink already use, instead of a hand-rolled parse that kept only errors[].message and silently dropped locations/path/extensions — meaning extensions.code (which AuthLink's unauthorized detection and GraphQLExceptionExtension both key off) was lost for any error routed through either link.
  • Split auth_link.dart into auth_types.dart (typedefs, AuthFailure, UnauthorizedContext), refresh_header_link.dart (the internal link refreshTokenHeader uses), and auth_link.dart (just AuthLink itself) — the public API (package:mk_graphql/mk_graphql.dart) is unchanged.
  • Fix: SseLink no longer has its own duplicated, non-deduped 401-refresh logic. It now surfaces an unauthorized response as a ServerException (via the same classifyDioException classification DioLink uses) so an outer AuthLink catches and retries it through its existing single-flight refresh lock — the same path every other link goes through — instead of racing its own independent retry against AuthLink's.
  • HttpLink: Added cancellation support via the existing DioLinkCancelTokenContextEntry context entry (the same CancelToken used everywhere else in the pipeline) — cancelling stops HttpLink from waiting on the in-flight request and throws HttpLinkCanceledException. Note package:http has no request-abort primitive, so the underlying request itself keeps running in the background; it's just no longer waited on. Also added HttpLinkConnectionException for connection-level failures (http.ClientException, e.g. DNS/socket errors) and reclassified malformed response bodies as ResponseFormatException — previously both fell into a single generic ServerException bucket. Added test coverage (previously untested).
  • Fix: AuthLink's internal token-refresh client now shares the exact same configured Dio instance as the rest of the pipeline (same defaultHeaders, same interceptors), instead of silently falling back to a second, bare Dio() whenever the app didn't pass its own dio: to GraphQLClient(...). Previously, refresh requests would go out missing any headers configured via defaultHeaders.
  • Fix: Fixed a race in WebSocketLink where two subscriptions started back-to-back, before the first connection finished handshaking, could each open their own WebSocketChannel — the second silently replacing (and leaking) the first. Connection attempts are now single-flight, mirroring AuthLink's existing refresh dedup.
  • Resource cleanup: GraphQLClient.dispose(), AuthLink, SseLink, HttpLink, and BatchLink now close the Dio/http.Client they created for themselves (never one the caller passed in — that stays the caller's to close). BatchLink.dispose() also cancels its pending batch timer and fails any queued-but-undispatched requests instead of leaving their callers hanging forever.
  • Performance: TypenameLink and the query-document printing done by every terminating link (DioLink, SseLink, WebSocketLink, HttpLink, BatchLink) and by LoggerLink now cache their result per DocumentNode instance. Generated requests hold one static final DocumentNode per operation (and hand-written ones resolve through the shared parse cache), so the same instance recurs on every call of a given operation — previously each call re-walked and rebuilt the whole AST (TypenameLink) and re-serialized it back to a query string (printNode, on every single network request) from scratch.
  • Internal: De-duplicated the twice-written dio.Response JSON-body coercion in DioLink into one helper.

1.2.0 #

  • Breaking: OnRefreshCallback (used by onTokenRefresh on GraphQLClient, AuthLink, and SseLink) now receives a plain GraphQLClient (no auth link attached) instead of a raw DioLink, so refresh logic can use the typed client API (refreshClient.future(...), .stream(...)) instead of hand-rolling a raw HTTP request.
  • Fix: AuthLink now also catches a thrown ServerException (e.g. DioLinkServerException, raised for a real non-2xx HTTP status) and treats a 401/403 status — or a parsed UNAUTHORIZED/UNAUTHENTICATED/FORBIDDEN GraphQL error code — as unauthorized. Previously the refresh-and-retry flow only fired for HTTP 200 responses carrying a GraphQL error payload, silently skipping real HTTP-level 401/403s.
  • Managed token storage: GraphQLClient can now own its auth session end-to-end via the new refreshCallback parameter and TokenStorage (backed by flutter_secure_storage through the new SecureTokenStorage, whose onChange is driven by FlutterSecureStorage.registerListener; injectable for tests via tokenStorage). Supply refreshCallback: (refreshClient, refreshToken) async => AuthToken(...) and the client reads/writes the Authorization header and persists refreshed tokens itself — no more app-side token storage plumbing. New GraphQLClient.setAuthToken() (call after login), .clearAuthToken() (call on logout), .isAuthenticated, and .onAuthTokenChange (a Stream<AuthToken?> that emits on every login/refresh and null on logout or a revoked refresh — listen to it to drive app-wide auth state, e.g. redirect to login).
  • Breaking: Removed onTokenRefresh from GraphQLClient (still available on the lower-level AuthLink/SseLink) — refreshCallback is now the sole source of truth for refresh at the client level. RefreshCallback's refreshToken parameter is now non-nullable: the client skips calling it and revokes immediately when there's no stored refresh token, instead of asking every callback to null-check it.
  • However many requests fail at once, AuthLink makes at most one refresh call and queues the rest behind it, retrying all of them once it resolves — already true via its single-flight refresh lock, now covered by an explicit concurrent-401 test through the refreshCallback path.
  • Pre-parsed request documents: Added GraphQLRequest.documentNode (a DocumentNode), which GraphQLClient now sends instead of parsing document per request. Defaults to the existing shared string-keyed parse cache (GraphQLClient.getParsedDocument) for hand-written requests — no behavior change there — but mk_graphql_generator now overrides it with a static final field computed once per generated request class, skipping even that cache lookup on every subsequent request of that type.
  • Fix: DioLink no longer lets a malformed GraphQL response body (errors/data present but wrongly shaped — a non-Map error entry, a missing message, etc.) escape as a raw, unclassified TypeError. ResponseParser.parseResponse does unchecked casts internally; DioLink now wraps that call so a bad body always surfaces as DioLinkParserException → ParseException (for a 2xx response) or degrades DioLinkServerException.parsedResponse to null while still preserving the real status code (for a non-2xx response), instead of a bug-shaped exception with no stack trace or classification.
  • Added DioLinkConnectionException (DioExceptionType.connectionError — no connectivity, DNS failure, socket error) and DioLinkCertificateException (DioExceptionType.badCertificate — TLS/SSL validation failure) as dedicated DioLink exception types, matching the existing timeout/cancel/server distinction instead of bucketing both into the generic DioLinkUnkownException. GraphQLExceptionExtension.fromLinkException maps them to NetworkException and the new CertificateException respectively (previously connectionError was reclassified after the fact by unwrapping DioLinkUnkownException.originalException in two separate places — now decided once, at the source).

1.1.0 #

  • Dynamic Pagination Variables: Added applyVariables() and updateVariables() to GraphQLPaginator to dynamically update active variables (e.g. search queries, genre/status filters, sorting) on page 1 without recreating the paginator or wiping other active filter criteria.
  • Refetch & Pagination Reset Lifecycle: refetch() resets _baseRequest = initialRequest, restoring page 1 and clearing active filters/search to defaults, while loadNext() persists and accumulates items across pages.
  • Fine-Grained Page Tracking: Added optional getPage and hasNextPage callbacks to GraphQLPaginator.page(...) to allow authoritative backend pagination metadata (such as PageInfo.hasNextPage) to govern whether subsequent pages exist.
  • ErrorPolicy Support: Added ErrorPolicy.none, ErrorPolicy.ignore, and ErrorPolicy.all across requests, client executions, copy constructors, and client defaults.
  • Performance & Cache Optimization: Added deterministic canonical JSON variable cache key sorting and Document AST caching.
  • Documentation: Updated comprehensive guides for client requests, caching, links, and Flutter BLoC emit.forEach pagination patterns.

1.0.1 #

  • Pagination: added GraphQLPaginator with offset/page/cursor factories, a reactive PaginationState (items, isLoading, isLoadingMore, hasMore, error), and GraphQLClient/GraphQLRequest extension methods (paginate, paginateOffset, paginatePage, paginateCursor) for building infinite lists without hand-rolled pagination state.
  • Added an optional requestId override on GraphQLRequest, enabling cross-instance optimistic-response coalescing (used internally by CompositeGraphQLRequest).
  • Repository moved into the unified mk_graphql monorepo alongside mk_graphql_generator; no API changes from the move itself.

1.0.0 #

  • Initial release of mk_graphql.
  • Composable Link Architecture:
    • AuthLink with automatic 401 interception, thread-safe asynchronous token refresh queuing, and token revocation hooks.
    • DioLink powered by Dio with full cancellation support (CancelToken), automatic GraphQL Multipart Request spec uploads (Upload scalar / MultipartFile), and HTTP interceptors.
    • SseLink for Server-Sent Events (SSE) subscriptions with dynamic token header injection and 401 retry support.
    • WebSocketLink for GraphQL over WebSockets (graphql-ws and legacy protocol).
    • HttpLink for lightweight HTTP operations.
    • BatchLink for batching multiple GraphQL requests into a single HTTP payload.
    • TypenameLink for automatic __typename injection into AST selection sets.
    • LoggerLink for structured console logging with customizable options.
  • Client & Caching:
    • GraphQLClient supporting both Future (future()) and Stream (stream()) operations.
    • Pluggable cache interface with built-in normalized InMemoryCache.
    • Configurable fetch policies (cacheFirst, cacheOnly, networkOnly, cacheAndNetwork, noCache).
    • Optimistic response support.
  • Fully typed responses and exceptions (GraphQLResponse, GraphQLException, UnAuthorizedException, RequestCancelledException).
1
likes
140
points
1.05k
downloads

Documentation

API reference

Publisher

verified publisherkishormainali.com

Weekly Downloads

A clean, modern, simplified GraphQL client for Dart & Flutter with normalized caching, composable links, and stream-based operation execution.

Homepage
Repository (GitHub)
View/report issues

Topics

#graphql #flutter #client #sse #dio

License

MIT (license)

Dependencies

async, collection, dio, flutter, gql, gql_exec, gql_link, hive_ce, meta, token_storage, web_socket_channel

More

Packages that depend on mk_graphql