프로필 꾸미기 상점 추가 (1단계: 기본 프로필 테마) - 공부시간을 캐시로 써서 구매/장착

백엔드에 새로 생긴 /api/shop/* 엔드포인트를 붙여서 프로필 커스텀 화면을
실제 상점으로 채웠다. 랭킹/친구창 배경, 출석/그룹창 표시 카테고리는
와이어프레임 자리만 잡아둔 준비중 상태 - 1단계는 기본 프로필 테마만 동작.
장착한 테마는 메인 대시보드 프로필 카드 배경에 바로 반영된다.
학생이 직접 템플릿을 만들어 파는 기능은 이후 단계로 보류.
This commit is contained in:
2026-09-30 11:17:56 +00:00
parent 9fe3e61c1c
commit 705c0c086a
3 changed files with 574 additions and 28 deletions
+42 -9
View File
@@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import '../function/dashboard_layout_controller.dart';
import '../function/profile_shop_controller.dart';
import '../function/session_store.dart';
import '../function/student_dashboard_controller.dart';
import '../models/dashboard_tile_layout.dart';
@@ -79,6 +80,23 @@ class _MainDashboardState extends State<MainDashboard> {
if (kTileEditingEnabled) {
_layout.load(_tileSpecs().map((t) => t.id).toList());
}
if (_isStudent) {
_loadEquippedTheme();
}
}
// 🛍️ 프로필 꾸미기 상점에서 장착한 테마 색상 (없으면 기본 모습 그대로).
Color? _equippedPrimary;
Color? _equippedSecondary;
Future<void> _loadEquippedTheme() async {
final (primary, secondary) =
await ProfileShopController.fetchEquippedColors(_displayId);
if (!mounted) return;
setState(() {
_equippedPrimary = primary;
_equippedSecondary = secondary;
});
}
@override
@@ -645,11 +663,22 @@ class _MainDashboardState extends State<MainDashboard> {
vertical: 18,
),
decoration: BoxDecoration(
color: theme.color,
color: _equippedPrimary == null ? theme.color : null,
gradient: _equippedPrimary == null
? null
: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
_equippedPrimary!,
_equippedSecondary!,
],
),
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: theme.color.withValues(alpha: 0.25),
color: (_equippedPrimary ?? theme.color)
.withValues(alpha: 0.25),
blurRadius: 16,
offset: const Offset(0, 6),
),
@@ -715,13 +744,17 @@ class _MainDashboardState extends State<MainDashboard> {
elevation: 2,
child: InkWell(
customBorder: const CircleBorder(),
onTap: () => pushLaunchpad(
context,
(context) => ProfileCustomizeScreen(
studentId: _displayId,
studentName: _displayName,
),
),
onTap: () async {
await pushLaunchpad(
context,
(context) => ProfileCustomizeScreen(
studentId: _displayId,
studentName: _displayName,
),
);
// 상점에서 테마를 바꾸고 돌아왔을 수 있으니 다시 불러온다.
if (_isStudent) _loadEquippedTheme();
},
child: const Padding(
padding: EdgeInsets.all(8),
child: Icon(