2025-11-14 18:03:50 +09:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
|
import 'package:service_api/service_api.dart';
|
|
|
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
|
|
|
|
|
|
|
|
class SettingsScreen extends StatelessWidget {
|
|
|
|
|
const SettingsScreen({super.key});
|
|
|
|
|
|
|
|
|
|
Future<void> _launchHomepage() async {
|
|
|
|
|
final Uri url = Uri.parse('https://lunaticbum.kr');
|
|
|
|
|
if (!await launchUrl(url, mode: LaunchMode.externalApplication)) {
|
|
|
|
|
debugPrint('Could not launch $url');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-15 18:18:17 +09:00
|
|
|
// 🔽 [신규] 기록 삭제 확인 다이얼로그
|
|
|
|
|
Future<void> _confirmClearHistory(BuildContext context) async {
|
|
|
|
|
final bool? confirmed = await showDialog<bool>(
|
|
|
|
|
context: context,
|
|
|
|
|
builder: (ctx) => AlertDialog(
|
|
|
|
|
title: const Text('진단 기록 삭제'),
|
|
|
|
|
content: const Text('저장된 모든 두뇌 진단 기록을 삭제하시겠습니까?\n삭제된 데이터는 복구할 수 없습니다.'),
|
|
|
|
|
actions: [
|
|
|
|
|
TextButton(
|
|
|
|
|
onPressed: () => Navigator.pop(ctx, false),
|
|
|
|
|
child: const Text('취소'),
|
|
|
|
|
),
|
|
|
|
|
TextButton(
|
|
|
|
|
onPressed: () => Navigator.pop(ctx, true),
|
|
|
|
|
style: TextButton.styleFrom(foregroundColor: Colors.red),
|
|
|
|
|
child: const Text('삭제'),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (confirmed == true && context.mounted) {
|
|
|
|
|
final identityService = context.read<IdentityService>();
|
|
|
|
|
await identityService.clearAssessmentHistory();
|
|
|
|
|
|
|
|
|
|
if (context.mounted) {
|
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
|
|
|
const SnackBar(content: Text('진단 기록이 삭제되었습니다.')),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-14 18:03:50 +09:00
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
final themeNotifier = context.watch<ThemeNotifier>();
|
|
|
|
|
final sessionNotifier = context.watch<SessionNotifier>();
|
|
|
|
|
|
|
|
|
|
return Scaffold(
|
2025-12-15 18:18:17 +09:00
|
|
|
appBar: AppBar(title: const Text('설정')),
|
2025-11-14 18:03:50 +09:00
|
|
|
body: ListView(
|
|
|
|
|
children: [
|
|
|
|
|
ListTile(
|
|
|
|
|
leading: Icon(
|
|
|
|
|
sessionNotifier.isGuest
|
|
|
|
|
? Icons.person_outline
|
|
|
|
|
: Icons.person_rounded
|
|
|
|
|
),
|
|
|
|
|
title: Text(
|
|
|
|
|
sessionNotifier.isLoading
|
|
|
|
|
? '계정 정보 로딩 중...'
|
|
|
|
|
: (sessionNotifier.isGuest
|
|
|
|
|
? '게스트 계정'
|
|
|
|
|
: sessionNotifier.session?.userName ?? '로그인됨')
|
|
|
|
|
),
|
|
|
|
|
subtitle: Text(
|
|
|
|
|
sessionNotifier.isLoading
|
|
|
|
|
? ''
|
|
|
|
|
: (sessionNotifier.isGuest
|
|
|
|
|
? '진행 상황을 저장하려면 로그인하세요.'
|
|
|
|
|
: (sessionNotifier.session?.email ?? '소셜 계정'))
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
if (!sessionNotifier.isLoading)
|
|
|
|
|
if (sessionNotifier.isGuest)
|
|
|
|
|
Padding(
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
|
|
|
|
child: Row(
|
|
|
|
|
children: [
|
|
|
|
|
Expanded(
|
|
|
|
|
child: OutlinedButton.icon(
|
2025-12-15 18:18:17 +09:00
|
|
|
icon: const Icon(Icons.g_mobiledata),
|
2025-11-14 18:03:50 +09:00
|
|
|
label: const Text('Google 로그인'),
|
|
|
|
|
onPressed: () {
|
|
|
|
|
sessionNotifier.login('google');
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(width: 10),
|
|
|
|
|
Expanded(
|
|
|
|
|
child: OutlinedButton.icon(
|
|
|
|
|
icon: const Icon(Icons.apple),
|
|
|
|
|
label: const Text('Apple 로그인'),
|
|
|
|
|
onPressed: () {
|
|
|
|
|
sessionNotifier.login('apple');
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
else
|
|
|
|
|
ListTile(
|
|
|
|
|
title: const Text('로그아웃', style: TextStyle(color: Colors.red)),
|
|
|
|
|
leading: const Icon(Icons.logout, color: Colors.red),
|
|
|
|
|
onTap: () {
|
|
|
|
|
sessionNotifier.logout();
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
const Divider(),
|
|
|
|
|
|
|
|
|
|
SwitchListTile(
|
|
|
|
|
title: const Text('다크 모드'),
|
|
|
|
|
secondary: const Icon(Icons.dark_mode_outlined),
|
|
|
|
|
value: themeNotifier.isDarkMode,
|
|
|
|
|
onChanged: (newValue) {
|
|
|
|
|
themeNotifier.toggleTheme(newValue);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
|
2025-12-15 18:18:17 +09:00
|
|
|
// 🔽 [신규] 글자 크기 조절 섹션
|
|
|
|
|
ListTile(
|
|
|
|
|
title: const Text('글자 크기'),
|
|
|
|
|
subtitle: Text(_getScaleLabel(themeNotifier.textScaleFactor)),
|
|
|
|
|
leading: const Icon(Icons.format_size),
|
|
|
|
|
),
|
|
|
|
|
Padding(
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
Slider(
|
|
|
|
|
value: themeNotifier.textScaleFactor,
|
|
|
|
|
min: 0.85,
|
|
|
|
|
max: 1.5,
|
|
|
|
|
divisions: 4,
|
|
|
|
|
label: _getScaleLabel(themeNotifier.textScaleFactor),
|
|
|
|
|
onChanged: (value) {
|
|
|
|
|
themeNotifier.setTextScale(value);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
children: const [
|
|
|
|
|
Text("작게", style: TextStyle(fontSize: 12)),
|
|
|
|
|
Text("표준", style: TextStyle(fontSize: 12)),
|
|
|
|
|
Text("크게", style: TextStyle(fontSize: 12)),
|
|
|
|
|
Text("더 크게", style: TextStyle(fontSize: 12)),
|
|
|
|
|
Text("완전 크게", style: TextStyle(fontSize: 12)),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
const Divider(),
|
|
|
|
|
|
|
|
|
|
// 🔽 [신규] 데이터 관리 섹션
|
|
|
|
|
const Padding(
|
|
|
|
|
padding: EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 8.0),
|
|
|
|
|
child: Text(
|
|
|
|
|
'데이터 관리',
|
|
|
|
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
ListTile(
|
|
|
|
|
leading: const Icon(Icons.delete_outline, color: Colors.red),
|
|
|
|
|
title: const Text('진단 기록 삭제', style: TextStyle(color: Colors.red)),
|
|
|
|
|
subtitle: const Text('저장된 두뇌 건강 진단 기록을 모두 지웁니다.'),
|
|
|
|
|
onTap: () => _confirmClearHistory(context),
|
|
|
|
|
),
|
|
|
|
|
|
2025-11-14 18:03:50 +09:00
|
|
|
const Divider(),
|
|
|
|
|
|
|
|
|
|
const Padding(
|
|
|
|
|
padding: EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 8.0),
|
|
|
|
|
child: Text(
|
|
|
|
|
'앱 테마 색상',
|
|
|
|
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
...appColors.entries.map((entry) {
|
|
|
|
|
final String colorName = entry.key;
|
|
|
|
|
final MaterialColor color = entry.value;
|
|
|
|
|
|
|
|
|
|
return ListTile(
|
|
|
|
|
leading: CircleAvatar(
|
|
|
|
|
backgroundColor: color,
|
|
|
|
|
),
|
|
|
|
|
title: Text(colorName),
|
|
|
|
|
trailing: (themeNotifier.currentColor == color)
|
|
|
|
|
? Icon(Icons.check, color: Theme.of(context).colorScheme.secondary)
|
|
|
|
|
: null,
|
|
|
|
|
onTap: () {
|
|
|
|
|
themeNotifier.setTheme(colorName);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
const Divider(),
|
|
|
|
|
|
|
|
|
|
ListTile(
|
|
|
|
|
leading: const Icon(Icons.description_outlined),
|
|
|
|
|
title: const Text('오픈소스 라이선스'),
|
|
|
|
|
onTap: () {
|
|
|
|
|
showLicensePage(
|
|
|
|
|
context: context,
|
|
|
|
|
applicationName: '스도쿠 게임',
|
|
|
|
|
applicationVersion: '1.0.0',
|
|
|
|
|
applicationIcon: const Icon(Icons.apps_rounded, size: 64),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
ListTile(
|
|
|
|
|
leading: const Icon(Icons.info_outline),
|
|
|
|
|
title: const Text('앱 정보'),
|
|
|
|
|
onTap: () {
|
|
|
|
|
showAboutDialog(
|
|
|
|
|
context: context,
|
|
|
|
|
applicationName: '스도쿠 게임',
|
|
|
|
|
applicationVersion: '1.0.0',
|
|
|
|
|
applicationIcon: const Icon(Icons.apps_rounded, size: 48),
|
|
|
|
|
children: [
|
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
|
InkWell(
|
|
|
|
|
onTap: _launchHomepage,
|
|
|
|
|
child: Padding(
|
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
|
|
|
|
child: Text(
|
|
|
|
|
'© 2025 lunaticbum',
|
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
color: Theme.of(context).colorScheme.primary,
|
|
|
|
|
decoration: TextDecoration.underline,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-12-15 18:18:17 +09:00
|
|
|
|
|
|
|
|
String _getScaleLabel(double scale) {
|
|
|
|
|
if (scale <= 0.9) return "작게";
|
|
|
|
|
if (scale <= 1.05) return "표준";
|
|
|
|
|
if (scale <= 1.2) return "크게";
|
|
|
|
|
if (scale <= 1.3) return "더 크게";
|
|
|
|
|
return "완전 크게";
|
|
|
|
|
}
|
2025-11-14 18:03:50 +09:00
|
|
|
}
|