Skip to content

Instantly share code, notes, and snippets.

@nhancv
Last active April 14, 2021 10:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nhancv/5e45020386a2bc010c614b09978d5e22 to your computer and use it in GitHub Desktop.
Save nhancv/5e45020386a2bc010c614b09978d5e22 to your computer and use it in GitHub Desktop.
Flutter observer pattern. Ex: global timer
class Subject<T> {
List<Observer<T>> observers = <Observer<T>>[];
T _state;
T get state => _state;
set state(T value) {
_state = value;
notifyAllObservers();
}
void addObserver(Observer<T> observer) {
observers.add(observer);
}
void removeObserver(Observer<T> observer) {
observers.remove(observer);
}
void notifyAllObservers() {
for (final Observer<T> observer in observers) {
observer.update(state);
}
}
}
abstract class Observer<T> {
void update(T state);
}
import 'observable.dart';
class TimerProvider extends Subject<int> {
}
import 'package:flutter/material.dart';
import 'package:habitapp/services/app/observable.dart';
import 'package:habitapp/services/app/timer_provider.dart';
import 'package:provider/provider.dart';
class WObservable extends StatefulWidget {
const WObservable({Key key, this.child, this.update}) : super(key: key);
final Widget child;
final Function(int state) update;
@override
_WObservableState createState() => _WObservableState();
}
class _WObservableState extends State<WObservable>
with WidgetsBindingObserver, Observer<int> {
TimerProvider timerProvider;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
timerProvider = Provider.of(context, listen: false);
_attach();
});
}
@override
void dispose() {
_detach();
super.dispose();
}
@override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
print('state: $state');
switch (state) {
case AppLifecycleState.paused:
_detach();
break;
case AppLifecycleState.resumed:
_attach();
break;
default:
break;
}
}
@override
Widget build(BuildContext context) {
return widget.child ?? Container();
}
@override
void update(int state) {
if (widget.update != null) {
widget.update(state);
}
}
// Attach to timer provider
void _attach() {
if (timerProvider != null) {
timerProvider.addObserver(this);
update(timerProvider.state);
}
}
// Detach from timer provider
void _detach() {
timerProvider?.removeObserver(this);
}
}
Future<void> myMain() async {
/// Start services later
WidgetsFlutterBinding.ensureInitialized();
/// Run Application
runApp(
MultiProvider(
providers: <SingleChildWidget>[
Provider<TimerProvider>(create: (_) => TimerProvider()),
],
child: const MyApp(),
),
);
}
class MyApp extends StatefulWidget {
const MyApp({Key key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
Timer _timer;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
/// After first build
WidgetsBinding.instance.addPostFrameCallback((_) {
_startTimer();
});
}
@override
void dispose() {
_stopTimer();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
switch (state) {
case AppLifecycleState.paused:
_stopTimer();
break;
case AppLifecycleState.resumed:
_startTimer();
break;
default:
break;
}
}
@override
Widget build(BuildContext context) {
/// Build Material app
return MaterialApp(
debugShowCheckedModeBanner: false,
home: WObservable(),
);
}
/// Start timer
Future<void> _startTimer() async {
_stopTimer();
await _timerLogic();
_timer = Timer.periodic(const Duration(seconds: 1), (_) {
_timerLogic();
});
}
// Timer logic
Future<void> _timerLogic() async {
context.read<TimerProvider>().state = DateTime.now().millisecondsSinceEpoch;
}
/// Stop timer
void _stopTimer() {
if (_timer != null) {
_timer.cancel();
_timer = null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment