Don't think! Just do it!

종합 IT 기술 정체성 카오스 블로그! 이... 이곳은 어디지?

Flutter/Flutter Study

[Flutter] getx page&controller 재사용

방피터 2022. 11. 2. 20:00

get으로 Page랑 controller를 만들고 이걸 여러 군데에서 재사용해야 할 때가 있어.

뭐 Page(Widget)야 그냥 쓰면 되는데 GetxController의 instance가 독립적으로 생성되게 하기 위해서는 binding할 때 tag 옵션을 줘야해. 예를 만들어 보자구.

먼저 3개의 파일을 만들었어. page_text.dart, page_text_controller.dart 마지막으로 page_text_binding.dart

get으로 page 기본 구성이라고 할 수 있어.

get page 기본 구성

내용은 아래와 같고, 

//page_test.dart

class PageTest extends GetView<PageTestController> {//Getview로 선언하면
  const PageTest({super.key});
  static const routeName = '/ScreenTest';

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Obx(() => Text(controller.text)),// Get.find()없이 controller에 접근 가능!
          ElevatedButton(
              onPressed: () => {controller.text = "Changed!!"},
              child: Text("Change the Text!"))
        ],
      ),
    );
  }
}
//page_test_controller.dart

import 'package:get/get.dart';

class PageTestController extends GetxController {
  var _text = "Page Test Text".obs;
  String get text => _text.value;
  set text(String value) => _text.value = value;
}
//page_test_binding.dart

class PageTestBinding extends Bindings {
  @override
  void dependencies() {
    // TODO: implement dependencies
    Get.lazyPut(() => PageTestController());//lazyPut => 필요할 때 로딩
  }
}

이걸 만약에 navigator에서 GetRoutePage와 함께 사용한다면 다음과 같이 되겠지.

return Navigator(
  key: Get.nestedKey(NavKeys.home.index),
  initialRoute: ScreenHome.routeName,
  onGenerateRoute: ((settings) {
  ...
    return GetPageRoute(
      page: () => PageTest(),
      binding: PageTestBinding(),
    );
  ...
}

단순한 get page와 controller

여기까지는 "원래 이렇게 동작하는 거"니까 그렇다치고 이제 test page를 다른 tab에도 넣어서 테스트 해보면!👇👇

다른 탭의 state가 함께 변한다.

controller를 공유해서 다른 탭의 state가 함께 변해.. 당연하겠지만 이건 우리가 원하는 결과가 아니지 ㅎ 이제 tag를 사용해서 이문제를 해결해보도록 하자고. 먼저 binding부터 살펴보면 👇

//page_test_binding.dart

class PageTestBinding extends Bindings {
  PageTestBinding({this.controllerTag});
  final String? controllerTag;
  @override
  void dependencies() {
    // TODO: implement dependencies
    Get.lazyPut(
      () => PageTestController(),
      tag: controllerTag,//tag 옵션과 함께 dependency 주입!
    );
  }
}

Get.lazyPut에서 입력받은 controllerTag와 함께 dependency를 주입해. 그리고 Page에서도 마찬가지로 입력받은 controller를 get tag에 override해주자.

class PageTest extends GetView<PageTestController> {
  const PageTest({super.key, this.controllerTag});
  static const routeName = '/ScreenTest';
  final String? controllerTag;
  @override
  // TODO: implement tag
  String? get tag => controllerTag; // get tag override
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Obx(() => Text(controller.text)),
          ElevatedButton(
              onPressed: () => {controller.text = "Changed!!"},
              child: Text("Change the Text!"))
        ],
      ),
    );
  }
}

그리고 GetPage나 GetPageRoute할 때 tag만 넣어주면 끝!👇 물론 다른 탭에서 사용하는 Page의 tag는 다른 걸 넣어줘야겠지.

case PageTest.routeName:
  return GetPageRoute(
    page: () => PageTest(controllerTag: "홈"),
    binding: PageTestBinding(controllerTag: "홈"),
  );

이렇게 하고 테스트를 해보면 page에 따라서 controller가 개별적으로 잘 동작하는 걸 확인할 수 있어.👇

controller가 따로따로 잘 동작한다.

그 대신 binding을 사용하지 못하는 경우나 page 밖에서 controller에 접근하는 경우에는 반드시 tag를 지정해줘야 해.👇 안 그러면 controller가 주입되지 않았다며 오류가 날거야.

var controller = Get.find<PageTestController>(tag: "홈");

nested navigator가 필요한 프로젝트에서는 이런식으로 tag를 사용하는게 필수가 아닐까 싶어.

그럼 다들 화이팅하라고! 안녕!

반응형