bloc을 이용하여 디자인을 해볼 시간이다. 카운터부터 만들어보자.

1
2
3
4
5
6
7
8
9
10
11
12
abstract class Counter {
  int count;
  Counter(this.count);
}

class Increase extends Counter {
  Increase(count) : super(count);
}

class Decrease extends Counter {
  Decrease(count) : super(count);
}

추상 클래스로 더하기와 빼기를 만들어준다. 당연히 여기서는 아무것도 정의하지 않는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class BlocCounter extends Bloc<Counter, int> {
  BlocCounter() : super(0) {
    on<Increase>((event, emit) => emit(state + event.count));
    on<Decrease>((event, emit) => emit(state - event.count));
  }

  @override
  void onEvent(Counter event) {
    super.onEvent(event);
  }

  @override
  void onTransition(Transition<Counter, int> transition) {
    super.onTransition(transition);
    print(transition);
  }
}

Bloc은 2개의 제네릭을 받는다. 하나는 우리가 받을 형식이고, 또하나는 그 형식의 타입이다. 우리는 Counter 클래스를 형식으로 받을 것이고, 그것은 int 타입이다. 생성자 함수를 통해 on<제네릭>(함수)를 선언하는데 이러한 패턴이 바로 Business Logic Component, 줄여서 BLoC 패턴이다. 하지만 너무 많은 생성자 함수를 추가하는 것은 SRP(Single Responsibility Principle)를 위반하게 되기에 주의해야 한다.

  • SRP(Single Responsibility Principle)란 하나의 클래스는 하나의 책임만 가지도록 설계되어야 한다는 소프트웨어 디자인 원칙이다.

on<제네릭>에서 우리가 실행할 클래스를 함수처럼 넘겨받고, 그것의 명령을 설계해준다. 이제 마지막으로 구동되는 부분을 작성할 시간이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyApp extends HookWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          body: BlocProvider<BlocCounter>(
        create: (context) => BlocCounter(),
        child: const BuildWidget(),
      )),
    );
  }
}

기본적으로 bloc 또한 provider를 이용하기에 프로바이더와 유사한 패턴을 가진다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class BuildWidget extends HookWidget {
  const BuildWidget({super.key});

  @override
  Widget build(BuildContext context) {
    final counter = BlocProvider.of<BlocCounter>(context);
    return BlocBuilder<BlocCounter, int>(builder: (context, count) {
      return Center(
        child: Column(
          children: [
            Text('${counter.state}', style: const TextStyle(fontSize: 30)),
            GestureDetector(
              onTap: () => counter.add(Increase(1)),
              child: Container(
                width: 50,
                height: 50,
                decoration: BoxDecoration(
                  color: Colors.green,
                  borderRadius: BorderRadius.circular(50),
                ),
                child: const Icon(Icons.exposure_plus_1),
              ),
            ),
            GestureDetector(
              onTap: () => counter.add(Decrease(1)),
              child: Container(
                width: 50,
                height: 50,
                decoration: BoxDecoration(
                  color: Colors.blue,
                  borderRadius: BorderRadius.circular(50),
                ),
                child: const Icon(Icons.exposure_minus_1),
              ),
            ),
          ],
        ),
      );
    });
  }
}

일급 함수 counter에서 of를 받아주고, BlocCounter 제네릭을 지정해준다. 우리는 BlocCounter 형식을 넘겼으니까 BlocCounter 형식을 받아야 한다. builder 함수를 호출하고, 리턴 값으로 우리의 위젯들 안에 counter의 메소드를 넣으면 완성된다.