[Flutter] Bloc 상태관리

Raon·2023년 1월 29일
2

Flutter

목록 보기
11/24

Bloc?

블록? -> 상태관리를 위해 사용하는 디자인 패턴. Flutter에서는 bloc패키지와 flutter_bloc패키지를 이용해서 쉽게 사용할 수 있다.

간단하게 GetX 대비 장 / 단점을 나열해보자면

장점

  • 견고한 구조를 가지기 때문에 시스템이 복잡해져도 소스코드의 유지 관리에 도움이 된다.
  • 다른 상태관리 패키지보다 정형화된 Boilerplate를 가지고 있어 팀 단위 작업시 신규 인원이 투입되어도 코드를 이해하기 수월하다.

단점

  • 프로젝트 규모가 크지 않은 경우 Boilerplate로 인한 코드량 증가가 압도적으로 심하다.
  • 입문 난이도가 상대적으로 높다.

정도로 요약할 수 있다.
이제 예제를 통해 사용법을 알아보도록하자.


예제

이 글에서 다루는 예제는 Bloc을 구현하기 위한 패턴을 구현하는 것 보다는 bloc, flutter_bloc 패키지를 통해 어떻게 bloc을 활용할 수 있을까에 대해 다뤄보고자 한다.

우선 이번 예제에서 사용할 데이터 모델을 정의한다.
항상 모든 예제들이 불편했던 부분 중 하나가 int, String같은 단순 데이터를 가지고 예시를 들었는데 실제로 개발을 하다보면 중첩된 List, Map 등 복잡한 자료구조가 사용되기 때문에 이번 예제에서는 먼저 데이터 모델을 정의 하고자 한다.

class User {
	final String name;
	final int age;

	User({this.name = '', this.age=0});
}

이제 Flutter프로젝트에서 bloc과 flutter_bloc을 다운받는다.

$ flutter pub add bloc flutter_bloc

이제 bloc을 구현하는데, bloc을 구현할 때는 Bloc혹은 Cubit을 상속받아 구현한다. 이번 예제는 Bloc을 상속받아 구현해보려고한다.

class UserBloc extends Bloc<UserEvent, User> {
  UserBloc() : super(User()) {

    //이벤트 리스너
    on<CreateUserEvent>(createUser);
  }

  // 이벤트 리스너 구현부
  FutureOr<void> createUser(CreateUserEvent event, Emitter<User> emit) {
    final User user = User(name: event.name, age: int.tryParse(event.age) ?? 0);
    emit(user);
  }
}


// 이벤트 정의
abstract class UserEvent {}

class CreateUserEvent extends UserEvent {
  final String name;
  final String age;

  CreateUserEvent({required this.name, required this.age});
}

예제의 Bloc은 UserEvent와 User를 상속받는다. UserEvent는 UI에서 Bloc으로 전달되는 이벤트를 정의한 것이고 User는 Bloc이 상태를 관리하려는 객채가 된다.

이벤트를 클래스로 정의한 이유는 클래스 내부에 변수를 선언해 둠으로서 각 이벤트마다 조건과 파라미터를 정의할 수 있는 점이 편리하기 때문이다. enum이나 다른 것들로 이벤트를 정의해도 상관없다.

이러한 이벤트들을 Bloc객체 생성 시 선언된 on함수를 통해 감지하고, on함수의 구현부에서 emit을 통해 상태를 변화시키는 것이 Bloc의 기본적인 동작이다.

실제로 동작하는 것을 확인해보기 위해 UI를 구현한다.

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocProvider(
        create: (context) => UserBloc(),
        child: Home(),
      ),
    );
  }
}


class Home extends StatelessWidget {
  Home({super.key});

  TextEditingController nameController = TextEditingController();
  TextEditingController ageController = TextEditingController();

  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => FocusScope.of(context).unfocus(),
      child: Scaffold(
        appBar: AppBar(title: const Text('Test')),
        body: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16.0),
            child: Column(
              children: [
                TextFormField(
                  controller: nameController,
                  decoration: const InputDecoration(labelText: 'Name'),
                ),
                TextFormField(
                  controller: ageController,
                  decoration: const InputDecoration(labelText: 'Age'),
                ),
                const SizedBox(height: 24.0),
                BlocBuilder<UserBloc, User>(
                  builder: (context, state) {
                    return Column(
                      children: [
                        Text('Name : ${state.name}'),
                        Text('Age : ${state.age}'),
                      ],
                    );
                  },
                ),
                const SizedBox(height: 24.0),
                ElevatedButton(
                  onPressed: () {
                   BlocProvider.of<UserBloc>(context).add(
                     CreateUserEvent(
                       name: nameController.text,
                       age: ageController.text,
                      ),
                    );
                  },
                  child: const Text('Generate User'),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

BlocBuilder<UserBloc, User>는 builder를 통해 제네릭으로 선언된 Bloc의 상태가 변화하면 화면을 다시 그리는 역할을 한다.
BlocProvider는 현재 위젯의 BuildContext를 통해 Bloc을 찾는 역할을 하며 add함수를 통해 이벤트를 전달한다.


동작 화면

profile
Flutter 개발자

0개의 댓글