bifrosted 0.10.2 copy "bifrosted: ^0.10.2" to clipboard
bifrosted: ^0.10.2 copied to clipboard

The rainbow bridge connecting your app to APIs. A lightweight REST API client and repository pattern with caching, offline support, and error handling.

Changelog #

0.10.2 #

  • Fixed: RestAPI.host rejected any value containing a colon. _buildUri passed host straight into Uri(scheme: 'https', host: host), and Uri's host parameter reads a colon as an IPv6 literal — so a scheme-qualified URL, a port, and therefore any local dev server threw FormatException: Illegal IPv6 address, invalid character (at character 1), an error naming none of the actual causes.

    host now accepts what you'd reasonably put in a config value:

    host Request URL
    api.example.com https://api.example.com/...
    https://api.example.com as given
    http://localhost:8080 as given — scheme and port preserved
    10.0.2.2:3000 https://10.0.2.2:3000/...
    https://example.com/v1 base path preserved, not dropped

    Backward compatible: a bare hostname still resolves to https.

  • An empty host now throws ArgumentError naming the likely cause — an app launched without --dart-define-from-file — instead of silently building https:///path and failing at the transport layer.

0.10.1 #

  • Widened the logger re-export. Logger and Level alone were not enough to reconfigure bifrostLogger: its default DevelopmentFilter drops every log in release builds and ignores level, so keeping warnings in release meant naming ProductionFilter — which required adding a direct logger dependency just to reassign our own global. Now also exports ProductionFilter, DevelopmentFilter, LogFilter, LogPrinter, LogOutput, PrettyPrinter, and SimplePrinter.
    bifrostLogger = Logger(
      level: kReleaseMode ? Level.warning : Level.debug,
      filter: ProductionFilter(),   // required; DevelopmentFilter ignores `level`
    );
    

0.10.0 #

  • Fixed: nothing persisted across app launches. SharedPrefService.init() called SharedPreferences.setMockInitialValues({}), which replaces the platform store with an empty in-memory map (SharedPreferencesStorePlatform.instance = InMemorySharedPreferencesStore.withData(...)). Because init() runs on every launch, no preference, token, onboarding flag, or cached response survived a cold start. init() now only reads. If your app appeared to "forget everything," this was why.
  • Breaking: removed SharedPrefService.updateInitialMock. It was test scaffolding on a production class, and it set the mock store as a side effect. Replace it with useMockStorage.
  • Added useMockStorage({values}) to package:bifrosted/testing.dart — the storage counterpart to useMockClient. Tests need it for two reasons: there is no SharedPreferences platform plugin in a test process, so getInstance() otherwise throws MissingPluginException; and the mock store is a static platform instance, so a value written by one test is still readable in the next unless it is reset. Call it in setUp (or once per file, before initServices):
    useMockStorage();                                   // clean slate
    useMockStorage(values: {'onboarding_done': true});  // seeded state
    
  • Added [OnboardingController], a reusable multi-step flow controller. Subclass it, implement buildResult, and inherit next/back/skip, answer collection, completion, and the analytics funnel.
  • It is a plain ChangeNotifier — no state-management dependency — matching the documented exception for one-time flows that run before the app's main state exists.
  • Emits the events a funnel is computed from: started, step_viewed, answered, skipped, back, completed (with duration, reach and skip count), and abandoned on dispose-before-completion, naming the step the user quit on. The event prefix is configurable.
  • step_viewed fires once per step per session, on first arrival only. Re-counting a revisit after back() would inflate early steps and overstate the funnel's health.
  • The controller reports events, not rates: a completion rate is a population statistic that one session cannot know. completed ÷ started is a query in your analytics tool.
  • back() never clears answers — losing input on a back tap is the most reliable way to cause abandonment.

