Lifecycle(앱 상태) 이벤트 1편
Lifecycle(앱 상태) 이벤트 2편
Lifecycle(앱 상태) 이벤트 4편
Lifecycle(앱 상태) 이벤트 5편
get | Flutter Package
shared_preferences | Flutter Package
App Life Cycle에 대하여 정리해 놓은 인트로 참고하세요 !
App Lifecycle Intro
이전 글에 이어서 이번 글에서도 Flutter에서 앱 상태를 수신하는 또 하나의 방법인 GetX에서 수신하는 방법에 대해서 알아보도록 하자.
이전 글에서 사용한 방식과 동일하게 하였다.
앱 상태를 수신 받아 각 앱 상태의 변경에 대해서 UI로 나타내 주고자 로컬 저장소를 사용하여 만들어 보았다.
상태가 변경될 때 상태와 시간을 shared_preferences 라이브러리를 통해 로컬 저장소에 저장한 뒤 화면에 노출 시켜 앱 상태 변화를 확인해 보자.
dependencies:
get: ^4.6.5
shared_preferences: ^2.0.17
상태 변경에 대해서 저장된 로컬 저장소에서 상태를 가져와 화면에 보여주는 부분의 코드이다.
GetBuilder<LifeCycleGetx>(
init: LifeCycleGetx()..started(),
builder: (state) {
return Scaffold(
appBar: appBar(title: "Life Cycle With Get X"),
body:
lifeCycleUIListView(data: state.lifeCycle, context: context));
});
ListView lifeCycleUIListView({
required List<String> data,
required BuildContext context,
}) {
return ListView(
children: [
const SizedBox(height: 12),
...data.map(
(e) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.2,
child: Text(
e.split("/")[0],
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: e.split("/")[0] == "inactive"
? Colors.amber
: e.split("/")[0] == "stop"
? Colors.amber.shade200
: e.split("/")[0] == "detached"
? Colors.deepOrange
: e.split("/")[0] == "restart"
? Colors.blue.shade200
: e.split("/")[0] == "resumed"
? Colors.blue
: Colors.green,
),
)),
const SizedBox(width: 12),
Text(
e.split("/")[1],
style: const TextStyle(
color: Color.fromRGBO(195, 195, 195, 1),
),
),
],
),
),
)
],
);
}
GetxController 생성시 StateFul과 동일하게 WidgetsBindingObserver 객체를 상속받자.
class LifeCycleGetx extends GetxController with WidgetsBindingObserver {
List<String> lifeCycle = [];
...
}
StateFul의 initState와 동일한 기능을 가지고 있다. WidgetsBindingObserver를 생성하자.
void onInit() {
super.onInit();
WidgetsBinding.instance.addObserver(this);
}
GetxController가 dispose될 때 WidgetsBindingObserver를 지워 주는 코드이다.
setLocalStorage 함수는 아래에서 살펴도록 하겠다.
void onClose() {
super.onClose();
_setLocalStorage("Detached");
WidgetsBinding.instance.removeObserver(this);
}
GetxController에서도 앱의 상태를 확인할 수 있는 기능이 StateFul과 동일하게 제공된다.
didChangeAppLifecycleState에서 앱 상태를 체크하여 상태 값과 시간을 저장해두는 setLocalStorage 함수를 호출하고 앱 진입시 상태인 resumed에서는 저장해 둔 로컬 저장소 데이터를 가져와 화면에 보여주기 위한 기능인 getLocalStorage 함수를 호출하자.
void didChangeAppLifecycleState(AppLifecycleState state) async {
switch (state) {
case AppLifecycleState.detached:
_setLocalStorage("Detached");
break;
case AppLifecycleState.paused:
_setLocalStorage("Paused");
break;
case AppLifecycleState.inactive:
_setLocalStorage("Inactive");
break;
case AppLifecycleState.resumed:
_setLocalStorage("Resumed");
_getLocalStorage();
break;
default:
}
}
Future<void> _getLocalStorage() async {
SharedPreferences _pref = await SharedPreferences.getInstance();
List<String> _list = _pref.getStringList(_lifeCycleKey) ?? [];
lifeCycle = _list;
update();
}
Future<void> _setLocalStorage(String value) async {
String _saveData = "$value/${DateTime.now().toString().substring(0, 19)}";
SharedPreferences _pref = await SharedPreferences.getInstance();
List<String> _list = _pref.getStringList(_lifeCycleKey) ?? [];
_list.add(_saveData);
_pref.setStringList(_lifeCycleKey, _list);
}
이제 결과를 확인하기 위해 앱을 백그라운드에 두었다가 앱을 진입해 보자. 이전 글에서 사용한 StateFul과 동일한 결과가 나온다. 그럼 GetxController에서는 detached 상태가 호출이 될까 ?
결과는 역시 동일하게 수신되지 않고 있다. StateFul에서 확인해본 것과 동일하게 뷰를 파괴시켜 detached 상태에서 로그를 출력해보자.
로그는 호출되지만 로컬 저장소에 저장은 되지 않고 있다.
exit(0); // IOS
SystemNavigator.pop(); // Android
https://github.com/boglbbogl/flutter_velog_sample/blob/main/lib/life_cycle/life_cycle_getx.dart
StateFul, GetX 두 개의 방법을 사용하여 Flutter에서 앱 상태를 수신하는 방법에 대해서 살펴봤다. 상태 수신에는 문제가 없지만 앱 종료 상태인 detached 상태가 호출되지 않는다는 것을 확인하였다.
이 문제를 해결하기 위해 많은 방법을 고민하고 시도해 봤지만 Flutter에서는 도저히 해결이 되지않는다.
앱 사용자가 버튼을 눌러 앱을 종료 시키지도 않을 뿐더러, 디바이스를 강제 종료 시키거나 배터리가 없어서 꺼지게 되더라도 앱 종료를 수신받을 수 없다.
앱 종료 상태에서 API 호출을 하는 기능이 필요 하였기에, API 호출도 해봤지만 정상적인 작동이 되지 않았다.
다음 글에서는 네이티브에서 앱 상태는 Flutter와 어떻게 다르게 작동되고, Flutter에서 수신하고 수행하지 못하는 앱 종료 상태의 API 호출을 네이티브에서는 가능할지에 대해서 살펴보려고 한다.