85 lines
2.3 KiB
Dart
85 lines
2.3 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
|
|
typedef RefreshCallback = FutureOr<void> 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<void> _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<RefreshCoordinator>((ref) {
|
|
final coordinator = RefreshCoordinator();
|
|
ref.onDispose(coordinator.dispose);
|
|
return coordinator;
|
|
});
|