Last active
June 23, 2022 17:43
-
-
Save fabiancrx/662810f6e8ddfc2e7587c817cbaa41e6 to your computer and use it in GitHub Desktop.
A widget that executes a callback periodically, respecting app lifecycle.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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