본문 바로가기

Flutter

[Flutter] getx를 통한 상태관리를 쉽게 알아봅니다.🔥

반응형

🔥 getx를 시작하기 전 getx에 대한 짧은 설명 

정의 : Flutter 개발을 좀 더 쉽게 해주는 강력한 라이브러리 혹은 미니 프레임워크입니다. 상태관리 뿐만 아니라 네비게이션 등의 기능을 좀 더 쉽게 이용할 수 있다고 합니다. 이를 뒷받침해주는 3가지 원칙이 있습니다. 

1. 생산성 : 같은 기능도 더 편하고 간결하게 표현 가능

2. 성능 성능과 최소한의 리소스 소비에 중점

3. 조직화 : 화면, 비지니스 로직, 종속성 주입 및 네비게이션을 완전히 분리하여 관리가능

 

GetX를 사용하게 되면 기본적으로 클린 코드를 가지게 되어 애플리케이션의 각 기능을 쉽게 찾을 수 있습니다. 

이렇게 보면 잘 와닿지 않네요. 코드를 보면서 살펴보겠습니다. 이 글은 플러터 개발자이신 @개발하는남자님 유튜브를 보며 작성했습니다.

 

✅ getx를 사용하기 위한 준비작업

1. pub.dev에서 getX를 가져와서 pubspec.yaml에 최신 버전을 추가해줍니다. 

 

2. main.dart 페이지에 Material을 GetMaterial로 수정합니다. 

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:getx_demo/src/home.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const Home(),
    );
  }
}

 

 

 

✅ getx를 사용한 라우트 관리 - General Route Management 

 

먼저 기존에 플러터에서 페이지 이동을 위해 사용하는 코드입니다. 

Navigator.of(context).push(MaterialPageRoute(
	builder: (context) => FirstPage(),
));

 

getx를 사용한 코드입니다. 매우 단순해졌습니다.

Get.to(() => FirstPage()); //페이지 이동
Get.offAll(Home()); //모든 정보를 지우고 페이지를 이동할 때 사용

✅ 라우트관리 - Named 

주로 플러터에서는 main.dart에 프로젝트 내에 존재하는 route들을 추가하여 경로를 보기쉽게 관리합니다. 

 

이번에도 기존의 코드와 비교해보겠습니다. main.dart에서 이렇게 경로를 추가해줍니다. 

routes: {
	'/': (context) => Home(),
	'/first': (context) => FirstNamedPage(),
	'/second': (context) => SecondNamedPage(),
},

 

다음은 getX를 사용해서 경로를 추가하는 방법입니다. 

 getPages: [
 	GetPage(name: '/', page: () => Home()),
 	GetPage(name: '/first', page: () => FirstNamedPage()),
    GetPage(name: '/second', page: () => SecondNamedPage()),
 ],

 

그리고 실제 페이지 이동을 구현할 화면 코드입니다. 

Get.toNamed('/second'); // 방법1. id가 second 페이지로 이동
Get.offNamed('/second'); // 방법2. 해당 페이지의 내용을 날리고 id가 second인 페이지로 이동

 

✅ 라우트 관리 - Argument 전달 

 

이번에는 페이지 이동 시 argument를 전달해야 하는 상황을 구현한 코드입니다. 

//방법1
 Get.toNamed('/next', arguments: 'Hello');

//방법2 맵 방식을 보냅니다. 
Get.toNamed('/next', arguments: {'name': '제로', 'age': '31'});

//방법3. custom하게 만든 객체도 보냅니다.
Get.toNamed('/next', arguments: User(name: '제로', age: 35));

class User {
  String name;
  int age;
  User({required this.name, required this.age});
}

 

여기서는 전달되는 argument를 화면에 출력합니다. 

// 방법1: 전달받은 argument를 변수에 담습니다. 
var arg = Get.arguments;

// 방법 2: 전달받은 argument를 바로 ui 화면에 표시합니다.
Text('${Get.arguments}'),

// 방법3: 여러 arguments가 담겨있는 경우 아래처럼 화면에 표시합니다. 
Text('이름 : ${Get.arguments['name']}  나이 : ${Get.arguments['age']}'),

//방법 3: 여러 arguments가 담겨있는 경우 아래처럼 화면에 표시합니다.2 
Text('이름 : ${(Get.arguments as User).name}  나이 : ${(Get.arguments as User).age}'),

 

