fromis_9/app/lib/controllers/members_controller.dart
caadiq 671618442c Flutter 앱: 전체 화면 MVCS 아키텍처 리팩토링
홈, 멤버, 앨범 화면을 Riverpod 기반 MVCS 패턴으로 리팩토링.
View는 UI와 애니메이션만, Controller는 상태와 비즈니스 로직 담당.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 18:18:44 +09:00

111 lines
2.7 KiB
Dart

/// 멤버 컨트롤러 (MVCS의 Controller 레이어)
///
/// 비즈니스 로직과 상태 관리를 담당합니다.
/// View는 이 Controller를 통해 데이터에 접근합니다.
library;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/member.dart';
import '../services/members_service.dart';
/// 멤버 상태
class MembersState {
final List<Member> members;
final int currentIndex;
final bool isLoading;
final String? error;
const MembersState({
this.members = const [],
this.currentIndex = 0,
this.isLoading = true,
this.error,
});
/// 상태 복사 (불변성 유지)
MembersState copyWith({
List<Member>? members,
int? currentIndex,
bool? isLoading,
String? error,
}) {
return MembersState(
members: members ?? this.members,
currentIndex: currentIndex ?? this.currentIndex,
isLoading: isLoading ?? this.isLoading,
error: error,
);
}
/// 현재 선택된 멤버
Member? get currentMember =>
members.isNotEmpty ? members[currentIndex] : null;
}
/// 멤버 컨트롤러
class MembersController extends Notifier<MembersState> {
@override
MembersState build() {
// 초기 데이터 로드
Future.microtask(() => loadMembers());
return const MembersState();
}
/// 멤버 목록 로드
Future<void> loadMembers() async {
state = state.copyWith(isLoading: true, error: null);
try {
final members = await getMembers();
// 현재 멤버 먼저, 전 멤버 나중에 정렬
members.sort((a, b) {
if (a.isFormer != b.isFormer) {
return a.isFormer ? 1 : -1;
}
return 0;
});
state = state.copyWith(
members: members,
isLoading: false,
);
} catch (e) {
state = state.copyWith(
isLoading: false,
error: e.toString(),
);
}
}
/// 현재 인덱스 변경
void setCurrentIndex(int index) {
if (index >= 0 && index < state.members.length) {
state = state.copyWith(currentIndex: index);
}
}
/// 나이 계산
int? calculateAge(String? birthDate) {
if (birthDate == null) return null;
final birth = DateTime.tryParse(birthDate);
if (birth == null) return null;
final today = DateTime.now();
int age = today.year - birth.year;
if (today.month < birth.month ||
(today.month == birth.month && today.day < birth.day)) {
age--;
}
return age;
}
/// 생일 포맷팅
String formatBirthDate(String? birthDate) {
if (birthDate == null) return '';
return birthDate.substring(0, 10).replaceAll('-', '.');
}
}
/// 멤버 Provider
final membersProvider = NotifierProvider<MembersController, MembersState>(
MembersController.new,
);