
우선 상태 관리, 혹은 State Management는 앱 프로세스 내에서 그 상태가 변화하는 것을 관리하는데 앱이 실행되고 있는 동안 동적으로 계속해서 변화하는 데이터를 의미.
- User Preference: main theme이나 language 등의 설정
- 세션 정보: 유저가 로그인했을 때의 세션 정보 등
- 기타 다양한 변화하는 정보들
이러한 상태를 모아놓고 관리하는 매니저가 필요한데 이것이 상태 관리자이다.
React에서 대표적인 상태관리 라이브러리가 Redux가 있다면 Flutter에서는 GetX를 주로 사용.
get: ^4.7.2
GetX에서는 다음과 같은 기능을 제공한다.
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => NextPage(),
));
이것이 기본 코드라면 GetX를 사용하면,
Get.to(NextPage());
이런 식으로 처리가 가능함
Get.put()와 Get.lazyPut() 메서드를 사용하여 객체를 싱글톤 형태로 관리하거나 필요할 때 인스턴스 생성
void main() {
// MyController를 의존성으로 등록
Get.put(MyController());
runApp(MyApp());
}
void main() {
// MyController는 필요할 때만 생성됩니다.
Get.lazyPut(() => MyController());
runApp(MyApp());
}
MyController myController = Get.find();
observable 객체가 들어가 있지 않아서 Reactive State Management 방식보다 훨씬 가볍고, 간단.
단점으로 상태 변화를 감지하기 위해 매번 update() 메서드를 호출해야 한다는 단점이 존재.
자주 변화하지 않는 상태를 관리하는데 적합.
class Controller extends GetxController {
int _count = 0;
int get count => _count;
void increment() {
_count++;
}
}
Controller는 GetXController를 상속한다.(GetX의 상태 관리를 위해 무조건 상속)
그리고 count라는 상태를 저장하며 increment 메서드에서 count를 증가 시킨다.
위 코드에서 작성한 Controller를 사용하기 위해서는 위의 의존성 주입이 필요하다.
final Controller controller = Get.put(Controller());
Get.put을 사용하여 Controller를 주입한다.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({super.key});
final controller = Get.put(Controller());
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
GetBuilder<Controller>(
builder: (_) => Text(
'Current Counter : ${controller.count}',
style: const TextStyle(fontSize: 20),
)),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () => controller.increment(),
child: const Text(
'Increase',
style: TextStyle(fontSize: 24),
))
],
)),
));
}
}
위의 예제 코드에서 GetBuilder를 사용하여 Text 위젯을 불러와 초기화한 controller 인스턴스의 count 변수를 Text 위젯으로 출력했다.
또한 ElevatedButton 버튼으로 controller의 increment() 메서드를 호출하였다.
하지만 버튼을 눌러도 숫자가 증가하지 않는데
void increment() {
_count++;
update();
}
Simple State Management 설명에서 말했던 상태 변화를 감지하기 위한 update() 메서드를 호출해야한다.
별도로 Controller 인스턴스를 초기화 해주지 않고 GetBuilder 내부에서 초기화도 가능하다.
final controller = Get.put(Controller());
위에서 작성한 이 부분을 생략하고
GetBuilder<Controller>(
init: Controller(),
builder: (_) => Text(
'Current Counter : ${Get.find<Controller>().count}',
style: const TextStyle(fontSize: 20),
)),
GetBuilder에서 init 요소를 Controller()로 초기화를 해주고 나면, Get.find() 메소드를 통해 인스턴스를 불러올 수 있다. 이때 find의 템플릿(제네릭)은 Controller로 지정해주면 된다.
ElevatedButton(
onPressed: () => Get.find<Controller>().increment(),
child: const Text(
'Increase',
style: TextStyle(fontSize: 24),
))
버튼 역시 마찬가지로 수정한다.
GetBuilder 구조가 필요 없이 controller를 put 또는 find로 찾아서 해당하는 상태에 값을 알아서 변경해주는 방식
상태를 찾게 해주기 위해 상태를 변경하고자 하는 UI를 Obx(()=>widget) 형태로 감싸주면 controller가 알아서 해당되는 상태를 찾아가게 해준다.
우선 Person 모델 생성
class Person {
Person({this.age = 0, this.name = ''});
int age;
String name;
}
Controller 역시 생성
class Controller extends GetxController {
final person = Person().obs;
void updateInfo() {
//val = 콜백 인스턴스
person.update((val) {
val?.age++;
val?.name = 'nx006';
});
}
}
위의 Controller에서 Person().obs는 observable의 약자로 오브젝트 변화를 감지하겠다는 의미
obs가 붙은 객체는 update 메서드 사용 (Simple State Management 메서드의 update와 의미 다름.)
update 메소드는 내부적으로 콜백 인스턴스를 받는데, 이 콜백 인스턴스을 감시하고 있는 Person 오브젝트가 들어온다. 이때 val에 null 값이 들어올 수 있으므로 val? 형태로 null 체크를 해주어야 한다. 그리고 이 인스턴스의 값을 변경해주면, GetX가 update 메소드를 통한 변화를 감지하고 화면을 리빌드시킴
이해하기 쉽게 HomePage -> FirstPage -> SecondPage라고 가정해보면
Get.to(() => const SecondPage());
Get.off(() => const SecondPage());
Get.offAll(() => const SecondPage());
Get.toNamed('/second');
Get.back();
Get.snackbar(
'title',
'message',
snackPosition: SnackPosition.TOP,
colorText: Colors.white,
backgroundColor: Colors.black,
borderColor: Colors.white);
Get.defaultDialog(
radius: 10.0,
contentPadding: const EdgeInsets.all(20.0),
title: 'title',
middleText: 'content',
textConfirm: 'Okay',
confirm: OutlinedButton.icon(
onPressed: () => Get.back(),
icon: const Icon(
Icons.check,
color: Colors.blue, ),
label: const Text('Okay',
style: TextStyle(color: Colors.blue),
), ),
cancel: OutlinedButton.icon(
onPressed: (){},
icon: Icon(),
label: Text(),),);