0.9.0 #

  • Breaking: fetch and mutate return BifrostResult<T> instead of T?. A bare null told callers that something failed but never what, so every screen could only show generic copy. BifrostFailure carries a FailureReason, an optional status code, and diagnostic detail. Migrate call sites from a null check to an exhaustive switch:
    // Before
    final user = await repo.getUser(id);
    if (user != null) {
      print(user.name);
    } else {
      // Error was already handled by SystemNotifier
    }
    
    // After
    switch (await repo.getUser(id)) {
      case BifrostSuccess(:final data):
        print(data.name);
      case BifrostFailure(:final reason):
        // Error was already handled by SystemNotifier; render an error state.
    }
    
  • Breaking: mutate takes a required model: instead of an optional fromJson:. This is not a rename — every existing body-less mutate(apiRequest: ..., invalidateKeys: [...]) call (the pattern fromJson being optional made possible) is now a compile error. Switch those call sites to send(), which already returns bool and takes the same invalidateKeys:.
  • Breaking (behavior): a 2xx response with an empty body through mutate now returns BifrostFailure(FailureReason.parse) instead of nullmutate has nothing to deserialize into model: when there's no body. Cache invalidation still runs. Use send() for writes that don't return a body.
  • Added silent: to fetch, mutate, and send — a background refresh no longer fires SystemNotifier.
  • Added CachePolicy (networkFirst, cacheFirst, networkOnly). Cache fallback now keys on request outcome rather than ConnectionChecker.isConnected: online-but-failing previously returned nothing while holding usable cached data. 4xx is excluded from fallback, since a definitive server answer must not be masked by stale data.
  • cacheDuration now means something under cacheFirst; previously it only bounded the offline fallback.
  • Added RestAPI.resolveHeaders() and RestAPI.onUnauthorized() for one-shot 401 refresh, with a shared in-flight future so concurrent 401s cause one refresh, not five. Defaults preserve prior behaviour.
  • Removed DeserializationException, which was defined, exported, and never thrown.

0.8.0 #

  • Added app-facing service interfaces: [AnalyticsService], [EntitlementService], [PaywallPresenter], and [AttributionService], each with a NoOp default. bifrosted does not call these — they live here so one package covers every project's service shapes. No vendor SDK is added; the app supplies implementations.
  • [EntitlementService] exposes entitlements as a Stream, not a one-shot check, because they change mid-session on purchase, restore, and expiry.
  • [PaywallPresenter] is separate from [EntitlementService] so Superwall, RevenueCat, and custom Flutter paywalls swap without touching call sites.
  • [AttributionService] models the platform asymmetry deliberately: Android's Play Install Referrer yields a per-install code, iOS has no runtime equivalent and captureReferral() returning null there is the expected result.
  • The entitlement and paywall no-ops warn through bifrostLogger when they swallow a user-initiated action; the analytics and attribution no-ops stay silent, since shipping without analytics and returning null on iOS are both legitimate steady states.
  • bifrostTestEnv gains analytics, entitlements, paywalls, and attribution doubles, all resolvable through bifrostServiceLocator.

0.7.0 #

  • Breaking: Removed the firebase_performance dependency. bifrosted now defines [PerformanceTracker] / [HttpTrace] / [BifrostHttpMethod] and ships [NoOpPerformanceTracker]; the app supplies the SDK-specific implementation and assigns bifrostPerformanceTracker.
  • Breaking: Removed bifrostPerformanceEnabled. Assign const NoOpPerformanceTracker() to disable metrics.
  • Breaking: useMockClient moved from package:bifrosted/bifrosted.dart to package:bifrosted/testing.dart. package:http/testing.dart is no longer imported by core.
  • Breaking: Fake data is now seeded by default (kDefaultFakeSeed), making Model.fake() reproducible across runs so golden tests are stable. Set bifrostFakeSeed = null for the previous random behaviour.
  • Traces complete without await on the request path, so a slow monitoring SDK no longer adds latency to every request.
  • Unlike the 0.5.5 tracker, resolution is a plain global with a no-op default rather than bifrostServiceLocator, so no registration is required and tryBifrostServiceLocator is unnecessary.

