import 'dart:async'; import 'package:riverpod_annotation/riverpod_annotation.dart'; /// Mixed into a `@riverpod` `AutoDisposeAsyncNotifier` to auto-refresh it on /// a timer while it has at least one listener — Dashboard's summary and /// Fleet's aggregated view, specifically. The API has no push/websocket, so /// this is the honest refresh model: a manual pull plus a background /// timer, not a live stream. /// /// Usage: `class FooNotifier extends _$FooNotifier with PollingMixin`, /// implement [interval] and [fetch], and have `build()` return /// `startPolling()`. mixin PollingMixin on $AsyncNotifier { /// How often to refetch while this provider has at least one listener. Duration get interval; /// Performs the actual fetch. Thrown exceptions surface as /// `AsyncValue.error` exactly like any other async provider. Future fetch(); /// Call from `build()`: starts the periodic timer (cancelled on dispose) /// and returns the initial fetch. Future startPolling() { final timer = Timer.periodic(interval, (_) => refresh()); ref.onDispose(timer.cancel); return fetch(); } /// Re-fetches and updates state. Deliberately never emits an /// intermediate `AsyncLoading` — the previous data/error stays on screen /// until the new result lands, so a background tick never flashes the /// UI back to a loading spinner. Future refresh() async { state = await AsyncValue.guard(fetch); } }