이전 포스트(https://uaremine.tistory.com/35)에서 M -> A1 -> A2 이동 시에 발생하는 문제를 확인했다.
처음에 이 상황에서 문제가 발생하는 것을 알았을 때, onClose() -> onInit() 가 아니라 onInit() -> onClose() 순서로 호출되는 것이 문제라고 생각해서 가장 먼저 시도해 본 것은 toNamed(), offNamed()가 아닌 다른 method들을 이용해보는 것이었다.
설명만 보면 Get.offAndToNamed() 를 이용하면 될 것 같았다.
하지만, offAndToNamed()를 호출하면 아예 A2 Controller의 onInit()이 호출되지도 않는다.
[GETX] CLOSE TO ROUTE /ArticlePage/1
[GETX] GOING TO ROUTE /ArticlePage/2
I/flutter ( 9610): ### ArticlePageController 1 onClose()
[GETX] "ArticlePageController" onDelete() called
[GETX] "ArticlePageController" deleted from memory
(A1 -> A2 를 Get.offAndToNamed('/ArticlePage/2')로 이동한 경우의 로그)
이것은 분명 페이지 전환 과정에서 Controller 를 폐기하고 새로 생성할 때 기존 페이지의 Controller 와 새 페이지의 Controller를 제대로 구분하지 못해서라는 생각이 들었다.
이럴 때를 위해서 tag 이 있는 것이 아닌가?
class ArticlePage extends StatelessWidget {
const ArticlePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
ArticlePageController controller =
Get.put(ArticlePageController(), tag: Get.parameters['id']); // article id 를 unique한 tag으로 사용했다.
return Scaffold(
appBar: AppBar(),
body: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text('This is ArticlePage ${controller.articleId}'),
Obx(() => Text(
'This Page is ' + (controller.isAlive.value ? 'alive' : 'dead'))),
TextButton(
onPressed: () =>
Get.offNamed('/ArticlePage/${controller.targetId}'),
child: Text('Move To ArticlePage ${controller.targetId}')),
]));
}
}
Get.put(ArticlePageController())할 때, unique한 tag(여기서는 article id)을 넣어줬더니 어머! 이제 기존 페이지의 Controller와 새 페이지의 Controller를 잘 구분해서 init, close 한다.
[GETX] Instance "GetMaterialController" has been created
[GETX] Instance "GetMaterialController" has been initialized
[GETX] GOING TO ROUTE /ArticlePage/1
I/flutter ( 9610): ### ArticlePageController 1 onInit()
[GETX] Instance "ArticlePageController" has been created with tag "1"
[GETX] Instance "ArticlePageController" with tag "1" has been initialized
[GETX] REPLACE ROUTE /ArticlePage/1
[GETX] NEW ROUTE /ArticlePage/2
I/flutter ( 9610): ### ArticlePageController 2 onInit()
[GETX] Instance "ArticlePageController" has been created with tag "2"
[GETX] Instance "ArticlePageController" with tag "2" has been initialized
I/flutter ( 9610): ### ArticlePageController 1 onClose()
[GETX] "ArticlePageController1" onDelete() called
[GETX] "ArticlePageController1" deleted from memory
[GETX] REPLACE ROUTE /ArticlePage/2
[GETX] NEW ROUTE /ArticlePage/1
I/flutter ( 9610): ### ArticlePageController 1 onInit()
[GETX] Instance "ArticlePageController" has been created with tag "1"
[GETX] Instance "ArticlePageController" with tag "1" has been initialized
I/flutter ( 9610): ### ArticlePageController 2 onClose()
[GETX] "ArticlePageController2" onDelete() called
[GETX] "ArticlePageController2" deleted from memory
A1 -> A2 이동 시(파란 색)에는 AC2 생성하고 AC1 삭제, A2 -> A1 이동 시(주황색)에는 AC1 생성하고 AC2 삭제가 의도한 대로 잘 된다.
tag을 이용하기 싫다면?
좀 더 복잡한 방법을 쓸 수 있다.
GetX 문서를 보면 Get.put() 으로 Dependency Injection한 경우에 삽입된 객체는 어디서나 access 할 수 있다. 즉, 전역 객체가 된다. (물론 삭제되기 전 까지)
그렇다면 ArticlePageController의 객체를 전역으로 만들지 않으면 해결되지 않겠는가?
안타깝게도 Get.put()에는 그런 옵션이 없지만, GetBuilder() 에는 global parameter가 있다.
class ArticlePage extends StatelessWidget {
const ArticlePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetBuilder<ArticlePageController>(
init: ArticlePageController(),
global: false,
dispose: (state) => state.controller?.onClose(),
builder: (controller) {
return Scaffold(
appBar: AppBar(),
body:
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text('This is ArticlePage ${controller.articleId}'),
Obx(() => Text('This Page is ' +
(controller.isAlive.value ? 'alive' : 'dead'))),
TextButton(
onPressed: () =>
Get.offNamed('/ArticlePage/${controller.targetId}'),
child: Text('Move To ArticlePage ${controller.targetId}')),
]));
},
);
}
}
// ArticlePageController는 동일
[GETX] Instance "GetMaterialController" has been created
[GETX] Instance "GetMaterialController" has been initialized
[GETX] GOING TO ROUTE /ArticlePage/1
I/flutter ( 9610): ### ArticlePageController 1 onInit()
[GETX] REPLACE ROUTE /ArticlePage/1
[GETX] NEW ROUTE /ArticlePage/2
I/flutter ( 9610): ### ArticlePageController 2 onInit()
I/flutter ( 9610): ### ArticlePageController 1 onClose()
[GETX] REPLACE ROUTE /ArticlePage/2
[GETX] NEW ROUTE /ArticlePage/1
I/flutter ( 9610): ### ArticlePageController 1 onInit()
I/flutter ( 9610): ### ArticlePageController 2 onClose()
Controller 인스턴스가 생성, 초기화, 정리, 삭제되었다는 GETX 로그가 빠지기는 했지만, 컨트롤러의 onInit(), onClose() 는 정상적으로 동작한다.
코드는 tag을 이용하는 쪽이 좀 더 간결해서 알아보기 편하지만, unique 한 tag을 지정하기 곤란한 경우에는 GetBuilder()를 사용하는 쪽도 무방하겠다. 참고로 나는 GetBuilder()를 사용했다.
끝.