[Flutter] BLoC 패턴

uengmin·2025년 3월 6일

Flutter

목록 보기
18/20
post-thumbnail

BLoC 패턴의 목적

  • 상태 관리, UI, 비지니스 로직을 분리하기 위함

    굳이 왜 분리 해야할까?
    배포를 위한 앱을 개발할 때 복잡한 구조의 위젯 트리를 만들었다고 했을 때 Scaffold 위젯을 통해서 많은 위젯이 제어. 이 과정 속에서 모든 하위 위젯들의 불필요한 업데이트가 진행.
    -> 간단한 앱이면 크게 상관 없지만 무거운 앱일수록 사용자가 체감될 정도로 앱이 느려짐.

이러한 문제를 막기 위해 BLoC 사용하여 UI와 비지니스 로직을 분리하고 상태 관리를 하여 제어하고 싶은 위젯만 따로 제어해 효율적이고 클린 코드 설계 가능하며 유지보수 측면에서 유리.

구현

프로젝트 구조

├── lib
│   ├── src
│   │   ├── bloc
│   │   │   └── counter_bloc.dart
│   │   ├── components
│   │   │   └── counter_view.dart
│   │   └── ui
│   │       └── bloc_display_widget.dart
│   └── main.dart
├── pubspec.lock
├── pubspec.yaml

main.dart

    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: BlocDisplayWidget() // BlocDisplayWidget 호출
    );
  }
  • 메인에서 위젯 호출.

bloc_display_widget.dart

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Bloc 패턴"),
        centerTitle: true,
        elevation: 0.0,
      ),
      body: CountView(), // Count만을 관리하는 CountView 호출

      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          IconButton(
            icon: Icon(Icons.add),
            onPressed: () {
              // countBloc에서 add() 이벤트를 호출
              countBloc.add();
            },
          ),
          IconButton(
            icon: Icon(Icons.remove),
            onPressed: () {
              // countBloc에서 remove() 이벤트를 호출
              countBloc.remove();
            },
          )
        ],
      ),
    );
  }
  • 이 코드는 UI 코드이며 전역 변수로 CountBloc을 호출하고 late로 나중에 값을 받음
  • floatingActionButton으로 버튼 생성

count_bloc.dart

class CountBloc {
  int _count = 0;

  // StreamController을 통해 여러 이벤트를 처리
  final StreamController _countSubject = StreamController.broadcast();

  // count는 _countSubject.stream 을 구독하고 있는 모든 위젯에게 변경된 상태를 알림
  Stream get count => _countSubject.stream;

  // count 덧셈 이벤트 처리
  add() {
    _count++;
    _countSubject.sink.add(_count); // _countSubject.sink 에다가 _count를 넣어준다.
  }

  // count 뺄셈 이벤트 처리
  remove() {
    _count--;
    _countSubject.sink.add(_count); // _countSubject.sink 에다가 _count를 넣어준다.
  }

  // _countSubject을 종료
  dispose() {
    _countSubject.close();
  }
}
  • StreamController으로 여러 이벤트 처리
  • _countSubject.stream을 구독하는 위젯에게 변경 상태를 count로 알림

count_view.dart

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

  
  Widget build(BuildContext context) {
    return Center(
      // 비동기 처리(StreamBuilder : 변화되는 값을 계속해서 감지)
      // StreamBuilder를 통해 countBloc.count을 감지
      child: StreamBuilder(
        stream: countBloc.count, // countBloc.count => _countSubject.stream 을 구독중
        initialData: 0,
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          // AsyncSnapshot을 통해 들어온 snapshot을 UI에 뿌려준다.
          if (snapshot.hasData) {
            return Text(
              snapshot.data.toString(),
              style: TextStyle(fontSize: 80),
            );
          }
          return CircularProgressIndicator();
        },
      ),
    );
  }

하지만 BLoC 패턴의 경우 간단한 로직 구현에도 최소 4개의 클래스를 작성해야함.
그래서 Provider가 대안으로 나왔으므로 다음에은 Provider를 공부해보자.

참조

0개의 댓글