✅ 라우트관리 - 동적 URL 적용 

이번에는 url 파라미터를 넘겨보겠습니다. main.dart에서 페이지 이동을 추가합니다. 

GetPage(name: '/user/:uid', page: () => UserPage()),

 

값을 전달하는 화면의 코드를 먼저 작성합니다.  user의 uid는 123123이고 name은 제로 age는 30인 url을 만들었습니다. 

 ElevatedButton(
 onPressed: () {
 Get.toNamed('/user/123123?name=제로&age=30');
 },
 child: Text('GetX를 사용한 동적 URL'),
 ),

 

값을 전달받는 화면의 코드입니다. 

Text('${Get.parameters['uid']}'),
Text('${Get.parameters['name']}님 안녕하세요'),
Text('당신의 나이는 ${Get.parameters['uid']}입니다.'),

 

출력되는 화면입니다. 아래처럼 값이 출력됩니다. 

 

✅ 상태관리 - 단순 상태 관리 Provider와 getx의 비교 

이번에는 단순하게 숫자를 카운팅하는 앱을 통해 Provider와 getX의 상태 관리를 알아보겠습니다. 

 

먼저 Provider를 사용한 상태 관리입니다. 

1. pub.dev에서 provider를 가져와서 pubspec.yaml에 최신 버전을 추가해줍니다. 

 

2. 단순상태관리를 보여줄 화면에서 Provider 상태관리를 확인하기 위한 파일을 새로 만들어 연결합니다. 

Expanded(
	child: ChangeNotifierProvider<CountControllerWithProvider>(
		create: (_) => CountControllerWithProvider(),
		child: WithProvider(),
	),
),

 

2. Provider로 상태관리할 코드를 추가합니다. 

Column(
	children: [
		const Text(
			'Provider',
			style: TextStyle(fontSize: 20),
		),
		Consumer<CountControllerWithProvider>(builder: (_, snapshot, child) {
			return Text(
				'${snapshot.count}',
				style: TextStyle(fontSize: 50),
			);
		}),
		ElevatedButton(
			onPressed: () {
				Provider.of<CountControllerWithProvider>(context, listen: false).increase();
			},
			child: Text(
				'+',
				style: TextStyle(fontSize: 50),
			),
        ),
	],
),

 

3. ControllerWithProvider 파일을 만들어 ChangeNotifier를 상속받습니다.  

class CountControllerWithProvider extends ChangeNotifier {
  int count = 0;
  void increase() {
    count++;
    notifyListeners();
  }
}

 

 

Provider를 통한 구현이 끝났습니다. 잘 확인되네요👍

 

이번에는 getx를 통한 상태관리를 살펴봅니다. 

 

1. 마찬가지로 단순상태관리를 보여줄 화면에서 GetX 상태관리를 확인하기 위한 파일을 새로 만들어 연결합니다.   

Provider를 ChangeNotifier를 포함시켰던 것과는 다르게 자유롭게 추가해주면 됩니다. build 바로 아래 부분에 GetX를 사용을 선언해주면 사용할 수 있습니다. 👍 

Widget build(BuildContext context) {
    Get.put(CountControllerWithGetX());
    ........
 Expanded(child: WithGetX()),

 

2. GetX로 상태관리할 코드를 추가합니다.  

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get/instance_manager.dart';
import 'package:getx_demo/src/controller/count_controller_with_getx.dart';

class WithGetX extends StatelessWidget {
  const WithGetX({Key? key}) : super(key: key);

  Widget _button() {
    return ElevatedButton(
        onPressed: () {
          Get.find<CountControllerWithGetX>().increase();
        },
        child: Text(
          '+',
          style: TextStyle(fontSize: 50),
        ));
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(20.0),
      child: Center(
        child: Column(
          children: [
            Text(
              'GetX',
              style: TextStyle(fontSize: 20),
            ),
            GetBuilder<CountControllerWithGetX>(builder: (controller) {
              return Text(
                '${controller.count}',
                style: TextStyle(fontSize: 50),
              );
            }),
            _button(),
          ],
        ),
      ),
    );
  }
}

 

3. ControllerWithGetX 파일을 만들어 GetXController를 상속받습니다.  

import 'package:get/get.dart';
import 'package:flutter/material.dart';

class CountControllerWithGetX extends GetxController {
  int count = 0;
  void increase() {
    count++;
    update();
  }
}

