import 'dart:async'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; typedef RefreshCallback = FutureOr Function(); /// Coordinates safe refresh points without owning any feature data. /// /// A screen registers its callback while it is visible. Registered callbacks /// run when the app returns to the foreground; callers can also request a /// manual refresh through the returned handle. class RefreshCoordinator with WidgetsBindingObserver { RefreshCoordinator() { WidgetsBinding.instance.addObserver(this); } final Set<_RefreshRegistration> _registrations = {}; AppLifecycleState _lifecycleState = AppLifecycleState.resumed; bool get isForeground => _lifecycleState == AppLifecycleState.resumed; RefreshRegistration register({ required RefreshCallback onRefresh, bool active = true, }) { final registration = _RefreshRegistration(onRefresh, active); _registrations.add(registration); return RefreshRegistration._(this, registration); } @override void didChangeAppLifecycleState(AppLifecycleState state) { _lifecycleState = state; if (state == AppLifecycleState.resumed) refreshVisible(); } void refreshVisible() { for (final registration in _registrations) { if (registration.active) unawaited(_run(registration)); } } Future _run(_RefreshRegistration registration) async { try { await registration.onRefresh(); } catch (_) { // Refresh failures belong to the owning feature's state/UI. } } void dispose() { WidgetsBinding.instance.removeObserver(this); _registrations.clear(); } } class RefreshRegistration { RefreshRegistration._(this._coordinator, this._registration); final RefreshCoordinator _coordinator; final _RefreshRegistration _registration; bool get active => _registration.active; set active(bool value) => _registration.active = value; void refreshNow() => _coordinator._run(_registration); void dispose() => _coordinator._registrations.remove(_registration); } class _RefreshRegistration { _RefreshRegistration(this.onRefresh, this.active); final RefreshCallback onRefresh; bool active; } final refreshCoordinatorProvider = Provider((ref) { final coordinator = RefreshCoordinator(); ref.onDispose(coordinator.dispose); return coordinator; });