GetX는 프로젝트를 진행해보면서 가장 유용하게 쓰인 상태관리 방법이다. Flutter에서 항상 강조하는 것이 UI와 Busniess Logic을 분리하자 라는 것인데 GetX를 통해 거의 완전에 가까운 분리가 가능하였다. Flutter 에는 상태 관리를 도와주는 Provider, block, GetX 등 여러 가지가 도구가 있다. Flutter에서 GetX를 사용하게 되면, 상태 관리뿐만 아니라, Route, 다국어 지원, 화면 크기 가져오기, API 호출 기능 등 다양한 기능을 사용 가능하다.
Getx는 ?
- Flutter를 개발 하기 위한 매우 가볍고 강력한 라이브러리
3가지 기본 원칙
- 생산성
- 성능
- 조직화
3가지 주요 기능
- 상태관리 : binding을 이용해 business logic과 view를 분리할 수 있다.
- 라우팅 : context 없이 routing 이 관리된다(엄청 편하다). ex) Get.to(SomePage())
- 종속성 관리 : 라우트에서 종속성 주입, 화면 이동시 종속성 주입, permanent와 GetxService 등등
< 라우팅 관리 종류 >
화면 이동 방법
화면을 이동시킬 때, 새로운 화면을 이전 화면 위에 계속 추가하는 방법도 있지만, 이전 화면을 제거하고 새로운 화면을 추가하거나, 전전 화면으로 이동한 후, 새로운 화면을 추가하는 등 다양항 방법으로 화면 이동을 구현해야 할 경우가 있습니다.
GetX에서도 다음과 같은 기능을 사용하여 이를 구현할 수 있습니다.
Get.until
- 주어진 조건을 만족하는 화면까지 화면을 제거합니다.
- Navigation.popUntil()와 같은 기능을 합니다.
- Get.until((route) => Get.currentRoute == '/home')와 같이 사용합니다.
Get.off
- 현재 화면을 제거하고, 새로운 화면을 추가합니다.
- Navigation.pushReplacement()와 같은 기능을 합니다.
- Get.off(Second())와 같이 사용합니다.
Get.offNamed
- 이름있는 라우트를 사용하여 현재 화면을 제거하고 새로운 화면을 추가합니다.
- Navigation.pushReplacementNamed()와 같은 기능을 합니다.
- Get.offNamed('/second')와 같이 사용합니다.
Get.offAndToNamed
- 이름있는 라우트를 사용하여 새로운 화면을 추가한 후, 이전 화면을 제거합니다.
- Navigation.popAndPushNamed()와 같은 기능을 합니다.
- Get.offAndToNamed('/second')와 같이 사용합니다.
Get.offUntil
- 주어진 조건을 만족하는 화면까지 화면을 제거한 후, 새로운 화면을 추가합니다.
- Navigation.pushAndRemoveUntil()와 같은 기능을 합니다.
- Get.offUntil(page, (route) => (route as GetPageRoute).routeName == '/home')와 같이 사용합니다.
Get.offNamedUntil
- 이름있는 라우트를 사용하여 주어진 조건을 만족하는 화면까지 화면을 제거한 후, 새로운 화면을 추가합니다.
- Navigation.pushNamedAndRemoveUntil()와 같은 기능을 합니다.
- Get.offNamedUntil(page, ModalRoute.withName('/home'))와 같이 사용합니다.
Get.removeRoute
- 주어진 조건을 만족하는 화면을 제거합니다.
- Navigation.removeRoute()와 같은 기능을 합니다.
- Get.removeRoute(ModalRoute.withName('/home'))와 같이 사용합니다.
Get.offAll
- 모든 화면을 제거한 후, 새로운 화면을 추가합니다.
- Navigation.pushNamedAndRemoveUntil()와 같은 기능을 합니다.
- Get.offAll(Second())와 같이 사용합니다.
Get.offAllNamed
- 이름 있는 라우트를 사용하여 모든 화면을 제거한 후, 새로운 화면을 추가합니다.
- Navigation.pushNamedAndRemoveUntil()와 같은 기능을 합니다.
- Get.offAllNamed('/second')와 같이 사용합니다.
실습을 해보며 3가지 가장 큰 기능을 알아 볼 것이다.
이번에 실습 한 앱은 간단한 드랍다운 이벤트 처리를 하는 Busniess Logic을 처리하는 앱이다.
GetX 설정
Flutter에서 GetX를 사용하기 위해서는 MaterialApp 대신 GetMaterialApp을 사용할 필요가 있습니다. 이를 확인하기 위해 lib/main.dart 파일을 열고 다음과 같이 수정합니다.
- Main.dart
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
initialBinding: BindingsBuilder((){
Get.put(DropdwonButtonController());
}),
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: App()
);
}
}
지금까지 예제를 보면 GetX의 상태 값을 사용하기 위해서는 Get.put을 이용하여 컨트롤러를 생성하여 사용 해준다.
상태 관리
GetX는 다음과 같이 두가지 상태 관리 방법을 제공합니다.
- 단순 상태 관리(Simple state management)
- 반응형 상태 관리(Responsive state management)
단순 상태 관리
GetX를 사용하여 단순 상태 관리와 반응형 상태 관리의 차이는 Controller에서 상태 변화가 이루어지는 부분의 Rx를 사용하냐 안하냐 차이이다.
enum DropDownMenu { DEFAULT, MENU1, MENU2}
enum 형식의 DropDownMenu가 주어진다면 아래와 같이 나눌 수 있다.
단순 상태 관리
DropDownMenu cnt = DropDownMenu.DEFAULT;
반응형 상태 관리
Rx<DropDownMenu> currentItem = DropDownMenu.DEFAULT.obs;
반응형 상태 관리는 함수를 통해 상태를 직접 통보해야 하는 단순 상태 관리와는 달리 내부 로직으로 값의 상태 변화를 감지하고 화면에 변경된 값을 적용합니다. observer 디자인 패턴으로 이해하면 된다.
여기서 볼 때 int 타입이 RxInt로 변경되며 0 값 옆에 obs라는 것이 붙게 되는데 이것이 반응형 상태 관리를 위한 변수 선언 방법입니다.
변수를 "observable"하게 만드는 방법에는 3가지로 할 수 있습니다.
첫 번째 타입 선언 방법 Rx{Type}
// 초기값을 설정하는 것을 추천하지만, 필수는 아닙니다.
final name = RxString('');
final isLogged = RxBool(false);
final count = RxInt(0);
final balance = RxDouble(0.0);
final items = RxList<String>([]);
final myMap = RxMap<String, int>({});
두 번째 타입: Rx<Type>
final name = Rx<String>('');
final isLogged = Rx<Bool>(false);
final count = Rx<Int>(0);
final balance = Rx<Double>(0.0);
final number = Rx<Num>(0);
final items = Rx<List<String>>([]);
final myMap = Rx<Map<String, int>>({});
// 커스텀 클래스 - 그 어떤 클래스도 가능합니다
final user = Rx<User>();
세 번째 방법: 단순히 **.obs**를 value의 속성으로 덧붙이는 방법
final name = ''.obs;
final isLogged = false.obs;
final count = 0.obs;
final balance = 0.0.obs;
final number = 0.obs;
final items = <String>[].obs;
final myMap = <String, int>{}.obs;
// 커스텀 클래스 - 그 어떤 클래스도 가능합니다
final user = User().obs;
- DropdownButtonController.dart
enum DropDownMenu { DEFAULT, MENU1, MENU2}
extension DropDownMenuExtention on DropDownMenu{
String get name{
switch(this){
case DropDownMenu.DEFAULT:
return "기본메뉴";
case DropDownMenu.MENU1:
return "Menu 1";
case DropDownMenu.MENU2:
return "Menu 2";
}
}
}
class DropdwonButtonController extends GetxController {
Rx<DropDownMenu> currentItem = DropDownMenu.DEFAULT.obs;
void changeDropDownMenu(int? itemIndex){
var selectedItem = DropDownMenu.values.where((menu) => menu.index == itemIndex).first; // 매칭이 되는 값을 찾아 낼 수 있다.
currentItem(selectedItem);
}
}
구현할 메뉴는 총 3가지 DEFAULT, MENU1, MENU2를 itemIndex값을 받아 Rx로 선언된 currentItem에 상황에 맞는 obs를 넣어준다. 그럼 상태 변화를 감지하고 화면에 변경된 값이 적용이 된다.
- DropdownButtonWidget
class DropdownButtonWidget extends GetView<DropdwonButtonController> {
const DropdownButtonWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Obx(() => DropdownButton(
value: controller.currentItem.value.index,
onChanged: (int? value) {
controller.changeDropDownMenu(value);
},
items: DropDownMenu.values
.map(
(menu) => DropdownMenuItem(
value: menu.index,
child: Text(menu.name),
),
)
.toList(),
));
}
}
그런 다음 위에서 구성한 Controller를 반영하기 위해 ButtonWidget을 따로 구현하여 Obx를 통한 DropDownButton을 선언해준다. 그러면 value를 위에서선언한 Rx<> 형식의 데이터를 가져올 수 있고 그 value에 대한 index를 DropDownButton의 value에 넣어주면 onchanged() 이벤트가 작동하여 value값이 controller에 changeDropDownMenu 함수를 싱행시킨다.
여기서 controller를 바로 사용해줄 수 있는 이유는 extends GetView<DropdownButtonController>를 이용하여 바로 View를 가져왔기 때문이다. 어렵게 따로 선언 하지 않고 controller로 바로 사용할 수 있는 꿀팁(?)이다.
< 마무리>
1. DeafaultPage.dart
import 'package:flutter/material.dart';
class DefaultPage extends StatelessWidget {
const DefaultPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.green[300],
child: Center(child: Text("기본페이지"))
);
}
}
2. Page1.dart
import 'package:flutter/material.dart';
class Page1 extends StatelessWidget {
const Page1({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.red[300],
child: Center(child: Text("Page 1"))
);
}
}
3. Page2.dart
import 'package:flutter/material.dart';
class Page2 extends StatelessWidget {
const Page2({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.blue[300],
child: Center(child: Text("Page 2"))
);
}
}
3개의 stateless한 page를 만들어 주고 아래와 같이 구현해 주면 된다.
- App.dart
class App extends GetView<DropdwonButtonController> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("DropDwon Sample"),
),
body: Column(
children: [
DropdownButtonWidget(),
Expanded(child: Obx((){
switch(controller.currentItem.value){
case DropDownMenu.DEFAULT:
return DefaultPage();
case DropDownMenu.MENU1:
return Page1();
case DropDownMenu.MENU2:
return Page2();
}
}
))
],
),
);
}
}
DropdownButtonWidget()은 Rx형식으로 구현한 observalble한 데이터를 불러오는 위젯이기 때문에 위에서 따로 구현하였고, App()클래서에서 또한 switch()문을 통한 value값의 현재상태를 obx로 observe하여 상황에 맞는 case로 return 값을 구현을 해준다.
정리 : GetX를 다시 공부해보니 Routing 기법이 기존 navigator보다 너무 쉽고 빠르게 처리가 가능하단 사실에 놀랐고 가벼운 성능으로 provider나 bloc보다 더욱 유용하게 사용할 수 있을 것 같다. 이것으로 GetX를 사용하여 Flutter에서 라우트를 관리, 종속성 관리, 상태관리 하는 방법에 대해서 알아보다. 또한 화면 이동을 위한 다양한 방법과, 파라메터를 전달하는 방법 등 라우트를 사용하는 다양항 방법에 대해서도 알아보았다.
'플러터' 카테고리의 다른 글
Flutter - provider패턴 아키텍처에 대한 이해 (0) | 2022.05.13 |
---|---|
Flutter - Bloc패턴 아키텍처에 대한 이해 (0) | 2022.05.10 |
Flutter - Firebase연동 채팅 앱 (0) | 2022.03.11 |
Flutter - Link_Help 앱 개발 (0) | 2022.03.11 |
Flutter - 인스타그램 이미지 픽 앱 (0) | 2022.03.07 |