본문 바로가기

Mobile/Flutter

[Flutter] GetX를 이용한 상태 관리

728x90
반응형

https://pub.dev/packages/get

 

get | Flutter Package

Open screens/snackbars/dialogs without context, manage states and inject dependencies easily with GetX.

pub.dev

지난 포스팅에서 라우트 관리에 대해 정리한 것에 이어 이번 포스팅에서는 상태 관리를 정리해보려 한다.

 

* 상태관리를 왜 해야할까?

플러터는 widget안에 widget으로 구성되고 그 widget은 또 widget으로 구성되는 이른바 트리 구조로 구현되어있다.

만약 최하단 위젯에서 최상단 부모 위젯으로 data를 보내면 어떨까? 또는 다른 페이지의 위젯으로 data를 보내면 어떨까? 하나의 data를 사용하기 위해서 매번 여러 페이지에서 정보를 불러온다면 앱의 규모가 커질수록 코드가 복잡해지고 여간 쉬운 일이 아니다.

그래서 우리는 상태 관리 도구들을 사용하여 코드의 로직을 분리시켜 사용할 수 있다. ex) UI, Network, DB 만 담당하는 클래스로 나눈다

 

 

 

GetX에는 크게 두 가지의 상태 관리법이 존재한다. 이를 간단한 예시인 Counter 앱을 통해 설명해보겠다.

 

1. simple 방식

첫 번째 방식은 비교적 간단한 방식이다. 이 방식은 이후에 설명할 reactive 방식보다 메모리를 적게 사용한다는 장점이 있다.

 

1) 

먼저 GetxController를 extend하는 Controller 클래스를 만든다. 초기값을 0으로 설정한 count1 변수를 선언하고, count1을 증가시킨 후 update()를 호출하는 increment()를 호출한다.

update() 함수는 변수가 증가할 때 이를 화면에 알려준다. ChangeNotifier의 notifyListeners()와 동일하게 생각하면 된다.

class BuilderController extends GetxController {
  int count = 0;

  increment() {
    count++;
    update();
  }
}

 

2)

GetBuilder를 통해 화면에 count1 변수를 보여주고, init에 Controller를 등록한다.

GetBuilder<BuilderController>(
    init: BuilderController(),
    builder: (_) {
        return Text('count : ${_.count}');
    },
)

 

3)

Get.find()를 사용하여 increment()를 호출하는 버튼을 만들어 텍스트 아래에 배치한다.

ElevatedButton(
    onPressed: () {
        Get.find<BuilderController>().increment();
    },
    child: Text('Count 업!')
),

하지만 리빌드 해보면 Get.find<Controller>()에서 에러가 발생할 것이다. Get.find<Controller>()가 Controller를 찾는 시점이 GetBuilder()의 init에서 Controller를 등록하기 이전이라서 그렇다.

 

4)

이 문제를 해결하기 위해 Get.put()을 사용한다.

build() 메소드 내부에서 Get.put()을 통해 Controller를 등록하여 이를 controller 변수에 할당한다.

@override
Widget build(BuildContext context) {
  final controller = Get.put(BuilderController());
  // ...
}

위 과정에서 Controller를 등록한 것이기 때문에 GetBuilder에서 또 등록할 필요가 없다. 따라서 init() 부분을 지운다.

GetBuilder<BuilderController>(
    //init 부분 삭제
    builder: (_) {
        return Text('count : ${_.count}');
    },
)

버튼에서 increment()를 호출할 때 Get.find() 대신 controller 변수를 사용한다.

ElevatedButton(
    onPressed: () {
        controller.increment();
    },
    child: Text('Count 업!')
),

리빌드 해보면 에러가 발생하지 않는다.

 

 

2. reactive 방식

1)

먼저 GetxController를 extend하는 Controller 클래스를 만든다.

reactive 방식에서는 observable 변수라는 특별한 변수를 사용한다. observable 변수를 Rx라고도 부른다. Rx를 선언하는 방법에는 3가지가 있다.

  1.  Value.obs
  2. Rx<Type>(Value)
  3. RxType(Value)

이 중 간단한 1번 방법을 사용하여 count1 변수를 정의했다.

 

Rx 값에 접근할 때는 일반적인 변수와 다르게 .value를 통해 접근할 수 있다. String과 int 같은 primitive type에는 .value를 사용해야 하지만, List에서는 .value가 필요없다. dart api가 리스트에서만 .value 없이 값에 접근할 수 있게 해주기 때문이다.

.value를 사용해서 count1 값을 증가시키는 increment2() 함수를 정의했다. reactive 방식에서는 update() 함수가 필요하지 않다.

class ReactiveController extends GetxController {
  var count2 = 0.obs;

  increment2() => count2.value++;
}

 

2)

simple 방식의 GetBuilder과 같은 역할을 하는 GetX를 사용해서 count1의 값을 보여주는 텍스트를 만든다.

GetX<ReactiveController>(
    init: ReactiveController(),
    builder: (_) {
        return Text('Count 2 : ${_.count2.value}');
}),

 

 

3)

Get.find()를 사용하여 increment()를 호출하는 버튼을 만들어 텍스트 아래에 배치한다.

ElevatedButton(
    onPressed: () {
        Get.find<ReactiveController>().increment2();
    },
    child: Text('COUNT 2 UP!'),
)

마찬가지로 리빌드 해보면 에러가 발생한다.

 

4)

simple 방식과 마찬가지로 build() 메소드 내부에서 Get.put()을 통해 Controller를 등록하여 이를 controller 변수에 할당한다. init() 부분을 지우고 버튼에서 increment()를 호출할 때 Get.find() 대신 controller 변수를 사용한다.

@override
Widget build(BuildContext context) {
    final controller = Get.put(ReactiveController());
    // ...
        GetX<ReactiveController>(
            // init 부분 삭제
            builder: (_) {
                return Text('Count 2 : ${_.count2.value}');
            }),
        ElevatedButton(
            onPressed: () {
              controller.increment2();
            },
            child: Text('COUNT 2 UP!'),
          )
    // ...
 }

 

5) 

GetX(2)보다 더 간단한 방법이 있다. Obx()를 사용하는 것이다. Obx()의 경우 사용할 컨트롤러의 종류를 따로 명시할 필요가 없고, 보여줄 위젯만 리턴하면 된다. 하지만 이 방법은 무조건 Get.put()을 필요로 한다.

Obx(() {
    return Text('Count 2 : ${controller.count2.value}');
}),
728x90
반응형