Files
AcWeb/ac_web/lib/Widgets/AutoRefreshState.dart
T

69 lines
2.1 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
/// Keeps the data of a widget up to date by reloading it every
/// [refreshInterval].
///
/// Mix this into the [State] of every widget that shows data of the backend and
/// implement [RefreshData]. The mixin only owns the timer, fetching the data and
/// putting it on screen stays in the widget itself.
///
/// The backend has no push channel, so the only way to notice new agents,
/// tasks or reports is polling. The interval is therefore a compromise between
/// fresh data and load on the api.
///
/// A refresh happens quietly: implementations are expected to keep the data
/// that is already on screen while the request runs, so that the user neither
/// sees a spinner nor loses the scroll position every few seconds. Failures of
/// a refresh are swallowed on purpose - the data on screen simply stays as it
/// is and the next tick tries again.
mixin AutoRefreshState<TWidget extends StatefulWidget> on State<TWidget> {
/// Interval in which the api data of the whole app is refreshed.
static const Duration refreshInterval = Duration(seconds: 15);
Timer? _refreshTimer;
/// True while a refresh triggered by the timer is running. A slow api must
/// not pile up requests.
bool mIsAutoRefreshing = false;
@override
void initState() {
super.initState();
_refreshTimer = Timer.periodic(refreshInterval, (_) => _onTick());
}
@override
void dispose() {
_refreshTimer?.cancel();
_refreshTimer = null;
super.dispose();
}
/// Fetches the data of this widget again. Called every [refreshInterval] as
/// long as the widget is mounted.
///
/// Implementations may let api errors escape, [_onTick] catches them.
Future<void> RefreshData();
Future<void> _onTick() async {
if (mIsAutoRefreshing || !mounted) {
return;
}
mIsAutoRefreshing = true;
try {
await RefreshData();
} catch (error) {
// An automatic refresh must never replace data that is already on screen
// with an error message, so a failed tick is ignored.
} finally {
mIsAutoRefreshing = false;
}
}
}