NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev
A typed, functional HTTP client for Flutter. Wraps Dio with Either-based error handling, token refresh, retries, response caching, and a built-in test double.
Last release 1 months ago
17 Aug 2026
Release timing varies
gaps range from 1 weeks to 2 months
Nearly every release is documented
notes for 13 of 13 stable releases
Nothing withdrawn
no release was ever pulled
6 months old
13 releases · first in 2026
One column per month.
If you switch exhaustively over either enum, that switch no longer compiles until you add a case. This is the only source-breaking change and the reas…
Everything from 1.x keeps working — the old entrypoints are deprecated, not removed. See MIGRATION.md for the three things that can actually break you.
receiveTimeout for that call or globally, rather than removing the bound.ErrorSource and ResponseCode gained a parseError variant. If you switch exhaustively over either enum, that switch no longer compiles until you add a case. This is the only source-breaking change and the reason this is a major release.Failure equality now includes data and statusCode. Two failures that differ only in response body are no longer equal.ApiCacheHelper.getCacheData threw on a cache miss. The underlying APICacheManager.getCacheData calls .first on an empty query result, so asking for a URL that was never cached raised StateError despite the nullable return type promising otherwise. It now returns null.APICacheManager.addCacheData checks isAPICacheKeyExist and then inserts or updates, with no transaction around the pair, so two overlapping writes both insert. The duplicate rows are not self-correcting: isAPICacheKeyExist answers rows.length == 1, so it then reports the key as missing forever while every further write appends another row. Request de-duplication makes overlapping same-key writes routine rather than rare, so setCacheData now serialises writes per key.FormData is a stream Dio reads once — finalize() throws StateError on a second read. Both replay paths hit it: a 429/503 retry (those bypass the idempotency check, so an upload is retried) and a 401 token refresh, which is a likely outcome for an upload long enough to outlive its token. The body is now rebuilt with FormData.clone() before either replay.content-type in caller-supplied headers broke multipart uploads. It was only stripped from the package's default headers, so an upload that also needed, say, an X-Tenant-Id header silently sent application/json and lost the multipart boundary.async body runs synchronously to its first await, so such a probe completed — and cleared the in-flight slot — before that slot was assigned, stranding a completed future that reported offline for the rest of the session./users with params: {'page': 1} and page: 2 shared one entry and the second overwrote the first. Query parameters are now part of the key, sorted so argument order does not matter.connectivityCacheTtl (default 5s). Negative results are deliberately never cached, since that is exactly when the user is retrying.Failure discarded the response body, making 422 field errors unreachable.DioExceptionType.badCertificate mapped to the generic "unexpected error" instead of connectionFailure.ApiConfig + ApiServices.init(...) — one object for every setting, replacing configure() and the loose static setters. Adds baseUrl (so call sites pass /users, not the full URL), connectTimeout/receiveTimeout/sendTimeout, and defaultHeaders.getAs<T>, postAs<T>, putAs<T>, patchAs<T>, deleteAs<T> take a parser and return Either<Failure, T>. A throwing parser becomes a Failure with ErrorSource.parseError carrying the raw body; no exception escapes. listParser(User.fromJson) handles list endpoints, with an optional key for {"data": [...]} wrappers.cachePolicy and cacheMaxAge on get/getAs, with CachePolicy.cacheFirst, networkFirst, cacheOnly, and the default networkOnly (unchanged behaviour). response.isFromCache tells you which you got. ApiCacheHelper was already in the package but nothing called it.ApiConfig.cacheEnabled: false forbids caching outright, overriding any cachePolicy a call site passes and never opening the database; ApiConfig.defaultCachePolicy sets the policy for calls that don't name one. Not every app should cache, and "just don't pass a policy" relies on every call site getting it right.ApiLogOptions.trimBase64 — collapses base64 blobs in log output. An API returning photographs or fingerprints inline turns a single response into thousands of console lines, because the logger wraps every value at maxWidth and has no notion of a field worth hiding. With this on, a blob prints as a recognisable head plus a count of what was elided, and everything else passes through byte for byte. Off by default. Base64LogTrimmer is exported for tuning minRunLength/keptChars or placing the trimmer in front of your own sink.CancelToken, since cancelling one caller must not cancel the other; opt out with dedupe: false.RetryPolicy — configurable maxRetries, baseDelay, maxDelay, retryableStatusCodes, and a retryIf predicate. Retries now cover status codes as well as transport errors: 429 and 503 for any method (the server told us it did not process the request), 408/500/502/504 for idempotent methods only. Retry-After is honoured in both its delta-seconds and HTTP-date forms. Backoff is exponential with full jitter, replacing the lockstep linear interval that made concurrent failures retry in unison.ApiConfig.isSuccess — treat a 200 carrying {"success": false} as a failure, with the message pulled from the body the same way a real error response would be.ApiObserver — one hook for every request, response, and failure, for Sentry/Crashlytics/analytics. Fires exactly once per call, including for failures Dio never produces an exception for (offline short-circuits, parse errors, isSuccess rejections). A throwing observer can never break a request.ApiConfig.httpClientAdapter — supply your own adapter for certificate pinning or to route through Charles/Proxyman. Deliberately consumer-supplied so the package stays usable on web.upload() — multipart uploads with progress, taking UploadFile.fromPath (mobile/desktop) or UploadFile.fromBytes (web, where a picked file has no path). The multipart body is rebuilt before any replay — a 429/503 retry or a 401 token refresh — because a FormData is a stream that Dio reads once and refuses to read again. A JSON content-type is stripped for multipart bodies, including one you pass yourself.ApiServices.reset() — clears the singleton, config, and loader callbacks. Fixes setLogging being silently a no-op after the first instance() call, and gives tests a clean slate.package:baaba_api_handler/testing.dart — ships FakeApiServices, an in-memory double with stubbing and call recording, so consumers can test repositories without mocking Dio. An unstubbed endpoint throws a StateError naming it rather than quietly returning null.Failure.data, Failure.statusCode, Failure.validationErrors, Failure.requestOptions — the raw body, the literal HTTP status (code collapses anything unrecognised to defaultError), per-field errors parsed from a {"errors": {...}} body, and the request that failed. requestOptions is excluded from equality: it is context about where a failure came from, not part of what the failure is.package:baaba_api_handler/baaba_api_handler.dart as the conventional entrypoint. The old ts_api_handler.dart import still works.Added ApiLogOptions — the consuming app now controls what the console logger prints: enabled, request, requestHeader, requestBody, responseHeader, res
ApiLogOptions — the consuming app now controls what the console logger prints: enabled, request, requestHeader, requestBody, responseHeader, responseBody, error, maxWidth, compact, and logPrint. Previously the logger was hardcoded to requestBody: true with no way to change it. Includes an ApiLogOptions.disabled() constructor and copyWith.logging parameter to ApiServices.configure(), defaulting to const ApiLogOptions() — same output as before, so existing callers see no change.ApiServices.setLogging(ApiLogOptions) — same control for apps that don't use token auth. Must be called before the first ApiServices.instance(), since the Dio client and its logger are built once and cached.kReleaseMode is true.Added support for the detail key in API error responses (RFC 7807), falling back to it if message is missing but prioritizing it over error.
detail key in API error responses (RFC 7807), falling back to it if message is missing but prioritizing it over error.Added ApiServices.download() — streams a file response directly to disk (savePath) instead of loading it into memory, with onReceiveProgress, cancelTo
ApiServices.download() — streams a file response directly to disk (savePath) instead of loading it into memory, with onReceiveProgress, cancelToken, and deleteOnError support. Goes through the same connectivity check and loader plumbing as the other HTTP methods.refreshTimeout parameter to ApiServices.configure() (default 30 seconds). Bounds how long a request that 401s while another refresh is already in flight will wait for that refresh before giving up and failing with the original error.TokenRefreshInterceptor: requests that 401 while a refresh is already in progress now wait for that refresh to finish and retry with the fresh token, instead of failing immediately. Previously only the first request in a concurrent-401 burst would succeed; the rest errored out just for losing the race.NetworkRetryInterceptor now only auto-retries idempotent methods (GET, HEAD, OPTIONS, PUT, DELETE). POST/PATCH are no longer retried on transient timeouts/connection errors, since the server may have already processed the request and a blind retry could duplicate the side effect.maxAge parameter to ApiCacheHelper.getCacheData() — if the cached entry is older than maxAge, it's treated as a miss (the stale entry is cleared and null returned) instead of returning stale data. Omit it to keep the previous behaviour of returning cached data regardless of age.dio to ^5.10.0, pretty_dio_logger to ^1.4.0, internet_connection_checker_plus to ^3.1.1, fpdart to ^1.2.0, equatable to ^2.1.0, and sqflite_common_ffi (dev) to ^2.4.0+3.Added ApiServices.configureLoader({onShow, onHide}) — a global, framework-agnostic loading indicator shown automatically around every request (success
ApiServices.configureLoader({onShow, onHide}) — a global, framework-agnostic loading indicator shown automatically around every request (success, failure, and thrown exceptions all covered), so callers no longer need a per-screen isLoading flag. onShow/onHide are plain callbacks (e.g. Get.dialog/Get.back for GetX, or showDialog/Navigator.pop with a global key) — the package has no UI dependency.showLoader parameter (default true) to get/post/put/patch/delete to opt a specific request out of the loader.onShow fires only for the first in-flight request, onHide only once every in-flight request has finished.Breaking: ErrorSource enum variants renamed from snake_case to camelCase (e.g. no_content → noContent, bad_request → badRequest). Update any switch or
ErrorSource enum variants renamed from snake_case to camelCase (e.g. no_content → noContent, bad_request → badRequest). Update any switch or direct references in your code.bypassConnectivityCheck parameter to ApiServices.configure() for staging/internal environments where connectivity probes fail due to proxies or firewalls.ApiServices.setConnectivityCheck({bool enabled}) — controls the connectivity check independently of token auth configuration.cancelRequest() now cancels all in-flight requests (previously only the most recent). All active CancelTokens are tracked in a Set and cancelled together.ResponseCode and ErrorSource with six new HTTP status codes: created (201), requestTimeout (408), conflict (409), unprocessableEntity (422), tooManyRequests (429), badGateway (502).ResponseCode.noContent raw value from 201 to 204.ResponseCode refactored to use inline integer values (ResponseCode.success(200) style) — no longer requires an extension for .value.ResponseStrings rewritten with cleaner, user-facing error messages.ErrorHandler no longer implements Exception.Added TokenRefreshInterceptor for automatic token refresh on 401 responses.
TokenRefreshInterceptor for automatic token refresh on 401 responses.NetworkRetryInterceptor for automatic retry on transient network failures.ApiServices.configure() static method for setting up token-based authentication.headerBuilder parameter to ApiServices.configure() for customising auth headers per request.onSendProgress, onReceiveProgress, and CancelToken parameters to all request methods.Response and CancelToken from Dio, and APICacheDBModel from api_cache_manager — no separate imports needed.Updated readme documentation and bumped version.
* Added license information.
Updated package structure and internal functionality.
Fixed multipart request method naming.
Added multipart request support.
HTTP methods: GET, POST, PUT, PATCH, DELETE.
ApiCacheHelper.Failure, ErrorSource, and ResponseCode.Your coding agent can read these notes before it upgrades. Set up the MCP server →