0.6.0 #

  • Added direct Firebase Performance HTTP metric instrumentation in [RestAPI]
  • Adds firebase_performance as a dependency
  • Toggle via top-level bifrostPerformanceEnabled (defaults to true; auto-disabled when Firebase isn't initialized)
  • Test env disables performance monitoring automatically via bifrostTestEnv.reset()

0.5.6 #

  • Breaking: Removed [PerformanceTracker], [tryBifrostServiceLocator], and HTTP metric hooks from [RestAPI]
  • [RestAPI] reuses one [http.Client] per instance; use [RestAPI.closeClient] when discarding an API

0.5.5 #

  • Added pluggable [PerformanceTracker] via [bifrostServiceLocator] for HTTP metrics (removed in 0.5.6)
  • [RestAPI] uses performance hooks when a tracker is set (no-op when null)
  • [MockPerformanceTracker] in package:bifrosted/testing.dart for tests (removed in 0.5.6)
  • App supplies Firebase/Sentry/etc. implementations; bifrosted has no Firebase dependency

0.5.4 #

  • Breaking: Simplified [SystemNotifier] to three UI-only callbacks:
    • onNetworkError(), onUnauthorized(), onRequestFailed({statusCode, body})
    • Removed onForbidden, onServerError, onApiError
  • Docs: notifiers must handle user-facing UI only; use bifrostLogger for diagnostics

0.5.3 #

  • Breaking: Removed fetchList — use fetch<List<T>> with the same fromJson
  • Breaking: Removed endpoint on fetch — use cacheKey only (it was only used for caching)
  • fetch<R, M> auto-detects top-level JSON array vs object; fromJson is always the item parser (User.fromJson), return type is User or List<User>

0.5.2 #

  • Removed unwrapResponse() override
  • Removed in-memory singleton cache (_memoryCache, clearMemoryCache, useMemoryCache)
  • Simplified fetch/fetchList signatures

0.5.1 #

  • Added bifrostJsonDecode - Global async JSON decoder
    • Defaults to synchronous jsonDecode (works everywhere)
    • Override to decode on a background isolate for large payloads:
      // Dart:
      bifrostJsonDecode = (body) => Isolate.run(() => jsonDecode(body));
      
      // Flutter:
      bifrostJsonDecode = (body) => compute(jsonDecode, body);
      
    • Web-safe: no dart:isolate dependency in bifrosted itself

0.5.0 #

  • Breaking: Removed dart:io dependency - now works on web/Jaspr
  • Breaking: post/put/patch/delete body is now Object? instead of String?
    • Maps and Lists are auto-encoded to JSON
    • Strings are sent as-is
  • Breaking: Removed Deserializer class - deserialization is now inlined
  • Added unwrapResponse() override for wrapped API responses ({"data": {...}})
  • Added mutate<T>() for write operations (POST/PUT/PATCH/DELETE) with optional deserialization
  • Added send() for fire-and-forget writes that return bool
  • Both mutate and send accept invalidateKeys for automatic cache invalidation
  • Added in-memory singleton cache for deserialized objects
    • BifrostRepository.clearMemoryCache() to reset
    • useMemoryCache parameter on fetch/fetchList to opt out per call
  • clearAllCache() now only removes bifrost-prefixed keys (no longer wipes all storage)
  • DeserializationException is still exported for custom use

0.4.5 #

  • update dependencies

0.4.0 #

  • Breaking: Removed generics from BifrostRepository
  • Added bifrostServiceLocator - Set once, used everywhere
    // At app startup:
    bifrostServiceLocator = <T>() => Get.find<T>();
    
  • Repositories now have zero boilerplate:
    class UserRepo extends BifrostRepository {
      Future<User?> getUser(String id) => fetch<User>(...);
    }
    

0.3.0 #

  • Added global mock client support for testing
    • useMockClient() - Enable mock responses for all RestAPI instances
    • useRealClient() - Reset to real HTTP clients
    • setClientFactory() - Set custom client factory
    • No more per-API client overrides needed

0.2.2 #

  • Fixed build.yaml to correctly combine generated code into .g.dart files
    • Changed build_to: cache and build_extensions: .fake.g.part
    • Generator output now properly merges with json_serializable/freezed

0.2.1 #

  • Added build.yaml for auto-discovery by build_runner
    • No manual configuration needed - just add the dependency and run build_runner
    • Works like freezed/json_serializable out of the box

0.2.0 #

  • Added @generateFake annotation for code generation
  • Added FakeUtils utility class (uses faker package)
    • fakeForKey(String key) - generates fake data based on field name
    • create<T>() - generates fake model from factory
    • fakeJson() / fakeJsonList() - generic JSON generators
  • Added FakeGenerator for build_runner integration
    • Generates .fake() extension methods for annotated classes
    • Works with freezed models

0.1.1 #

  • Updated README

0.1.0 #

  • Initial release
  • RestAPI abstract class for REST API clients
    • GET, POST, PUT, PATCH, DELETE methods
    • Automatic error handling and logging
    • Header management with extra headers support
  • BifrostRepository for repository pattern with caching
    • fetch<T>() and fetchList<T>() for automatic deserialization
    • Offline-first with cache fallback
    • Automatic cache expiration
  • SystemNotifier interface for global error handling
    • onNetworkError(), onUnauthorized(), onForbidden()
    • onServerError(), onApiError()
  • StorageService interface for pluggable storage backends
  • ConnectionChecker interface for connectivity detection
  • Uses logger package for logging
  • Comprehensive test suite
0
likes
90
points
205
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

The rainbow bridge connecting your app to APIs. A lightweight REST API client and repository pattern with caching, offline support, and error handling.

Repository (GitHub)
View/report issues

Topics

#api #http #rest #repository #caching

License

MIT (license)

Dependencies

analyzer, build, faker, flutter, flutter_test, http, logger, share_plus, shared_preferences, source_gen, web

More

Packages that depend on bifrosted