이벤트 가져오기 위젯 테스트 추가 및 테스트 훅 도입

This commit is contained in:
2025-09-12 06:33:44 +09:00
parent 107abc963f
commit a5f2544d11
121 changed files with 39277 additions and 3911 deletions
@@ -40,7 +40,8 @@ class _ClubStatisticsScreenState extends State<ClubStatisticsScreen> {
setState(() {
_isLoading = true;
});
// await 이전에 messenger 캡처하여 안전하게 사용
final messenger = ScaffoldMessenger.of(context);
try {
final authService = Provider.of<AuthService>(context, listen: false);
final clubService = Provider.of<ClubService>(context, listen: false);
@@ -73,9 +74,9 @@ class _ClubStatisticsScreenState extends State<ClubStatisticsScreen> {
}
}
} catch (e) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('데이터 로드 중 오류가 발생했습니다: $e')));
messenger.showSnackBar(
SnackBar(content: Text('데이터 로드 중 오류가 발생했습니다: $e')),
);
} finally {
setState(() {
_isLoading = false;
@@ -10,6 +10,7 @@ import '../../services/score_service.dart';
import '../../models/member_model.dart';
import '../../models/score_model.dart';
import 'score_entry_screen.dart';
import '../../widgets/dialog_actions.dart';
class MemberStatisticsScreen extends StatefulWidget {
final String memberId;
@@ -56,12 +57,15 @@ class _MemberStatisticsScreenState extends State<MemberStatisticsScreen>
_isLoading = true;
});
// await 이전에 messenger 캡처
final messenger = ScaffoldMessenger.of(context);
try {
final authService = Provider.of<AuthService>(context, listen: false);
final clubService = Provider.of<ClubService>(context, listen: false);
final memberService = Provider.of<MemberService>(context, listen: false);
final scoreService = Provider.of<ScoreService>(context, listen: false);
final clubService = Provider.of<ClubService>(context, listen: false);
if (clubService.currentClub != null) {
// 회원 정보 로드
_member = await memberService.fetchMemberById(widget.memberId);
@@ -80,13 +84,15 @@ class _MemberStatisticsScreenState extends State<MemberStatisticsScreen>
]);
}
} catch (e) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('데이터 로드 중 오류가 발생했습니다: $e')));
messenger.showSnackBar(
SnackBar(content: Text('데이터 로드 중 오류가 발생했습니다: $e')),
);
} finally {
setState(() {
_isLoading = false;
});
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
@@ -608,7 +614,7 @@ class _MemberStatisticsScreenState extends State<MemberStatisticsScreen>
dotData: const FlDotData(show: true),
belowBarData: BarAreaData(
show: true,
color: Colors.blue.withOpacity(0.2),
color: Colors.blue.withValues(alpha: 0.2),
),
),
],
@@ -619,43 +625,28 @@ class _MemberStatisticsScreenState extends State<MemberStatisticsScreen>
void _showDeleteConfirmationDialog(Score score) {
showDialog(
context: context,
builder: (context) => AlertDialog(
builder: (dialogContext) => AlertDialog(
title: const Text('점수 삭제'),
content: const Text('이 점수 기록을 정말 삭제하시겠습니까?'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('취소'),
),
TextButton(
onPressed: () async {
Navigator.of(context).pop();
try {
final scoreService = Provider.of<ScoreService>(
context,
listen: false,
);
await scoreService.deleteScore(score.id);
// 데이터 새로고침
_loadMemberData();
if (mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('점수가 삭제되었습니다')));
}
} catch (e) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('점수 삭제에 실패했습니다: $e')));
}
},
style: TextButton.styleFrom(foregroundColor: Colors.red),
child: const Text('삭제'),
),
],
actions: DialogActions.confirm(
onCancel: () => Navigator.of(dialogContext).pop(),
onConfirm: () async {
// await 전에 필요한 객체 캡처
final navigator = Navigator.of(dialogContext);
final messenger = ScaffoldMessenger.of(dialogContext);
final scoreService = Provider.of<ScoreService>(dialogContext, listen: false);
navigator.pop();
try {
await scoreService.deleteScore(score.id);
_loadMemberData();
messenger.showSnackBar(const SnackBar(content: Text('점수가 삭제되었습니다')));
} catch (e) {
messenger.showSnackBar(SnackBar(content: Text('점수 삭제에 실패했습니다: $e')));
}
},
destructive: true,
confirmText: '삭제',
),
),
);
}
@@ -65,21 +65,16 @@ class _ScoreEntryScreenState extends State<ScoreEntryScreen> {
// 점수 저장
Future<void> _saveScore() async {
if (!_formKey.currentState!.validate()) {
return;
}
if (_selectedMemberId == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('회원을 선택해주세요')),
);
return;
}
if (!_formKey.currentState!.validate()) return;
setState(() {
_isLoading = true;
});
// await 전에 필요한 파생 객체 캡처 (catch에서도 사용 가능하도록 try 밖)
final navigator = Navigator.of(context);
final messenger = ScaffoldMessenger.of(context);
try {
final scoreService = Provider.of<ScoreService>(context, listen: false);
final clubService = Provider.of<ClubService>(context, listen: false);
@@ -106,22 +101,23 @@ class _ScoreEntryScreenState extends State<ScoreEntryScreen> {
// 점수 저장
await scoreService.addScore(scoreData);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('점수가 저장되었습니다')),
);
Navigator.of(context).pop();
}
// 성공 알림 및 화면 닫기
messenger.showSnackBar(
const SnackBar(content: Text('점수가 저장되었습니다')),
);
navigator.pop();
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
// 에러 알림 (사전 캡처한 messenger 사용)
messenger.showSnackBar(
SnackBar(content: Text('점수 저장에 실패했습니다: $e')),
);
} finally {
setState(() {
_isLoading = false;
});
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
@@ -197,15 +193,21 @@ class _ScoreEntryScreenState extends State<ScoreEntryScreen> {
final members = memberService.members;
return DropdownButtonFormField<String>(
key: const Key('score_entry_member_dropdown'),
decoration: const InputDecoration(
labelText: '회원 선택',
border: OutlineInputBorder(),
),
value: _selectedMemberId,
isExpanded: true,
items: members.map((member) {
return DropdownMenuItem<String>(
value: member.id,
child: Text(member.name),
child: Text(
member.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
);
}).toList(),
onChanged: (value) {
@@ -230,11 +232,13 @@ class _ScoreEntryScreenState extends State<ScoreEntryScreen> {
final events = eventService.events;
return DropdownButtonFormField<String>(
key: const Key('score_entry_event_dropdown'),
decoration: const InputDecoration(
labelText: '이벤트 선택 (선택사항)',
border: OutlineInputBorder(),
),
value: _selectedEventId,
isExpanded: true,
items: [
const DropdownMenuItem<String>(
value: null,
@@ -243,7 +247,11 @@ class _ScoreEntryScreenState extends State<ScoreEntryScreen> {
...events.map((event) {
return DropdownMenuItem<String>(
value: event.id,
child: Text(event.title),
child: Text(
event.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
);
}),
],