대시보드 배너/타이틀 중앙정렬 + 타일 UI 편집(드래그 이동/크기조절) + 로그인 유지 기능 추가

- 이름/부제, "스마트 관리 시스템 메뉴" 제목을 중앙 정렬
- 타일을 1x1~4x4 크기로 자유 배치할 수 있는 편집 모드 추가 (6x4 캔버스, 삼성 One UI 위젯 편집 방식)
- 타일 배치는 계정별로 서버에 저장/복원 (dashboard_layout_controller.dart)
- 설정 화면(톱니바퀴 아이콘) > "UI 편집" 진입점 추가
- shared_preferences로 로그인 유지 기능 추가 (비밀번호는 저장하지 않음)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-07 22:18:16 +09:00
co-authored by Claude Sonnet 5
parent 83c148186d
commit 7495ece0c8
11 changed files with 967 additions and 269 deletions
+52 -1
View File
@@ -6,9 +6,11 @@ import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
import 'config.dart';
import 'function/session_store.dart';
import 'pocket_watch_service.dart';
import 'theme/app_palette.dart';
import 'ui/login_screen.dart';
import 'ui/main_dashboard.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -44,7 +46,56 @@ class SchoolAttendanceApp extends StatelessWidget {
onSurface: AppPalette.ink,
),
),
home: const LoginScreen(), // 🚪 앱을 켜면 무조건 로그인 화면이 먼저 등장합니다.
home: const _StartupGate(), // 🚪 저장된 로그인이 있으면 대시보드로, 없으면 로그인 화면으로.
);
}
}
/// 🔐 "로그인 유지" 진입점. 기기에 저장된 세션이 있는지 확인하는 동안 잠깐 로딩을 보여주고,
/// 있으면 로그인 화면 없이 바로 대시보드로, 없으면 로그인 화면으로 보낸다.
class _StartupGate extends StatefulWidget {
const _StartupGate();
@override
State<_StartupGate> createState() => _StartupGateState();
}
class _StartupGateState extends State<_StartupGate> {
SavedSession? _session;
bool _checked = false;
@override
void initState() {
super.initState();
_checkSession();
}
Future<void> _checkSession() async {
final session = await SessionStore.load();
if (!mounted) return;
setState(() {
_session = session;
_checked = true;
});
}
@override
Widget build(BuildContext context) {
if (!_checked) {
return Scaffold(
backgroundColor: AppPalette.mist,
body: const Center(child: CircularProgressIndicator()),
);
}
final session = _session;
if (session != null) {
return MainDashboard(
userId: session.userId,
userName: session.userName,
role: session.role,
isDeviceMatched: session.isDeviceMatched,
);
}
return const LoginScreen();
}
}