반응형

6. Riverpod — 현대적인 의존성 주입 + 상태관리

Riverpod은 Provider의 한계를 극복한 차세대 상태관리입니다.

모든 Provider가 전역적으로 안전하며, 의존 관계 추적과 테스트가 용이합니다.

Riverpod 예제

import 'package:flutter_riverpod/flutter_riverpod.dart';

// 단순 Provider
final greetingProvider = Provider<String>((ref) => 'Hello Riverpod');

// 상태 변경 가능한 Provider
class Counter extends Notifier<int> {
  @override
  int build() => 0;

  void increment() => state++;
}

final counterProvider = NotifierProvider<Counter, int>(Counter.new);

ConsumerWidget 사용 예시

class CounterPage extends ConsumerWidget {
  const CounterPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Scaffold(
      body: Center(child: Text('$count')),
      floatingActionButton: FloatingActionButton(
        onPressed: () => ref.read(counterProvider.notifier).increment(),
        child: const Icon(Icons.add),
      ),
    );
  }
}

테스트가 쉬운 이유

void main() {
  test('Counter increments', () {
    final container = ProviderContainer();
    final counter = container.read(counterProvider.notifier);
    counter.increment();
    expect(container.read(counterProvider), 1);
  });
}

 요약: 전역 안전, autoDispose, 의존 추적, 테스트 친화적

 

7. BLoC / Cubit — 이벤트 기반 리액티브 아키텍처

BLoC(Business Logic Component)은 Event → State 흐름으로 동작합니다.

Cubit은 BLoC의 경량 버전으로 보일러플레이트가 적습니다.

Cubit 예시

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);
  void increment() => emit(state + 1);
}

class CounterView extends StatelessWidget {
  const CounterView({super.key});
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => CounterCubit(),
      child: Scaffold(
        body: Center(
          child: BlocBuilder<CounterCubit, int>(
            builder: (_, count) => Text('$count'),
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => context.read<CounterCubit>().increment(),
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

BLoC (이벤트 → 상태)

sealed class TodoEvent {}
class FetchTodos extends TodoEvent {}
class AddTodo extends TodoEvent { AddTodo(this.title); final String title; }

sealed class TodoState {}
class TodoLoading extends TodoState {}
class TodoLoaded extends TodoState { TodoLoaded(this.items); final List<Todo> items; }
class TodoError extends TodoState { TodoError(this.message); final String message; }

class TodoBloc extends Bloc<TodoEvent, TodoState> {
  final TodoRepository repo;
  TodoBloc(this.repo): super(TodoLoading()) {
    on<FetchTodos>((event, emit) async {
      try {
        final items = await repo.fetchTodos();
        emit(TodoLoaded(items));
      } catch (e) {
        emit(TodoError(e.toString()));
      }
    });
  }
}

BLoC 장점
- 예측 가능하고 유지보수 쉬움
- 복잡한 비즈니스 로직에 적합
- 강력한 로깅/디버깅/QA 지원

 

8. GetX — 빠르고 간단한 올인원 프레임워크

GetX는 상태관리 + 라우팅 + 의존성 주입을 통합 제공합니다.

예제

class CounterController extends GetxController {
  final count = 0.obs;
  void increment() => count.value++;
}

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

  @override
  Widget build(BuildContext context) {
    final controller = Get.put(CounterController());
    return Scaffold(
      body: Center(child: Obx(() => Text('${controller.count}'))),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

장점
- 코드 간결, 러닝커브 낮음
- 라우팅/DI 내장

단점
- 전역 남용 시 결합도 증가
- 대형 프로젝트엔 구조화 필요

⚠️ 권장 사용법:

GetX Controller에는 UI 로직만 두고, 비즈니스 로직은 UseCase나 Repository로 분리하세요.

 

9. 중간 비교 요약

항목RiverpodBLoCGetX

설계 철학 안전한 DI 기반 이벤트 리액티브 올인원 솔루션
러닝커브 중간 높음 낮음
코드 복잡도 높음 낮음
테스트 용이성 매우 높음 높음 낮음
전역 안전성 높음 높음 낮음
추천 규모 중~대형 대형 소~중형
반응형
Posted by 까칠코더
,