Provider를 사용해서 첫 프로젝트를 잘 마쳤지만! 별 문제는 없었지만! 그럼에도 불구하고 GetX를 써본 사람들이 극찬하는 이유가 있겠지..? 하고 호기심이 생겼다. 다음번 앱은 GetX로 만들고 싶어서 공부 중에 하는 정리 📚
기존 Provider 사용법과 거의 유사한 방법...! ChangeNotifier의 역할을 GetxController가, Consumer의 역할을 GetBuilder가 거의 동일하게 구현함!
대신 사용할 때 context가 필요하지 않아서 stateless widget 안에서도 따로 위로 빼서 사용할 수 있다. 그리고 아래에 있는 Reactive 방식과 비교하면 제공되는 기능은 단순하지만, 리소스가 적다!
예제로 컨트롤러를 이용해서 토끼한테 당근을 줬다 뺏는.. 쓸데없는 화면을 구현해보았다.
토끼랑 당근 사이에 SizedBox를 놓고 width를 state값으로 만든 다음에, getX로 관리하는 게 끝..! 만약 토끼와 당근 사이의 거리가 0이면 거리를 그만 줄이고, 하트가 나타났다 사라지도록 했다.
GetxController
를 상속한 클래스를 만들고, 필요한 변수와 메서드를 작성해준다.
import 'package:get/get.dart';
class DistanceController extends GetxController {
double _distance = 100;
double get distance => _distance;
bool _heart = false;
bool get heart => _heart;
void getClose() {
if (_distance == 0) {
_heart = true;
update();
Future.delayed(const Duration(milliseconds: 500), () {
_heart = false;
update();
});
return;
}
//⬆️ 거리가 0이면 distance를 감소시키는 대신 heart를 true로 만들고, 0.5초 후에 다시 false가 되도록 하는 코드
_distance -= 20;
update();
}
void getFar() {
_distance += 20;
update();
}
}
state의 상태변화를 알리기 위해 update()를 사용한다. (privider의 notifierListener와 같은 역할!)
여러 위젯에서 쓰이는 컨트롤러의 경우 상위 위젯에서 put
으로 먼저 등록하고,
Get.put(DistanceController());
사용되는 위젯에서 find
로 변수에 담아준 뒤 쓰면 된다.
final controller = Get.find<DistanceController>();
만약 하나의 위젯에서만 사용된다면 그냥 해당 위젯 내부에 put
으로 바로 선언해서 써도 문제 없었다!
final controller = Get.put(DistanceController());
provider의 ChangeNotifierProvider 세팅에 비하면 아 쥬 간 단~~~!
Stack(
alignment: Alignment.topCenter,
children: <Widget>[
//⬇️ heart 사용 부분
GetBuilder<DistanceController>(
builder: (_) => AnimatedOpacity(
duration: const Duration(milliseconds: 500),
opacity: controller.heart ? 1 : 0,
child: const Text(
'💕',
style: TextStyle(fontSize: 24),
))),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
const Text('🐰'),
//⬇️ distance 사용 부분
GetBuilder<DistanceController>(
builder: (_) => AnimatedSize(
duration: const Duration(milliseconds: 500),
child: SizedBox(width: controller.distance))),
const Text('🥕')
]),
],
),
변수가 사용되는 위젯을 GetBuilder
로 감싸고, controller를 전달해서 변수를 꺼내쓰는 방식! builder에서 전달한 controller(여기선 '_')를 사용해도 되고, 앞에서 put 혹은 find로 선언한 controller를 써도 똑같이 작동을 하는데... 성능상 무슨 차이가 있는진 잘 모르겠움ㅎㅎ;;(무책임)
AnimatiedOpacity와 AnimatedSize는.. 부드러운 효과를 주려고 사용한 것!
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
controller.getClose();
},
child: const Text('주기')),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () {
controller.getFar();
},
child: const Text('뺏기')),
],
)
controller에서 그대로 메서드를 꺼내 쓰면 된당. 쏘 이지~~~
이번엔 반응형 상태 관리!
먼저 첫번째 방식과의 차이점이 뭐냐! 하면
ever
, once
, debounce
, interval
등 Workers를 등록해서 사용할 수 있다그럼 사용법을 알아보자~~~
제일 명확하게 다른 부분!
먼저 GetxController를 상속받지 않아도 되고, 상태값이 바뀔 때 update()도 호출하지 않는다.
대신 타입 선언 시 그냥 타입이 아니라 Rx를 붙인 타입(RxInt
, RxString
...)으로 선언하고, 초깃값 뒤에 .obs
를 붙여주어야 한다. 또 선언 후 해당 값을 이용할 때는 변수명.value
의 형태로 접근해야 한다.
class ReactiveDistanceController {
RxDouble _distance = 100.0.obs;
RxDouble get distance => _distance;
RxBool _heart = false.obs;
RxBool get heart => _heart;
void getClose() {
if (_distance.value == 0) {
_heart.value = true;
Future.delayed(const Duration(milliseconds: 500), () {
_heart.value = false;
});
return;
}
_distance.value -= 20;
}
void getFar() {
_distance.value += 20;
}
}
Get.put(ReactiveDistanceController());
final controller = Get.find<ReactiveDistanceController>();
Simple 방식과 똑 같 음!
Stack(
alignment: Alignment.topCenter,
children: <Widget>[
Obx(() =>
AnimatedOpacity(
duration: const Duration(milliseconds: 500),
opacity: controller.heart.value ? 1 : 0,
child: const Text('💕', style: TextStyle(fontSize: 24),))),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
const Text('🐰'),
Obx(() => AnimatedSize(
duration: const Duration(milliseconds: 500),
child: SizedBox(
width: controller
.distance
.value))),
const Text('🥕')
]),
],
),
GetBuilder 대신 Obx
로 위젯을 감싸서 사용하면 되고, 이때 자식 함수는 따로 컨트롤러를 전달하지 않는다. GetBuilder와 거의 똑같이 쓸 수 있는 GetX
방식도 있움!
그리구 앞서 언급했듯 Rx변수 뒤에 .value
를 붙여서 값을 사용해야 한다.
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
controller.getClose();
},
child: const Text('주기')),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () {
controller.getFar();
},
child: const Text('뺏기')),
],
)
이것도 첫 번째 방식과 차이가 없다>.<
결론: simple 방식은 사용 메모리가 적고, reactive 방식은 state값 변화에 따라 부가적인 기능을 제공한다. 프로젝트 규모나 상황에 따라 적절한 방식으로 쓰면 될 듯~~~!
[ Flutter / 플러터 ] GetX 주요 기능 3가지 중 그 두번째 상태관리 #3
[Flutter] GetX 정리 - Terry’s Dev-Diary