템플릿마다 메인 대시보드 전체 테마(배경/타일/아이콘 색)가 같이 바뀌게 추가
용어 정리: 템플릿 = 상점에서 사는 색/애니메이션 상품, 테마 = 그 템플릿이 몰고 오는 화면 전체 디자인(배경색/타일색/아이콘색). 1단계로 메인 대시보드부터 적용 - 프로필 카드 그라데이션뿐 아니라 페이지 배경, 타일 카드 배경/테두리, 아이콘 동그라미 배경, 타일 아이콘 강조색, 제목/부제목 텍스트 색까지 장착한 템플릿에 맞춰 통째로 바뀐다. 잠긴/비활성 타일(NFC 미인증 등)은 기존처럼 중립 회색(sage)을 그대로 써서 상태 구분은 유지. 6개 기존 템플릿마다 직접 디자인한 테마(lib/theme/profile_themes.dart)를 새로 만들었고, 최고가 "갤럭시" 템플릿은 다른 5개와 달리 통째로 다크 테마로 디자인해 프리미엄 아이템 느낌을 살렸다. 백엔드 /api/shop/equipped가 템플릿 code를 같이 내려주도록 수정해서 프론트엔드가 code로 디자인해둔 테마를 찾아 쓸 수 있게 했다.
This commit is contained in:
+44
-37
@@ -12,6 +12,7 @@ import '../function/session_store.dart';
|
||||
import '../function/student_dashboard_controller.dart';
|
||||
import '../models/dashboard_tile_layout.dart';
|
||||
import '../theme/app_palette.dart';
|
||||
import '../theme/profile_themes.dart';
|
||||
import 'admin_dashboard.dart';
|
||||
import 'app_notice.dart';
|
||||
import 'board_home_screen.dart';
|
||||
@@ -85,17 +86,17 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
}
|
||||
}
|
||||
|
||||
// 🛍️ 프로필 꾸미기 상점에서 장착한 테마 색상 (없으면 기본 모습 그대로).
|
||||
Color? _equippedPrimary;
|
||||
Color? _equippedSecondary;
|
||||
// 🛍️ 프로필 꾸미기 상점에서 장착한 템플릿이 몰고 오는 테마 (없으면 기본 모습 그대로).
|
||||
ProfileTheme? _equippedTheme;
|
||||
|
||||
// 🎨 타일 아이콘 등 강조색에 쓸 색 - 테마가 없으면 원래 쓰던 AppPalette.ink.
|
||||
Color get _tileAccent => _equippedTheme?.accent ?? AppPalette.ink;
|
||||
|
||||
Future<void> _loadEquippedTheme() async {
|
||||
final (primary, secondary) =
|
||||
await ProfileShopController.fetchEquippedColors(_displayId);
|
||||
final theme = await ProfileShopController.fetchEquippedTheme(_displayId);
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_equippedPrimary = primary;
|
||||
_equippedSecondary = secondary;
|
||||
_equippedTheme = theme;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -312,7 +313,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
: Icons.lock_rounded,
|
||||
title: 'NFC 태그',
|
||||
subtitle: widget.isDeviceMatched ? '출석 및 폰 수거 완료' : '본인 인증 기기 전용',
|
||||
color: widget.isDeviceMatched ? AppPalette.ink : AppPalette.sage,
|
||||
color: widget.isDeviceMatched ? _tileAccent : AppPalette.sage,
|
||||
onTap: widget.isDeviceMatched
|
||||
? () => _openPocketCheckIn(context)
|
||||
: () => AppNotice.show(
|
||||
@@ -330,7 +331,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.fastfood_rounded,
|
||||
title: '실시간 학교 상황',
|
||||
subtitle: '급식실 줄 & 매점 재고 확인',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => AppNotice.show(
|
||||
context,
|
||||
'실시간 학교 상황 페이지로 이동합니다.',
|
||||
@@ -344,7 +345,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.tablet_mac_rounded,
|
||||
title: '스마트기기 반출',
|
||||
subtitle: '패드 사용 신청 (시간/목적)',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => pushLaunchpad(
|
||||
context,
|
||||
(context) => DeviceCheckoutRequestScreen(
|
||||
@@ -360,7 +361,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.timer_rounded,
|
||||
title: '백품타',
|
||||
subtitle: '공부 타이머 | 학년별 공부 랭킹',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => pushLaunchpad(
|
||||
context,
|
||||
(context) => StudyTimerScreen(
|
||||
@@ -377,7 +378,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.forum_rounded,
|
||||
title: '백판',
|
||||
subtitle: '익명 커뮤니티 게시판',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => pushLaunchpad(
|
||||
context,
|
||||
(context) => BoardHomeScreen(studentId: _displayId),
|
||||
@@ -390,7 +391,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.campaign_rounded,
|
||||
title: '선생님 호출',
|
||||
subtitle: '교무실 위치 확인 및 호출',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => pushLaunchpad(
|
||||
context,
|
||||
(context) => TeacherCallScreen(
|
||||
@@ -406,7 +407,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.people_alt_rounded,
|
||||
title: '친구',
|
||||
subtitle: '학번으로 친구 추가',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => pushLaunchpad(
|
||||
context,
|
||||
(context) =>
|
||||
@@ -420,7 +421,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.groups_rounded,
|
||||
title: '그룹 스터디',
|
||||
subtitle: '친구랑 그룹 만들어 같이 공부',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => pushLaunchpad(
|
||||
context,
|
||||
(context) => GroupListScreen(
|
||||
@@ -437,7 +438,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.calendar_month_rounded,
|
||||
title: '출석 달력',
|
||||
subtitle: '연속 출석 기록 확인',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => pushLaunchpad(
|
||||
context,
|
||||
(context) => StudyCalendarScreen(studentId: _displayId),
|
||||
@@ -453,7 +454,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.assignment_turned_in_rounded,
|
||||
title: '실시간 출석 확인',
|
||||
subtitle: '학생 제출 로그 모니터링',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => pushLaunchpad(
|
||||
context,
|
||||
(context) => const TeacherAttendancePage(),
|
||||
@@ -466,7 +467,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.manage_accounts_rounded,
|
||||
title: '학생 계정 관리',
|
||||
subtitle: '계정 추가 및 강제 리셋',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => pushLaunchpad(
|
||||
context,
|
||||
(context) => const TeacherStudentManagementPage(),
|
||||
@@ -479,7 +480,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.tablet_mac_rounded,
|
||||
title: '스마트기기 반출 대장',
|
||||
subtitle: '패드 반출 신청 승인/거절',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => pushLaunchpad(
|
||||
context,
|
||||
(context) => DeviceCheckoutLedgerPage(
|
||||
@@ -495,7 +496,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.shield_outlined,
|
||||
title: '백판 신고 검토',
|
||||
subtitle: '학생 게시판 신고 처리',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => pushLaunchpad(
|
||||
context,
|
||||
(context) => BoardReportScreen(reviewerId: _displayId),
|
||||
@@ -508,7 +509,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.event_available_rounded,
|
||||
title: '출석 달력 관리',
|
||||
subtitle: '날짜별 선물/이벤트 등록',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => pushLaunchpad(
|
||||
context,
|
||||
(context) => StudyCalendarScreen(
|
||||
@@ -524,7 +525,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.my_location_rounded,
|
||||
title: '내 위치 알리기',
|
||||
subtitle: '학생 호출 확인 및 위치 등록',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => pushLaunchpad(
|
||||
context,
|
||||
(context) => TeacherLocationScreen(teacherId: _displayId),
|
||||
@@ -540,7 +541,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.terminal_rounded,
|
||||
title: '서버 DB 제어',
|
||||
subtitle: '시스템 원격 초기화',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () =>
|
||||
pushLaunchpad(context, (context) => const AdminDashboard()),
|
||||
),
|
||||
@@ -551,7 +552,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.delete_sweep_rounded,
|
||||
title: '계정 강제 삭제',
|
||||
subtitle: '학생 및 교사 DB 삭제',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () => _showDeleteUserDialog(context),
|
||||
),
|
||||
));
|
||||
@@ -561,7 +562,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
icon: Icons.edit_note_rounded,
|
||||
title: 'NFC 태그 쓰기',
|
||||
subtitle: '주머니 스티커 초기 설정',
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
onTap: () =>
|
||||
pushLaunchpad(context, (context) => const NfcTagWriterScreen()),
|
||||
),
|
||||
@@ -623,7 +624,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
final bool isEditing = _layout.isEditing;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppPalette.mist,
|
||||
backgroundColor: _equippedTheme?.background ?? AppPalette.mist,
|
||||
// 🪶 큰 색상 배너 대신 작은 프로필 카드를 본문에 두므로, 앱바는 ">" 메뉴
|
||||
// 버튼(또는 편집 중 "완료" 버튼)만 올려두는 투명한 얇은 바로 둔다.
|
||||
appBar: AppBar(
|
||||
@@ -672,21 +673,21 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
vertical: 18,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: _equippedPrimary == null ? theme.color : null,
|
||||
gradient: _equippedPrimary == null
|
||||
color: _equippedTheme == null ? theme.color : null,
|
||||
gradient: _equippedTheme == null
|
||||
? null
|
||||
: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
_equippedPrimary!,
|
||||
_equippedSecondary!,
|
||||
_equippedTheme!.primary,
|
||||
_equippedTheme!.secondary,
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: (_equippedPrimary ?? theme.color)
|
||||
color: (_equippedTheme?.primary ?? theme.color)
|
||||
.withValues(alpha: 0.25),
|
||||
blurRadius: 16,
|
||||
offset: const Offset(0, 6),
|
||||
@@ -959,7 +960,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
width: 26,
|
||||
height: 26,
|
||||
decoration: BoxDecoration(
|
||||
color: AppPalette.ink,
|
||||
color: _tileAccent,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: AppPalette.paper,
|
||||
@@ -1026,9 +1027,12 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
child: Ink(
|
||||
padding: EdgeInsets.all(cardPadding),
|
||||
decoration: BoxDecoration(
|
||||
color: AppPalette.paper,
|
||||
color: _equippedTheme?.surface ?? AppPalette.paper,
|
||||
borderRadius: BorderRadius.circular(cardRadius),
|
||||
border: Border.all(color: AppPalette.sage, width: 1),
|
||||
border: Border.all(
|
||||
color: _equippedTheme?.border ?? AppPalette.sage,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -1037,7 +1041,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
Container(
|
||||
padding: EdgeInsets.all(iconBoxPadding),
|
||||
decoration: BoxDecoration(
|
||||
color: AppPalette.linen,
|
||||
color: _equippedTheme?.surfaceAlt ?? AppPalette.linen,
|
||||
borderRadius: BorderRadius.circular(iconRadius),
|
||||
),
|
||||
child: Icon(icon, color: color, size: iconSize),
|
||||
@@ -1056,7 +1060,9 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
style: TextStyle(
|
||||
fontSize: titleFontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppPalette.ink,
|
||||
color:
|
||||
_equippedTheme?.onBackground ??
|
||||
AppPalette.ink,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1081,7 +1087,8 @@ class _MainDashboardState extends State<MainDashboard> {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: subtitleFontSize,
|
||||
color: AppPalette.ink.withValues(alpha: 0.55),
|
||||
color: (_equippedTheme?.onBackground ?? AppPalette.ink)
|
||||
.withValues(alpha: 0.55),
|
||||
height: 1.15,
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user