Last active
April 14, 2021 10:55
-
-
Save nhancv/5e45020386a2bc010c614b09978d5e22 to your computer and use it in GitHub Desktop.
Flutter observer pattern. Ex: global timer
This file contains 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
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); | |
} |
This file contains 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 'observable.dart'; | |
class TimerProvider extends Subject<int> { | |
} |
This file contains 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 '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); | |
} | |
} |
This file contains 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
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