๋ฐ์ํ
SMALL
๐ฌ ์ํ๊ด ๋น์ ๋ก ์ดํดํ๊ธฐ
BLoC์ ์ํ๊ด์ ๋น์ ํด๋ณผ๊ฒ์:
- Event (์ด๋ฒคํธ): ๊ด๊ฐ์ด ๊ทน์ฅ ์ง์์๊ฒ ํ๋ ์์ฒญ
- "ํ์ฝ ์ฃผ์ธ์", "์ํ ์์ํด์ฃผ์ธ์", "ํ์ฅ์ค ์ด๋์์?"
- BLoC (๋ธ๋ก): ๊ทน์ฅ ์ง์
- ๊ด๊ฐ์ ์์ฒญ์ ๋ฐ์์ ์ฒ๋ฆฌํจ
- State (์ํ): ๊ทน์ฅ์ ํ์ฌ ์ํฉ
- "์ํ ์์ ์ค", "ํ์ฝ ์ค๋น๋จ", "์ํ ์ข ๋ฃ"
๐ฑ ์ฝ๋๋ก ๋ณด๋ ์ค์ ์์ (ํ ๋ง ๋ณ๊ฒฝ)
๋น์ ์ ํ๋ก์ ํธ์์ ํ ๋ง ๋ณ๊ฒฝ ๊ธฐ๋ฅ์ ์๋ก ๋ค์ด๋ณผ๊ฒ์:
1๏ธโฃ Event (์ด๋ฒคํธ) - "๋ฌด์์ ์์ฒญํ ๊น?"
// theme_event.dart
// ์ด๋ฒคํธ = ์ฌ์ฉ์๊ฐ ํ ์ ์๋ ํ๋๋ค
sealed class ThemeEvent {
// 1. ์ฑ ์์ํ ๋ ํ
๋ง ์ด๊ธฐํ
const factory ThemeEvent.init() = _InitialEvent;
// 2. ํน์ ํ
๋ง๋ก ๋ณ๊ฒฝ
const factory ThemeEvent.change({ThemeType type}) = _ChangeThemeEvent;
// 3. ํ
๋ง ํ ๊ธ (๋ผ์ดํธ ↔ ๋คํฌ)
const factory ThemeEvent.toggle() = _ToggleThemeEvnet;
}
์ฝ๊ฒ ๋งํ๋ฉด:
- init: "์ฑ ์ผฐ์ด, ์ ์ฅ๋ ํ ๋ง ๋ถ๋ฌ์์ค!"
- change: "๋คํฌ ๋ชจ๋๋ก ๋ฐ๊ฟ์ค!"
- toggle: "์ง๊ธ ํ ๋ง ๋ฐ๋๋ก ๋ฐ๊ฟ์ค!"
2๏ธโฃ State (์ํ) - "ํ์ฌ ์ํฉ์?"
// theme_state.dart
class ThemeState {
final ThemeType type; // ํ์ฌ ํ
๋ง๊ฐ ๋ญ์ง
const ThemeState({
this.type = ThemeType.light, // ๊ธฐ๋ณธ์ ๋ผ์ดํธ ๋ชจ๋
});
}
์ฝ๊ฒ ๋งํ๋ฉด:
- "์ง๊ธ ์ฑ์ด ๋คํฌ ๋ชจ๋์ธ์ง ๋ผ์ดํธ ๋ชจ๋์ธ์ง" ์ ์ฅํ๋ ๊ณณ
- UI๋ ์ด ์ํ๋ฅผ ๋ณด๊ณ ํ๋ฉด์ ๊ทธ๋ฆผ
3๏ธโฃ BLoC (๋ธ๋ก) - "์ด๋ฒคํธ ์ฒ๋ฆฌํ๋ ์ง์"
// theme_bloc.dart
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
ThemeBloc() : super(const ThemeState()) {
// ์ด๋ฒคํธ๊ฐ ๋ค์ด์ค๋ฉด switch๋ก ์ฒ๋ฆฌ
on<ThemeEvent>((event, emit) async {
switch (event) {
case _InitialEvent():
await _initTheme(event, emit);
case _ChangeThemeEvent():
await _changeTheme(event, emit);
case _ToggleThemeEvnet():
await _toggleTheme(event, emit);
}
});
}
// 1. ํ
๋ง ์ด๊ธฐํ ์ฒ๋ฆฌ
Future<void> _initTheme(event, emit) async {
// ์ ์ฅ๋ ํ
๋ง ๋ถ๋ฌ์ค๊ธฐ
var result = await getAppThemeUseCase();
// ์๋ก์ด ์ํ๋ก ์
๋ฐ์ดํธ
emit(ThemeState(type: result));
}
// 2. ํ
๋ง ๋ณ๊ฒฝ ์ฒ๋ฆฌ
Future<void> _changeTheme(event, emit) async {
// ์ ํ
๋ง ์ ์ฅ
await saveAppThemeUseCase(event.type);
// ์ํ ์
๋ฐ์ดํธ
emit(state.copyWith(type: event.type));
}
// 3. ํ
๋ง ํ ๊ธ ์ฒ๋ฆฌ
Future<void> _toggleTheme(event, emit) async {
// ๋ฐ๋ ํ
๋ง ๊ณ์ฐ
var nextTheme = state.type == ThemeType.light
? ThemeType.dark
: ThemeType.light;
// ์ ์ฅํ๊ณ ์ํ ์
๋ฐ์ดํธ
await saveAppThemeUseCase(nextTheme);
emit(state.copyWith(type: nextTheme));
}
}
๐ ์ ์ฒด ํ๋ฆ ํ๋์ ๋ณด๊ธฐ
์ฌ์ฉ์ ๋ฒํผ ํด๋ฆญ
↓
[์ด๋ฒคํธ ๋ฐ์] ThemeEvent.toggle()
↓
[BLoC์ด ๋ฐ์] "์ค์ผ์ด, ํ ๊ธ ์ฒ๋ฆฌํ ๊ฒ!"
↓
[์ฒ๋ฆฌ ๋ก์ง ์คํ]
1. ํ์ฌ ํ
๋ง ํ์ธ (state.type)
2. ๋ฐ๋ ํ
๋ง ๊ณ์ฐ
3. ์ ์ฅ์์ ์ ์ฅ
↓
[์ ์ํ ๋ฐํ] emit(์๋ก์ด ThemeState)
↓
[UI ์๋ ์
๋ฐ์ดํธ] ํ๋ฉด์ด ๋คํฌ ๋ชจ๋๋ก ๋ณ๊ฒฝ๋จ!
๐ก ์ค์ ์ฌ์ฉ ์์
UI์์ BLoC ์ฌ์ฉํ๊ธฐ
// 1. BLoC ๊ฐ์ ธ์ค๊ธฐ
final themeBloc = context.read<ThemeBloc>();
// 2. ์ด๋ฒคํธ ๋ณด๋ด๊ธฐ (๋ฒํผ ํด๋ฆญ ์)
ElevatedButton(
onPressed: () {
// "ํ
๋ง ํ ๊ธํด์ค!" ์ด๋ฒคํธ ์ ์ก
themeBloc.add(const ThemeEvent.toggle());
},
child: Text('ํ
๋ง ๋ณ๊ฒฝ'),
)
// 3. ์ํ ๋ณํ ๊ฐ์งํ๊ธฐ
BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, state) {
// state๊ฐ ๋ฐ๋ ๋๋ง๋ค ์ด ๋ถ๋ถ ๋ค์ ๊ทธ๋ ค์ง
return Text(
'ํ์ฌ ํ
๋ง: ${state.type}' // "ํ์ฌ ํ
๋ง: dark"
);
},
)
๐ฏ ํต์ฌ ์ ๋ฆฌ
๊ตฌ์ฑ ์์์ญํ ๋น์
| Event | ์์ฒญ/๋ช ๋ น | ์ฃผ๋ฌธ์ |
| State | ํ์ฌ ์ํฉ | ์ฃผ๋ฌธ ์ํ |
| BLoC | ์ฒ๋ฆฌํ๋ ์ฌ๋ | ์ง์ |
| emit | ์ํ ๋ณ๊ฒฝ ์๋ฆผ | ์ง๋๋ฒจ ์ธ๋ฆผ |
| add | ์ด๋ฒคํธ ๋ณด๋ด๊ธฐ | ์ฃผ๋ฌธํ๊ธฐ |
๐ค ์์ฃผ ํ๋ ์ง๋ฌธ
Q: ์ ์ด๋ ๊ฒ ๋ณต์กํ๊ฒ ํ๋์? A:
- UI์ ๋น์ฆ๋์ค ๋ก์ง ๋ถ๋ฆฌ
- ํ ์คํธํ๊ธฐ ์ฌ์
- ์ํ ๊ด๋ฆฌ๊ฐ ๋ช ํํจ
- ์ฌ๋ฌ ํ๋ฉด์์ ๊ฐ์ ์ํ ๊ณต์ ๊ฐ๋ฅ
Q: Event๋ฅผ ๊ผญ ๋ง๋ค์ด์ผ ํ๋์? A: ๋ค! Event๊ฐ ์์ด์ผ:
- ์ด๋ค ๋์๋ค์ด ๊ฐ๋ฅํ์ง ๋ช ํํจ
- ๋์ค์ ์ฝ๋ ์ฐพ๊ธฐ ์ฌ์
- ๋๋ฒ๊น ํ ๋ ์ด๋ค ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋์ง ์ถ์ ๊ฐ๋ฅ
Q: State๋ ์ธ์ ๋ฐ๋๋์? A: BLoC์์ emit()์ ํธ์ถํ ๋๋ง!
๐ ๊ฐ๋จํ ์ฐ์ต ์์
์นด์ดํฐ ์ฑ์ผ๋ก ์ฐ์ตํด๋ณผ๊น์?
// 1. ์ด๋ฒคํธ ์ ์
sealed class CounterEvent {
const factory CounterEvent.increment() = _Increment;
const factory CounterEvent.decrement() = _Decrement;
}
// 2. ์ํ ์ ์
class CounterState {
final int count;
CounterState({this.count = 0});
}
// 3. BLoC ์ ์
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterState()) {
on<CounterEvent>((event, emit) {
switch (event) {
case _Increment():
emit(CounterState(count: state.count + 1));
case _Decrement():
emit(CounterState(count: state.count - 1));
}
});
}
}
// 4. UI์์ ์ฌ์ฉ
BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Column(
children: [
Text('${state.count}'), // ์ซ์ ํ์
ElevatedButton(
onPressed: () {
context.read<CounterBloc>()
.add(const CounterEvent.increment());
},
child: Text('+'),
),
],
);
},
)
๐จ ์ ๋ฆฌํ๋ฉด
- Event: "~ํด์ค!" (๋ช ๋ น)
- BLoC: "์์์ด, ์ฒ๋ฆฌํ ๊ฒ!" (์ฒ๋ฆฌ์)
- State: "์ง๊ธ ์ํฉ์ ์ด๋!" (๊ฒฐ๊ณผ)
- UI: State๋ฅผ ๋ณด๊ณ ํ๋ฉด์ ๊ทธ๋ฆผ
ํ๋ฆ: ๋ฒํผ ํด๋ฆญ → Event ๋ฐ์ → BLoC์ด ์ฒ๋ฆฌ → State ๋ณ๊ฒฝ → UI ์๋ ์ ๋ฐ์ดํธ
์ดํด๋์ จ๋์? ๋ ๊ถ๊ธํ ๋ถ๋ถ์ด ์์ผ๋ฉด ๋ง์ํด์ฃผ์ธ์! ๐
๋ฐ์ํ
LIST