getx를 통한 구현이 끝났습니다. 사실 아직까지는 상태관리에 대한 이해가 완벽하게 되지 않았기 때문에 글이 어수선한 것 같습니다.

이해가 된다면 설명이 더 명확해질 수 있도록 보완하겠습니다. 🥲

✅ 상태관리 - 반응형 상태 관리 

1. 반응형 상태관리의 화면에는 기존과 동일하게 버튼과 숫자를 표시할 화면을 바로 그려줍니다.  그리고 연결할 controller도 연결합니다. 

Get.put(CountControllerWithReactive());
Scaffold(
      appBar: AppBar(
        title: const Text('ReactiveStateManage page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              '0',
              style: TextStyle(fontSize: 50),
            ),
            ElevatedButton(
                onPressed: () {},
                child: Text(
                  '+',
                  style: TextStyle(fontSize: 50),
                )),
          ],
        ),
      ),
    );

 

2. ControllerWithReactive 파일을 만들어 obs로 선언합니다. 

여기서 int 타입이 RxInt로 변경되는데 반응형 상태 관리를 위한 변수 선언 방법입니다. 

import 'package:get/get.dart';

class CountControllerWithReactive {
  RxInt count = 0.obs;
  void increase(String id) {
    count++;
  }
}

 

3. obx : 반응형 상태관리에서 변화를 감지했을 때 업데이트해줍니다. 

반응형의 장점은 변경이 감지됐을 때만 업데이트해주기 때문에 리소스를 덜 사용하면서 화면을 제어할 수 있습니다. 

Obx(
  () => Text(
  	'${Get.find<CountControllerWithReactive>().count.value}',
  	style: TextStyle(fontSize: 50),
  	),
  ),
ElevatedButton(
  onPressed: () {
	  Get.find<CountControllerWithReactive>().increase();
  },
  child: Text(
  	'+',
  	style: TextStyle(fontSize: 50),
  )),

 

+ 반응형에서 GetxController를 상속받으면 여러 이벤트를 사용할 수 있습니다. 

 

✅ 3. 종속성관리 - 의존성주입 binding 사용 

binding을 사용하여 의존성을 관리하는 네 가지 방법을 알아봅니다. 

1. dependency_manage_page를 만들어 화면을 만들었고 binding을 사용해 코드를 작성합니다.  

binding을 사용하면 그 방법에 따라 화면이 빌드되는 그 즉시 메모리를 불러오고 화면에서 나가면 메모리를 삭제해줍니다. 

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:getx_demo/src/controller/dependency_controller.dart';
import 'package:getx_demo/src/pages/dependencies/get_lazyput.dart';
import 'package:getx_demo/src/pages/dependencies/get_put.dart';

class dependencyManagePage extends StatelessWidget {
  const dependencyManagePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('의존성 주입'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                  onPressed: () {
                    Get.to(
                      GetPut(),
                      binding: BindingsBuilder(
                        () {
                          Get.put(
                            DependencyController(),
                          );
                        },
                      ),
                    );
                  },
                  child: Text('Getput')),
              ElevatedButton(
                  onPressed: () {
                    Get.to(
                      GetLazyPut(),
                      binding: BindingsBuilder(
                        () {
                          Get.lazyPut<DependencyController>(
                            () => DependencyController(),
                          );
                        },
                      ),
                    );
                  },
                  child: Text('Get.lazyput')),
              ElevatedButton(
                  onPressed: () {
                    Get.to(
                      GetPut(),
                      binding: BindingsBuilder(
                        () {
                          Get.putAsync<DependencyController>(
                            () async {
                              await Future.delayed(Duration(seconds: 5));
                              return DependencyController();
                            },
                          );
                        },
                      ),
                    );
                  },
                  child: Text('Get.putAsync')),
              ElevatedButton(
                  onPressed: () {
                    Get.to(
                      GetPut(),
                      binding: BindingsBuilder(
                        () {
                          Get.create<DependencyController>(
                            () => DependencyController(),
                          );
                        },
                      ),
                    );
                  },
                  child: Text('Get.create')),
            ],
          ),
        ));
  }
}

 

의존성 주입을 마친 화면입니다. 화면을 나가면 메모리가 삭제됐다고 뜨는 걸 볼 수 있습니다. 

 

✅ 참고

https://youtu.be/O1Bw-mwF9xc

 

반응형