Skip to content

Instantly share code, notes, and snippets.

@fabiancrx
Last active June 23, 2022 17:43
Show Gist options
  • Save fabiancrx/662810f6e8ddfc2e7587c817cbaa41e6 to your computer and use it in GitHub Desktop.
Save fabiancrx/662810f6e8ddfc2e7587c817cbaa41e6 to your computer and use it in GitHub Desktop.
A widget that executes a callback periodically, respecting app lifecycle.
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
/// A widget that executes a [callback] on several conditions.
///
/// - If [pollingTime] is not null the [callback] will be executed periodically given that [pollingTime]
/// This widget respects the apps lifecycle and won't execute the callback if the app is in the background.
///
/// - If [updateDataOnResume] is true, when coming from background [callback] will be executed
/// - If [updateOnInitState] is true, [callback] will executed once as soon as the widget is mounted
class PeriodicCaller extends StatefulWidget {
final bool updateDataOnResume;
final bool updateOnInitState;
final Duration? pollingTime;
final VoidCallback? callback;
final Widget child;
PeriodicCaller({
Key? key,
required this.callback,
this.updateDataOnResume = true,
required this.child,
this.pollingTime,
this.updateOnInitState = false,
}) : super(key: key);
@override
_PeriodicCallerState createState() => _PeriodicCallerState();
}
class _PeriodicCallerState extends State<PeriodicCaller>
with WidgetsBindingObserver {
AppLifecycleState appLifecycleState = AppLifecycleState.resumed;
Timer? pollingTimer;
@override
void initState() {
WidgetsBinding.instance.addObserver(this);
pollingTimer = _initPollingTimer();
if (widget.updateOnInitState) widget.callback?.call();
super.initState();
}
Timer? _initPollingTimer() {
pollingTimer?.cancel();
return widget.pollingTime != null
? Timer.periodic(widget.pollingTime!, (_) {
widget.callback?.call();
})
: null;
}
@override
void didUpdateWidget(oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.pollingTime != widget.pollingTime) {
pollingTimer = _initPollingTimer();
}
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
pollingTimer?.cancel();
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
appLifecycleState = state;
switch (state) {
case AppLifecycleState.resumed:
if (mounted) {
if (widget.updateDataOnResume) {
// Manually trigger a refresh
widget.callback?.call();
}
if (widget.pollingTime != null) {
// Restart polling while on foreground
pollingTimer = _initPollingTimer();
}
}
break;
case AppLifecycleState.inactive:
//Stop polling on background (to decrease battery consumption)
pollingTimer?.cancel();
break;
case AppLifecycleState.paused:
break;
case AppLifecycleState.detached:
break;
}
}
@override
Widget build(BuildContext context) {
return widget.child;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty<VoidCallback?>('callback', widget.callback))
..add(DiagnosticsProperty<bool>(
'updateDataOnResume', widget.updateDataOnResume))
..add(DiagnosticsProperty<bool>(
'updateOnInitState', widget.updateOnInitState))
..add(DiagnosticsProperty<Timer>('polling_timer', pollingTimer));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment