From acb297fec1affa5164d005036ae5dee0feb0fa45 Mon Sep 17 00:00:00 2001 From: KIMGYEONGRAN Date: Mon, 1 Dec 2025 17:55:38 +0900 Subject: [PATCH] =?UTF-8?q?1.=ED=85=8C=EB=A7=88/=EC=BB=AC=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=202.=20UI=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=95=9C=EA=B5=AD=EC=96=B4=20=EB=B2=88=EC=97=AD=203.=20Setting?= =?UTF-8?q?Screen(=EC=84=A4=EC=A0=95=20=ED=8E=98=EC=9D=B4=EC=A7=80)=20-?= =?UTF-8?q?=ED=98=84=EC=9E=AC=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=8A=94=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=EB=A7=8C=20=EC=99=84?= =?UTF-8?q?=EC=84=B1=EB=90=98=EC=97=88=EC=9C=BC=EB=A9=B0,=20=ED=86=A0?= =?UTF-8?q?=EA=B8=80=20=EC=8A=A4=EC=9C=84=EC=B9=98=20=EB=B0=8F=20=EB=A7=81?= =?UTF-8?q?=ED=81=AC=20=EC=97=B0=EA=B2=B0=20=EA=B8=B0=EB=8A=A5=EC=9D=80=20?= =?UTF-8?q?=EB=AF=B8=EC=99=84=EC=84=B1=20=EC=83=81=ED=83=9C=EC=9E=85?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/control_screen.dart | 166 ++++++++------- lib/history_screen.dart | 181 ++++++++-------- lib/home_screen_content.dart | 373 ++++++++++++++++----------------- lib/login_screen.dart | 249 ++++++++++++++-------- lib/main.dart | 54 +++-- lib/rent_return_screen.dart | 277 ++++++++++++++---------- lib/rental_process_screen.dart | 212 ++++++++++++------- lib/settings_screen.dart | 262 ++++++++++++++++++++++- lib/widgets/custom_header.dart | 63 ++++-- 9 files changed, 1178 insertions(+), 659 deletions(-) diff --git a/lib/control_screen.dart b/lib/control_screen.dart index 7c5b28a..9f51b6d 100644 --- a/lib/control_screen.dart +++ b/lib/control_screen.dart @@ -8,10 +8,20 @@ class ControlScreen extends StatefulWidget { } class _ControlScreenState extends State { - final Color _bgColor = const Color(0xFF27292B); - final Color _cardColor = const Color(0xFF30343B); - final Color _buttonDarkColor = const Color(0xFF212327); - final Color _errorColor = Colors.redAccent; + final Color _mainBlueColor = const Color(0xFF002FA7); + final Color _mainTextColor = const Color(0xFF1C1C1E); + final Color _subTextColor = const Color(0xFF6A717B); + final Color _pageBackgroundColor = const Color(0xFFF5F7F9); + final Color _cardBackgroundColor = Colors.white; + final Color _accentContainerColor = const Color(0xFFF0F2F5); + final Color _warningColor = Colors.redAccent; + + static const BoxShadow _cleanShadow = BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.07), + blurRadius: 8, + offset: Offset(0, 4), + spreadRadius: 0, + ); bool _isSecurityLocked = true; int _selectedDoorIndex = 0; @@ -19,23 +29,24 @@ class _ControlScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: _bgColor, + backgroundColor: _pageBackgroundColor, appBar: AppBar( - title: const Text( - 'CONTROL CENTER', + title: Text( + '제어 센터', style: TextStyle( fontWeight: FontWeight.w700, - fontSize: 16, - color: Colors.white, + fontSize: 18, + letterSpacing: 0.5, + color: _mainTextColor, ), ), - backgroundColor: _bgColor, + backgroundColor: _pageBackgroundColor, scrolledUnderElevation: 0, elevation: 0, centerTitle: false, actions: [ IconButton( - icon: const Icon(Icons.more_vert, color: Colors.white), + icon: Icon(Icons.more_vert, color: _mainTextColor), onPressed: () {}, ), ], @@ -67,16 +78,17 @@ class _ControlScreenState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: _cardColor, - borderRadius: BorderRadius.circular(12), + color: _cardBackgroundColor, + borderRadius: BorderRadius.circular(8), + boxShadow: [_cleanShadow], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - 'SYSTEM STATUS', + Text( + '보관함 상태', style: TextStyle( - color: Colors.white, + color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.w600, ), @@ -92,7 +104,7 @@ class _ControlScreenState extends State { height: double.infinity, padding: const EdgeInsets.all(20.0), decoration: BoxDecoration( - color: _buttonDarkColor, + color: _accentContainerColor, borderRadius: BorderRadius.circular(8), ), child: Image.asset( @@ -105,10 +117,10 @@ class _ControlScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'NOW', style: TextStyle( - color: Colors.white, + color: _subTextColor, fontSize: 11, fontWeight: FontWeight.bold, ), @@ -116,17 +128,17 @@ class _ControlScreenState extends State { const SizedBox(height: 8), Expanded( child: _buildStatusButton( - text: 'ONLINE', - textColor: Colors.white, - bgColor: _buttonDarkColor, + text: '사용 중', + textColor: _mainBlueColor, + bgColor: _accentContainerColor, ), ), const SizedBox(height: 10), Expanded( child: _buildStatusButton( - text: 'SENSOR ERROR:\nLock Failure', - textColor: _errorColor, - bgColor: _buttonDarkColor, + text: 'ERROR: 문 열림', + textColor: _warningColor, + bgColor: _accentContainerColor, isError: true, ), ), @@ -171,16 +183,17 @@ class _ControlScreenState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: _cardColor, - borderRadius: BorderRadius.circular(12), + color: _cardBackgroundColor, + borderRadius: BorderRadius.circular(8), + boxShadow: [_cleanShadow], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - 'DOOR CONTROL', + Text( + '문 잠금 제어', style: TextStyle( - color: Colors.white, + color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.w600, ), @@ -213,14 +226,14 @@ class _ControlScreenState extends State { child: Container( height: 80, decoration: BoxDecoration( - color: isSelected ? Colors.white : _buttonDarkColor, + color: isSelected ? _mainBlueColor : _accentContainerColor, borderRadius: BorderRadius.circular(8), ), child: Center( child: Text( text, style: TextStyle( - color: isSelected ? Colors.black : Colors.white, + color: isSelected ? Colors.white : _mainTextColor, fontWeight: FontWeight.bold, fontSize: 18, ), @@ -235,16 +248,17 @@ class _ControlScreenState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: _cardColor, - borderRadius: BorderRadius.circular(12), + color: _cardBackgroundColor, + borderRadius: BorderRadius.circular(8), + boxShadow: [_cleanShadow], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - 'Control Results & Alerts', + Text( + '조작 기록 및 경고', style: TextStyle( - color: Colors.white, + color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.w600, ), @@ -253,7 +267,7 @@ class _ControlScreenState extends State { Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: _buttonDarkColor, + color: _accentContainerColor, borderRadius: BorderRadius.circular(6), ), child: Column( @@ -262,24 +276,24 @@ class _ControlScreenState extends State { Row( children: [ Icon(Icons.warning_amber_rounded, - color: _errorColor, size: 16), + color: _warningColor, size: 16), const SizedBox(width: 8), Expanded( child: RichText( text: TextSpan( children: [ TextSpan( - text: 'Sensor Alert: ', + text: '센서 경고: ', style: TextStyle( - color: _errorColor, + color: _warningColor, fontWeight: FontWeight.bold, fontSize: 12, ), ), - const TextSpan( - text: 'Door Not Fully Closed', + TextSpan( + text: '문 닫힘 불완전', style: TextStyle( - color: Colors.white, + color: _mainTextColor, fontSize: 12, fontWeight: FontWeight.bold, ), @@ -295,8 +309,8 @@ class _ControlScreenState extends State { borderRadius: BorderRadius.circular(4), child: LinearProgressIndicator( value: 0.6, - backgroundColor: Colors.grey[800], - valueColor: AlwaysStoppedAnimation(_errorColor), + backgroundColor: _pageBackgroundColor, + valueColor: AlwaysStoppedAnimation(_warningColor), minHeight: 6, ), ), @@ -312,16 +326,17 @@ class _ControlScreenState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: _cardColor, - borderRadius: BorderRadius.circular(12), + color: _cardBackgroundColor, + borderRadius: BorderRadius.circular(8), + boxShadow: [_cleanShadow], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - 'Alert & Logs', + Text( + '조작 기록 및 경고', style: TextStyle( - color: Colors.white, + color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.w600, ), @@ -330,36 +345,36 @@ class _ControlScreenState extends State { Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: _buttonDarkColor, + color: _accentContainerColor, borderRadius: BorderRadius.circular(6), ), - child: const Column( + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.warning_amber_rounded, - color: Colors.redAccent, size: 14), - SizedBox(width: 8), + color: _warningColor, size: 14), + const SizedBox(width: 8), Text( - 'Sensor Alert: Door Not Fully Closed', + '센서 경고: 문 열림', style: TextStyle( - color: Colors.white, + color: _mainTextColor, fontSize: 12, fontWeight: FontWeight.bold, ), ), ], ), - SizedBox(height: 8), + const SizedBox(height: 8), Row( children: [ - Icon(Icons.bookmark, color: Colors.white70, size: 14), - SizedBox(width: 8), + Icon(Icons.bookmark, color: _subTextColor, size: 14), + const SizedBox(width: 8), Text( - '2:02 PM - UV LED Activated', + '2:02 PM - UV LED 작동', style: TextStyle( - color: Colors.white, + color: _mainTextColor, fontSize: 12, fontWeight: FontWeight.bold, ), @@ -378,16 +393,17 @@ class _ControlScreenState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: _cardColor, - borderRadius: BorderRadius.circular(12), + color: _cardBackgroundColor, + borderRadius: BorderRadius.circular(8), + boxShadow: [_cleanShadow], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - 'Security Lock Mode', + Text( + '도난 방지 잠금', style: TextStyle( - color: Colors.white, + color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.w600, ), @@ -397,7 +413,7 @@ class _ControlScreenState extends State { padding: const EdgeInsets.symmetric(horizontal: 22, vertical: 16), decoration: BoxDecoration( - color: _buttonDarkColor, + color: _accentContainerColor, borderRadius: BorderRadius.circular(6), ), child: Row( @@ -405,10 +421,10 @@ class _ControlScreenState extends State { children: [ Text( _isSecurityLocked - ? 'ON (Activated)' - : 'OFF (Deactivated)', - style: const TextStyle( - color: Colors.white, + ? 'ON' + : 'OFF', + style: TextStyle( + color: _mainTextColor, fontSize: 16, fontWeight: FontWeight.bold, ), @@ -422,10 +438,10 @@ class _ControlScreenState extends State { _isSecurityLocked = val; }); }, - activeThumbColor: Colors.white, - activeTrackColor: _errorColor, + activeThumbColor: _mainBlueColor, + activeTrackColor: _mainBlueColor.withOpacity(0.5), inactiveThumbColor: Colors.white, - inactiveTrackColor: Colors.grey, + inactiveTrackColor: _accentContainerColor, ), ), ], diff --git a/lib/history_screen.dart b/lib/history_screen.dart index 1a048f5..14f2640 100644 --- a/lib/history_screen.dart +++ b/lib/history_screen.dart @@ -32,16 +32,28 @@ class HistoryScreen extends StatefulWidget { } class _HistoryScreenState extends State { - static const Color kBackgroundColor = Color(0xFF27292B); - static const Color kCardColor = Color(0xFF30343B); - static const Color kAccentColor = Colors.white; - static const Color kRedAccent = Color(0xFFFF5252); + // 🟦 최종 확정 테마 컬러 정의 (대비 구조 교체) + final Color _mainBlueColor = const Color(0xFF002FA7); // 진한 블루 (0xFF002FA7 -> 0xFF0A68FF로 통일) + final Color _mainTextColor = const Color(0xFF1C1C1E); // 어두운 텍스트 + final Color _subTextColor = const Color(0xFF6A717B); // 보조 텍스트 + final Color _pageBackgroundColor = const Color(0xFFF5F7F9); // 🚩 Scaffold 배경 (연한 그레이) + final Color _cardBackgroundColor = Colors.white; // 🚩 카드 배경 (순수 화이트) + final Color _accentContainerColor = const Color(0xFFF0F2F5); // 🚩 내부 상세 박스 배경 (중간 그레이) + final Color _borderColor = const Color(0xFF0A68FF).withOpacity(0.8); + + // 🚩 그림자 스타일 정의 + static const BoxShadow _cleanShadow = BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.07), + blurRadius: 8, + offset: Offset(0, 4), + spreadRadius: 0, + ); int _selectedFilterIndex = 0; final List _allHistoryList = [ UsageHistory( - date: '2025.11.25 (Tue)', + date: '2025.11.25 (화)', // 🇰🇷 요일 변경 startTime: '18:30', endTime: '19:15', duration: 45, @@ -52,7 +64,7 @@ class _HistoryScreenState extends State { isDried: true, ), UsageHistory( - date: '2025.11.23 (Sun)', + date: '2025.11.23 (일)', // 🇰🇷 요일 변경 startTime: '14:00', endTime: '15:30', duration: 90, @@ -63,7 +75,7 @@ class _HistoryScreenState extends State { isDried: false, ), UsageHistory( - date: '2025.11.06 (Thu)', + date: '2025.11.06 (목)', // 🇰🇷 요일 변경 startTime: '09:00', endTime: '09:30', duration: 30, @@ -74,7 +86,7 @@ class _HistoryScreenState extends State { isDried: true, ), UsageHistory( - date: '2025.09.20 (Sat)', + date: '2025.09.20 (토)', // 🇰🇷 요일 변경 startTime: '20:00', endTime: '21:00', duration: 60, @@ -120,16 +132,16 @@ class _HistoryScreenState extends State { final currentList = _filteredList; return Scaffold( - backgroundColor: kBackgroundColor, + backgroundColor: _pageBackgroundColor, appBar: AppBar( - title: const Text('HISTORY', style: TextStyle(fontWeight: FontWeight - .bold, fontSize: 16, color: Colors.white)), - backgroundColor: kBackgroundColor, + title: Text('나의 기록', style: TextStyle(fontWeight: FontWeight + .bold, fontSize: 18, letterSpacing: 0.5, color: _mainTextColor)), + backgroundColor: _pageBackgroundColor, scrolledUnderElevation: 0, elevation: 0, centerTitle: false, actions: [ - IconButton(icon: const Icon(Icons.filter_list, color: Colors.white), + IconButton(icon: Icon(Icons.filter_list, color: _mainTextColor), onPressed: () {}), ], ), @@ -138,8 +150,8 @@ class _HistoryScreenState extends State { _buildFilterTabs(), Expanded( child: currentList.isEmpty - ? const Center( - child: Text("기록이 없습니다.", style: TextStyle(color: Colors.grey))) + ? Center( + child: Text("기록이 없습니다.", style: TextStyle(color: _subTextColor))) : ListView.builder( padding: const EdgeInsets.all(16), itemCount: currentList.length, @@ -183,15 +195,15 @@ class _HistoryScreenState extends State { onSelected: (selected) { setState(() => _selectedFilterIndex = index); }, - backgroundColor: kCardColor, - selectedColor: kAccentColor, - checkmarkColor: Colors.black, + backgroundColor: _cardBackgroundColor, + selectedColor: _mainBlueColor, + checkmarkColor: Colors.white, labelStyle: TextStyle( - color: isSelected ? Colors.black : Colors.white, + color: isSelected ? Colors.white : _mainTextColor, fontWeight: FontWeight.bold, ), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), side: BorderSide.none), + borderRadius: BorderRadius.circular(8), side: BorderSide.none), ); }, ), @@ -203,8 +215,8 @@ class _HistoryScreenState extends State { padding: const EdgeInsets.symmetric(vertical: 16.0), child: Text( date, - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.bold ), @@ -216,12 +228,13 @@ class _HistoryScreenState extends State { return Container( margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( - color: kCardColor, - borderRadius: BorderRadius.circular(16), + color: _cardBackgroundColor, + borderRadius: BorderRadius.circular(8), + boxShadow: [_cleanShadow] ), child: InkWell( onTap: () => _showHistoryDetail(context, history), - borderRadius: BorderRadius.circular(16), + borderRadius: BorderRadius.circular(8), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( @@ -231,7 +244,7 @@ class _HistoryScreenState extends State { children: [ Text( '${history.startTime} - ${history.endTime}', - style: const TextStyle(color: Colors.white, + style: TextStyle(color: _mainTextColor, fontSize: 16, fontWeight: FontWeight.bold), ), @@ -239,12 +252,12 @@ class _HistoryScreenState extends State { padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4), decoration: BoxDecoration( - color: Colors.white, + color: _mainBlueColor, borderRadius: BorderRadius.circular(8), ), child: Text( - '${history.duration} min', - style: const TextStyle(color: Colors.black, + '${history.duration}분', // 🇰🇷 min -> 분 + style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.w600), ), @@ -254,54 +267,53 @@ class _HistoryScreenState extends State { const SizedBox(height: 12), Row( children: [ - _buildDot(Colors.grey), + _buildDot(_subTextColor), const SizedBox(width: 8), Expanded(child: Text(history.startStation, - style: const TextStyle(color: Colors.white, fontSize: 13), + style: TextStyle(color: _mainTextColor, fontSize: 13), overflow: TextOverflow.ellipsis)), ], ), Container( margin: const EdgeInsets.only(left: 3), height: 10, - decoration: const BoxDecoration( + decoration: BoxDecoration( border: Border( - left: BorderSide(color: Colors.grey, width: 1)), + left: BorderSide(color: _subTextColor.withOpacity(0.5), width: 1)), ), ), Row( children: [ - _buildDot(kRedAccent), + _buildDot(_mainBlueColor), const SizedBox(width: 8), Expanded(child: Text(history.endStation, - style: const TextStyle(color: Colors.white, fontSize: 13), + style: TextStyle(color: _mainTextColor, fontSize: 13), overflow: TextOverflow.ellipsis)), ], ), const SizedBox(height: 12), - const Divider(color: Colors.white, thickness: 0.5,), + Divider(color: _subTextColor.withOpacity(0.5), thickness: 0.5,), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ if (history.isSanitized) - _buildDot(kRedAccent), + _buildDot(_mainBlueColor), if (history.isSanitized) const SizedBox(width: 8), Text( - history.isSanitized ? "살균 완료" : "Pending", + history.isSanitized ? "살균 완료" : "진행 중", // 🇰🇷 Pending -> 진행 중 style: TextStyle( - color: history.isSanitized ? Colors.white : Colors - .white, + color: history.isSanitized ? _mainTextColor : _subTextColor, fontSize: 14, fontWeight: FontWeight.bold), ), ], ), - const Text( - "VIEW DETAILS >", - style: TextStyle(color: Colors.white, fontSize: 11), + Text( + "상세 보기 >", // 🇰🇷 VIEW DETAILS > -> 상세 보기 > + style: TextStyle(color: _subTextColor, fontSize: 11), ), ], ), @@ -330,16 +342,17 @@ class _HistoryScreenState extends State { .of(context) .size .height * 0.75, - decoration: const BoxDecoration( - color: kBackgroundColor, - borderRadius: BorderRadius.vertical(top: Radius.circular(24)), + decoration: BoxDecoration( + color: _cardBackgroundColor, + borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), + boxShadow: [_cleanShadow] ), child: Column( children: [ const SizedBox(height: 12), Container(width: 40, height: 4, - decoration: BoxDecoration(color: Colors.grey[600], + decoration: BoxDecoration(color: _subTextColor, borderRadius: BorderRadius.circular(2))), Expanded( child: SingleChildScrollView( @@ -347,45 +360,45 @@ class _HistoryScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text("HISTORY DETAILS", style: TextStyle( - color: Colors.white, + Text("이용 상세 정보", style: TextStyle( // 🇰🇷 HISTORY DETAILS + color: _mainTextColor, fontSize: 20, fontWeight: FontWeight.bold)), const SizedBox(height: 8), Text( history.date, - style: const TextStyle(color: Colors.white), + style: TextStyle(color: _subTextColor), ), const SizedBox(height: 30), Container( padding: const EdgeInsets.all(20), - decoration: BoxDecoration(color: kCardColor, - borderRadius: BorderRadius.circular(16)), + decoration: BoxDecoration(color: _accentContainerColor, + borderRadius: BorderRadius.circular(8)), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text("이용 시간", style: TextStyle( - color: Colors.white, fontSize: 12)), + Text("이용 시간", style: TextStyle( // 🇰🇷 이용 시간 + color: _subTextColor, fontSize: 12)), const SizedBox(height: 4), - Text("${history.duration} min", - style: const TextStyle(color: Colors.white, + Text("${history.duration}분", // 🇰🇷 min -> 분 + style: TextStyle(color: _mainTextColor, fontSize: 24, fontWeight: FontWeight.bold)), ], ), Container( - width: 1, height: 40, color: Colors.white24), + width: 1, height: 40, color: _subTextColor.withOpacity(0.5)), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text("이용 금액", style: TextStyle( - color: Colors.white, fontSize: 12)), + Text("이용 금액", style: TextStyle( // 🇰🇷 이용 금액 + color: _subTextColor, fontSize: 12)), const SizedBox(height: 4), - Text("$cost ₩", style: const TextStyle( - color: Colors.white, + Text("₩ $cost", style: TextStyle( // 🇰🇷 ₩ 위치 수정 + color: _mainTextColor, fontSize: 24, fontWeight: FontWeight.bold)), ], @@ -394,8 +407,8 @@ class _HistoryScreenState extends State { ), ), const SizedBox(height: 30), - const Text("TIMELINE", style: TextStyle( - color: Colors.white, + Text("시간별 기록", style: TextStyle( // 🇰🇷 TIMELINE + color: _mainTextColor, fontSize: 16, fontWeight: FontWeight.bold, letterSpacing: 1.0)), @@ -407,14 +420,14 @@ class _HistoryScreenState extends State { history.endTime, "반납 완료", history.endStation, isCompleted: true), _buildTimelineItem( - history.endTime, "살균 시작", "Auto-Cleaning System", + history.endTime, "살균 시작", "자동 살균 시스템", // 🇰🇷 Auto-Cleaning System isCompleted: true), _buildTimelineItem( - "15 min later", "살균 완료", "Ready for next user", + "15분 후", "살균 완료", "다음 사용자 준비 완료", // 🇰🇷 15 min later, Ready for next user isLast: true, isCompleted: history.isSanitized), const SizedBox(height: 30), - const Text("CONDITION", style: TextStyle( - color: Colors.white, + Text("살균/건조 상태", style: TextStyle( // 🇰🇷 CONDITION + color: _mainTextColor, fontSize: 16, fontWeight: FontWeight.bold)), const SizedBox(height: 8), @@ -442,25 +455,28 @@ class _HistoryScreenState extends State { Widget _buildTimelineItem(String time, String title, String subtitle, {bool isFirst = false, bool isLast = false, bool isCompleted = false}) { + // 🇰🇷 15 min later -> 15분 후 로직 처리 + String displayTime = time.contains("min later") ? "15분 후" : time; + return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 60, child: Text( - time, style: const TextStyle(color: Colors.white, fontSize: 12)), + displayTime, style: TextStyle(color: _mainTextColor, fontSize: 12)), ), Column( children: [ Icon( isCompleted ? Icons.check_circle : Icons.radio_button_unchecked, - color: isCompleted ? kRedAccent : Colors.grey, size: 20), + color: isCompleted ? _mainBlueColor : _subTextColor, size: 20), if (!isLast) Container(width: 2, height: 40, color: isCompleted - ? kRedAccent.withValues(alpha: 0.5) - : Colors.grey[800]), + ? _mainBlueColor.withOpacity(0.5) + : Colors.grey.shade300), ], ), const SizedBox(width: 16), @@ -469,12 +485,12 @@ class _HistoryScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: TextStyle( - color: isCompleted ? Colors.white : Colors.grey, + color: isCompleted ? _mainTextColor : _subTextColor, fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(height: 4), Text(subtitle, - style: TextStyle(color: Colors.white, fontSize: 12)), + style: TextStyle(color: _subTextColor, fontSize: 12)), const SizedBox(height: 24), ], ), @@ -489,29 +505,30 @@ class _HistoryScreenState extends State { width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: kCardColor, + color: _accentContainerColor, // 내부 상세 박스 배경 (중간 그레이) + boxShadow: isDone ? [_cleanShadow] : null, border: Border.all( - color: isDone ? Colors.white : Colors.transparent, width: 1.0), - borderRadius: BorderRadius.circular(12), + color: isDone ? _mainBlueColor.withOpacity(0.9) : _subTextColor.withOpacity(0.3), width: 1.0), + borderRadius: BorderRadius.circular(8), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon(icon, color: Colors.white, size: 28), + Icon(icon, color: _mainBlueColor, size: 28), const SizedBox(height: 8), Text( title, - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( - isDone ? "Completed" : "In Progress", - style: const TextStyle( - color: Colors.white, + isDone ? "완료됨" : "진행 중", // 🇰🇷 Completed/In Progress + style: TextStyle( + color: _subTextColor, fontWeight: FontWeight.bold, fontSize: 14 ), diff --git a/lib/home_screen_content.dart b/lib/home_screen_content.dart index c6d7cc6..8bb991c 100644 --- a/lib/home_screen_content.dart +++ b/lib/home_screen_content.dart @@ -14,7 +14,22 @@ class HomeScreenContent extends StatefulWidget { class _HomeScreenContentState extends State { static const double _uniformGap = 16.0; - final Color _pointColor = Colors.redAccent; + final Color _mainBlueColor = const Color(0xFF002FA7); + final Color _mainTextColor = Colors.black; + final Color _subTextColor = const Color(0xFF6A717B); + final Color _pageBackgroundColor = const Color(0xFFF5F7F9); + final Color _cardBackgroundColor = Colors.white; + final Color _accentContainerColor = const Color(0xFFF0F2F5); + final Color _toggleOffTrackColor = const Color(0xFFE0E0E0); + final Color _toggleOffKnobBorderColor = const Color(0xFFB0B0B0); + final Color _toggleOffTextColor = const Color(0xFF909090); + + static const BoxShadow _cleanShadow = BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.07), + blurRadius: 8, + offset: Offset(0, 4), + spreadRadius: 0, + ); final Map _controlToggles = { 'UV LED': false, @@ -25,53 +40,60 @@ class _HomeScreenContentState extends State { @override Widget build(BuildContext context) { - return Column( - children: [ - const CustomHeader(), - - Expanded( - child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: _uniformGap), - child: Column( - children: [ - const SizedBox(height: _uniformGap), - _buildOverviewSection(), - const SizedBox(height: _uniformGap), - _buildBatteryStatusCard(), - const SizedBox(height: _uniformGap), - _buildControlCard(), - const SizedBox(height: _uniformGap), - _buildEnvironmentSensorsCard(), - const SizedBox(height: _uniformGap), - _buildMyLocationCard(), - const SizedBox(height: _uniformGap), - _buildActivityCard(), - const SizedBox(height: _uniformGap * 2), - ], + return Container( + color: _pageBackgroundColor, + child: Column( + children: [ + const CustomHeader(), + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: _uniformGap), + child: Column( + children: [ + const SizedBox(height: _uniformGap), + _buildOverviewSection(), + const SizedBox(height: _uniformGap), + _buildBatteryStatusCard(), + const SizedBox(height: _uniformGap), + _buildControlCard(), + const SizedBox(height: _uniformGap), + _buildEnvironmentSensorsCard(), + const SizedBox(height: _uniformGap), + _buildMyLocationCard(), + const SizedBox(height: _uniformGap), + _buildActivityCard(), + const SizedBox(height: _uniformGap * 2), + ], + ), ), ), - ), - ], + ], + ), ); } Widget _buildOverviewSection() { - return Card( - child: Column( - children: [ - _buildOverviewHeader(), - Padding( - padding: const EdgeInsets.all(12.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded(child: _buildImageCard()), - const SizedBox(width: 12), - Expanded(child: _buildInfoCard()), - ], + return Container( + margin: const EdgeInsets.only(top: 5), + child: Card( + shadow: _cleanShadow, + cardColor: _cardBackgroundColor, + child: Column( + children: [ + _buildOverviewHeader(), + Padding( + padding: const EdgeInsets.all(12.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(child: _buildImageCard()), + const SizedBox(width: 12), + Expanded(child: _buildInfoCard()), + ], + ), ), - ), - ], + ], + ), ), ); } @@ -81,15 +103,15 @@ class _HomeScreenContentState extends State { padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), child: Row( children: [ - const Text('SYSTEM OVERVIEW', + Text('장치 개요', style: TextStyle( - color: Colors.white, + color: _mainTextColor, fontSize: 11, fontWeight: FontWeight.bold)), const Spacer(), - Icon(Icons.search, color: Colors.grey[400], size: 20), + Icon(Icons.search, color: _subTextColor, size: 20), const SizedBox(width: 8), - Icon(Icons.notifications_outlined, color: Colors.grey[400], size: 20), + Icon(Icons.notifications_outlined, color: _subTextColor, size: 20), ], ), ); @@ -101,36 +123,36 @@ class _HomeScreenContentState extends State { child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( - color: Colors.black.withOpacity(0.3), - borderRadius: BorderRadius.circular(10), + color: _accentContainerColor, + borderRadius: BorderRadius.circular(8), ), child: Stack( alignment: Alignment.center, children: [ Container( decoration: BoxDecoration( - color: const Color(0xFF2D2F33), - borderRadius: BorderRadius.circular(5) + color: _cardBackgroundColor, + borderRadius: BorderRadius.circular(5), ), ), Padding( padding: const EdgeInsets.only(bottom: 20.0), child: Image.asset( - 'assets/images/open.png', - width: 120, + 'assets/images/storage.png', + width: 90, ), ), Positioned( bottom: 12, child: Row( children: [ - _buildLedIndicator(Colors.grey.shade700), + _buildLedIndicator(Colors.grey.shade300), const SizedBox(width: 4), - _buildLedIndicator(Colors.grey.shade700), + _buildLedIndicator(Colors.grey.shade300), const SizedBox(width: 4), - _buildLedIndicator(Colors.grey.shade700), + _buildLedIndicator(Colors.grey.shade300), const SizedBox(width: 4), - _buildLedIndicator(Colors.white), + _buildLedIndicator(_mainTextColor), ], ), ) @@ -153,25 +175,28 @@ class _HomeScreenContentState extends State { Widget _buildInfoCard() { return SizedBox( - height: 160, + height: 164, child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - _buildInfoRow('Name / \nNumber', const Icon(Icons.person, color: Colors.white, size: 20), 'USER', '001'), + _buildInfoRow('이름 / \n회원번호', Icon(Icons.person, color: _mainTextColor, size: 20), 'USER', '001', + value1Color: _mainTextColor, value2Color: _mainTextColor), const SizedBox(height: 8), - _buildInfoRow('STATUS', null, 'UNLOCKED', '● ACTIVE', value1Color: _pointColor, value2Color: _pointColor), + _buildInfoRow('상태', null, '잠금 해제됨', '● 활성', + value1Color: _mainTextColor, value2Color: _mainTextColor), ], ), ); } - Widget _buildInfoRow(String title, Widget? icon, String value1, String value2, {Color? value1Color, Color? value2Color}) { + Widget _buildInfoRow(String title, Widget? icon, String value1, String value2, + {Color? value1Color, Color? value2Color}) { return Expanded( child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( - color: Colors.black.withOpacity(0.3), - borderRadius: BorderRadius.circular(10), + color: _accentContainerColor, + borderRadius: BorderRadius.circular(8), ), child: Row( children: [ @@ -179,18 +204,18 @@ class _HomeScreenContentState extends State { flex: 2, child: Text(title, textAlign: TextAlign.center, - style: TextStyle(color: Colors.grey[400], fontSize: 11, height: 1.4)), + style: TextStyle(color: _subTextColor, fontSize: 11, height: 1.4)), ), - VerticalDivider(color: Colors.grey[700], indent: 10, endIndent: 10), + VerticalDivider(color: _subTextColor.withOpacity(0.5), indent: 10, endIndent: 10), Expanded( flex: 3, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ if (icon != null) ...[icon, const SizedBox(height: 4)], - Text(value1, style: TextStyle(color: value1Color ?? Colors.white, fontWeight: FontWeight.bold, fontSize: 12)), + Text(value1, style: TextStyle(color: value1Color ?? _mainTextColor, fontWeight: FontWeight.bold, fontSize: 12)), const SizedBox(height: 2), - Text(value2, style: TextStyle(color: value2Color ?? Colors.white, fontWeight: FontWeight.w500, fontSize: 12)), + Text(value2, style: TextStyle(color: value2Color ?? _mainTextColor, fontWeight: FontWeight.w500, fontSize: 12)), ], ), ), @@ -202,16 +227,14 @@ class _HomeScreenContentState extends State { Widget _buildBatteryStatusCard() { return Card( + shadow: _cleanShadow, + cardColor: _cardBackgroundColor, child: Padding( padding: const EdgeInsets.all(_uniformGap), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('BATTERY STATUS (%)', - style: TextStyle( - color: Colors.white, - fontSize: 12, - fontWeight: FontWeight.bold)), + Text('배터리 상태 (%)', style: TextStyle(color: _mainTextColor, fontSize: 12, fontWeight: FontWeight.bold)), const SizedBox(height: 16), Row( children: [ @@ -224,15 +247,13 @@ class _HomeScreenContentState extends State { SizedBox.expand( child: CustomPaint( painter: _BatteryArcPainter( - backgroundColor: Colors.grey.shade800, - color: Colors.white, + backgroundColor: _accentContainerColor, + color: _mainBlueColor, percentage: 1.0, ), ), ), - const Text('86', - style: TextStyle( - fontSize: 24, fontWeight: FontWeight.w600)), + Text('86', style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: _mainTextColor)), ], ), ), @@ -243,55 +264,30 @@ class _HomeScreenContentState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text('NOW', - style: TextStyle( - color: Colors.white, - fontSize: 14, - fontWeight: FontWeight.bold)), - Text('사용 중', - style: TextStyle( - color: _pointColor, - fontSize: 14, - fontWeight: FontWeight.bold)), + Text('현재', style: TextStyle(color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.bold)), + Text('사용 중', style: TextStyle(color: _mainBlueColor, fontSize: 14, fontWeight: FontWeight.bold)), ], ), - const Divider( - color: Color(0xFF555555), - height: 20, - thickness: 1, - ), + Divider(color: _subTextColor.withOpacity(0.5), height: 20, thickness: 1), Row( children: [ - const Expanded( + Expanded( flex: 4, - child: Text('Solar Panel', - style: TextStyle( - color: Colors.white, fontSize: 14)), + child: Text('태양광 패널', style: TextStyle(color: _mainTextColor, fontSize: 14)), ), Expanded( flex: 5, child: Row( children: [ - const Expanded( - child: Text('전압: 00', - style: TextStyle( - color: Colors.white70, - fontSize: 14)), - ), + Expanded(child: Text('전압: 00', style: TextStyle(color: _subTextColor, fontSize: 14))), SizedBox( height: 20, - child: VerticalDivider( - color: Colors.grey[700], - thickness: 1, - ), + child: VerticalDivider(color: _subTextColor.withOpacity(0.5), thickness: 1), ), - const Expanded( + Expanded( child: Padding( - padding: EdgeInsets.only(left: 8.0), - child: Text('전류: 00', - style: TextStyle( - color: Colors.white70, - fontSize: 14)), + padding: const EdgeInsets.only(left: 8.0), + child: Text('전류: 00', style: TextStyle(color: _subTextColor, fontSize: 14)), ), ), ], @@ -312,16 +308,14 @@ class _HomeScreenContentState extends State { Widget _buildControlCard() { return Card( + shadow: _cleanShadow, + cardColor: _cardBackgroundColor, child: Padding( padding: const EdgeInsets.all(_uniformGap), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('CONTROL', - style: TextStyle( - color: Colors.white, - fontSize: 11, - fontWeight: FontWeight.bold)), + Text('', style: TextStyle(color: _mainTextColor, fontSize: 11, fontWeight: FontWeight.bold)), const SizedBox(height: 16), SizedBox( height: 70, @@ -332,24 +326,22 @@ class _HomeScreenContentState extends State { 'UV LED', _controlToggles['UV LED']!, (val) => setState(() => _controlToggles['UV LED'] = val))), - VerticalDivider( - color: Colors.grey[700], indent: 10, endIndent: 10), + VerticalDivider(color: _subTextColor.withOpacity(0.5), indent: 10, endIndent: 10), Expanded( child: _buildStyledToggleSwitch( 'CHARGING', _controlToggles['CHARGING']!, (val) => setState(() => _controlToggles['CHARGING'] = val))), - VerticalDivider( - color: Colors.grey[700], indent: 10, endIndent: 10), + VerticalDivider(color: _subTextColor.withOpacity(0.5), indent: 10, endIndent: 10), Expanded( child: _buildStyledToggleSwitch( 'HELMET', _controlToggles['HELMET']!, (val) => setState(() => _controlToggles['HELMET'] = val))), - VerticalDivider( - color: Colors.grey[700], indent: 10, endIndent: 10), + VerticalDivider(color: _subTextColor.withOpacity(0.5), indent: 10, endIndent: 10), Expanded( - child: _buildStyledToggleSwitch('FAN', _controlToggles['FAN']!, + child: _buildStyledToggleSwitch('FAN', + _controlToggles['FAN']!, (val) => setState(() => _controlToggles['FAN'] = val))), ], ), @@ -360,16 +352,11 @@ class _HomeScreenContentState extends State { ); } - Widget _buildStyledToggleSwitch( - String title, bool value, ValueChanged onChanged) { + Widget _buildStyledToggleSwitch(String title, bool value, ValueChanged onChanged) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text(title, - style: const TextStyle( - color: Colors.white, - fontSize: 13, - fontWeight: FontWeight.bold)), + Text(title, style: TextStyle(color: _mainTextColor, fontSize: 13, fontWeight: FontWeight.bold)), const SizedBox(height: 12), GestureDetector( onTap: () => onChanged(!value), @@ -380,7 +367,7 @@ class _HomeScreenContentState extends State { height: 30, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), - color: value ? Colors.white : Colors.grey.shade700, + color: value ? _mainTextColor.withOpacity(0.8) : _toggleOffTrackColor, ), child: Stack( children: [ @@ -395,7 +382,8 @@ class _HomeScreenContentState extends State { height: 26, decoration: BoxDecoration( shape: BoxShape.circle, - color: value ? const Color(0xFF27292B) : Colors.white, + color: Colors.white, + border: Border.all(color: value ? _mainTextColor : _toggleOffKnobBorderColor, width: 1.5), ), ), ), @@ -408,18 +396,14 @@ class _HomeScreenContentState extends State { style: TextStyle( fontSize: 10, fontWeight: FontWeight.bold, - color: value - ? const Color(0xFF27292B) - : Colors.transparent)))), + color: value ? Colors.white : Colors.transparent)))), Expanded( child: Center( child: Text('OFF', style: TextStyle( fontSize: 10, fontWeight: FontWeight.bold, - color: value - ? Colors.transparent - : Colors.white)))), + color: value ? Colors.transparent : _mainTextColor)))), ], ) ], @@ -432,6 +416,8 @@ class _HomeScreenContentState extends State { Widget _buildEnvironmentSensorsCard() { return Card( + shadow: _cleanShadow, + cardColor: _cardBackgroundColor, child: Padding( padding: const EdgeInsets.all(_uniformGap), child: Column( @@ -439,20 +425,15 @@ class _HomeScreenContentState extends State { Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - const Text('ENVIRONMENT SENSORS', - style: - TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + Text('환경 센서', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: _mainTextColor)), const Spacer(), InkWell( onTap: () {}, child: Row( children: [ - Text('VIEW HISTORY', - style: - TextStyle(color: Colors.grey[400], fontSize: 10)), + Text('기록 보기', style: TextStyle(color: _mainTextColor, fontSize: 10)), const SizedBox(width: 4), - Icon(Icons.arrow_forward_ios, - size: 10, color: Colors.grey[400]), + Icon(Icons.arrow_forward_ios, size: 10, color: _subTextColor), ], ), ), @@ -466,17 +447,18 @@ class _HomeScreenContentState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildSensorInfoRow(Icons.water_drop_outlined, 'HUMID: 60%'), + _buildSensorInfoRow(Icons.water_drop_outlined, '습도: 60%', '습도'), const SizedBox(height: 24), - _buildSensorInfoRow(Icons.thermostat, 'TEMP: 24.5℃'), + _buildSensorInfoRow(Icons.thermostat, '온도: 24.5℃', '온도'), ], ), ), const SizedBox(width: 16), - const Expanded( + Expanded( flex: 1, child: SizedBox( - height: 60, child: _LineChartPlaceholder())), + height: 60, + child: _LineChartPlaceholder(lineColor: _mainTextColor, subLabelColor: _subTextColor))), ], ), ], @@ -485,29 +467,27 @@ class _HomeScreenContentState extends State { ); } - Widget _buildSensorInfoRow(IconData icon, String text) { + Widget _buildSensorInfoRow(IconData icon, String text, String type) { return Row(children: [ Container( width: 40, height: 40, decoration: BoxDecoration( - color: Colors.white, + color: _accentContainerColor, borderRadius: BorderRadius.circular(8), ), - child: Icon(icon, color: Colors.black, size: 24), + child: Icon(icon, color: _mainTextColor, size: 24), ), const SizedBox(width: 12), - Text(text, - style: const TextStyle( - fontSize: 14, - color: Colors.white, - fontWeight: FontWeight.w600)) + Text(text, style: TextStyle(fontSize: 14, color: _mainTextColor, fontWeight: FontWeight.w600)) ]); } Widget _buildMyLocationCard() { const LatLng exampleLocation = LatLng(37.5665, 126.9780); return Card( + shadow: _cleanShadow, + cardColor: _cardBackgroundColor, clipBehavior: Clip.antiAlias, child: SizedBox( height: 200.0, @@ -518,30 +498,21 @@ class _HomeScreenContentState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Column( + Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('My Location', - style: TextStyle( - fontSize: 13, - color: Colors.white, - fontWeight: FontWeight.bold)), - SizedBox(height: 4), - Text('주소: 남구 효덕로 277', - style: - TextStyle(fontSize: 11, color: Colors.white70)), + Text('현재 위치', style: TextStyle(fontSize: 13, color: _mainTextColor, fontWeight: FontWeight.bold)), + const SizedBox(height: 4), + Text('주소: 남구 효덕로 277', style: TextStyle(fontSize: 11, color: _mainTextColor)), ], ), InkWell( onTap: () {}, child: Row( children: [ - Text('VIEW MORE', - style: - TextStyle(color: Colors.grey[400], fontSize: 9)), + Text('더 보기', style: TextStyle(color: _mainTextColor, fontSize: 9)), const SizedBox(width: 4), - Icon(Icons.arrow_forward_ios, - size: 10, color: Colors.grey[400]), + Icon(Icons.arrow_forward_ios, size: 10, color: _subTextColor), ], ), ), @@ -553,25 +524,22 @@ class _HomeScreenContentState extends State { options: const MapOptions( initialCenter: exampleLocation, initialZoom: 15.0, - interactionOptions: - InteractionOptions(flags: InteractiveFlag.none), + interactionOptions: InteractionOptions(flags: InteractiveFlag.none), ), children: [ TileLayer( - urlTemplate: - 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', + urlTemplate: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', userAgentPackageName: 'com.example.app', subdomains: const ['a', 'b', 'c', 'd'], retinaMode: true, ), - const MarkerLayer( + MarkerLayer( markers: [ Marker( point: exampleLocation, width: 80, height: 80, - child: Icon(Icons.location_pin, - size: 40, color: Colors.white), + child: Icon(Icons.location_pin, size: 40, color: _mainTextColor), ), ], ), @@ -586,15 +554,17 @@ class _HomeScreenContentState extends State { Widget _buildActivityCard() { return Card( + shadow: _cleanShadow, + cardColor: _cardBackgroundColor, child: Padding( padding: const EdgeInsets.all(_uniformGap), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('Activity', + Text('Activity', style: TextStyle( fontSize: 13, - color: Colors.white, + color: _mainTextColor, fontWeight: FontWeight.bold)), const SizedBox(height: 12), Row( @@ -630,35 +600,44 @@ class _HomeScreenContentState extends State { Widget _activityText(String text) { return Text(text, - style: const TextStyle(fontSize: 11, color: Colors.white70)); + style: TextStyle(fontSize: 11, color: _mainTextColor)); } } - - class _LineChartPlaceholder extends StatelessWidget { - const _LineChartPlaceholder(); + final Color lineColor; + final Color subLabelColor; + + const _LineChartPlaceholder({ + super.key, + required this.lineColor, + required this.subLabelColor + }); + @override Widget build(BuildContext context) { return Column(children: [ Expanded( child: - CustomPaint(painter: _LineChartPainter(), size: Size.infinite)), + CustomPaint(painter: _LineChartPainter(color: lineColor), size: Size.infinite)), const SizedBox(height: 4), - const Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('24H AGO', style: TextStyle(fontSize: 8, color: Colors.white54)), - Text('12H AGO', style: TextStyle(fontSize: 8, color: Colors.white54)), - Text('NOW', style: TextStyle(fontSize: 8, color: Colors.white54)) + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Text('24H AGO', style: TextStyle(fontSize: 8, color: subLabelColor)), + Text('12H AGO', style: TextStyle(fontSize: 8, color: subLabelColor)), + Text('NOW', style: TextStyle(fontSize: 8, color: subLabelColor)) ]) ]); } } class _LineChartPainter extends CustomPainter { + final Color color; + _LineChartPainter({required this.color}); + @override void paint(ui.Canvas canvas, ui.Size size) { final paint = Paint() - ..color = Colors.white.withOpacity(0.8) + ..color = color ..strokeWidth = 1.5 ..style = PaintingStyle.stroke; @@ -683,12 +662,18 @@ class Card extends StatelessWidget { final Widget child; final EdgeInsetsGeometry? padding; final Clip clipBehavior; + final Color? borderColor; + final Color? cardColor; + final BoxShadow? shadow; const Card({ super.key, required this.child, this.padding, this.clipBehavior = Clip.none, + this.borderColor, + this.cardColor, + this.shadow, }); @override @@ -696,8 +681,9 @@ class Card extends StatelessWidget { return Container( clipBehavior: clipBehavior, decoration: BoxDecoration( - color: Theme.of(context).primaryColor, - borderRadius: BorderRadius.circular(12), + color: cardColor ?? Colors.white, + borderRadius: BorderRadius.circular(8), + boxShadow: shadow != null ? [shadow!] : null, ), child: child, ); @@ -723,7 +709,6 @@ class _BatteryArcPainter extends CustomPainter { ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round; - final Paint foregroundPaint = Paint() ..color = color ..strokeWidth = 8 diff --git a/lib/login_screen.dart b/lib/login_screen.dart index 264ce4a..bcd04d4 100644 --- a/lib/login_screen.dart +++ b/lib/login_screen.dart @@ -1,105 +1,188 @@ import 'package:flutter/material.dart'; import 'main.dart'; -class LoginScreen extends StatelessWidget { +class LoginScreen extends StatefulWidget { + const LoginScreen({super.key}); + + @override + State createState() => _LoginScreenState(); +} + +class _LoginScreenState extends State { final TextEditingController _idController = TextEditingController(); final TextEditingController _pwController = TextEditingController(); - LoginScreen({super.key}); + final FocusNode _idFocusNode = FocusNode(); + final FocusNode _pwFocusNode = FocusNode(); + + final Color mainBlueColor = const Color(0xFF007AFF); + + bool _isIdFocused = false; + bool _isPwFocused = false; + + @override + void initState() { + super.initState(); + _idFocusNode.addListener(() { + setState(() => _isIdFocused = _idFocusNode.hasFocus); + }); + _pwFocusNode.addListener(() { + setState(() => _isPwFocused = _pwFocusNode.hasFocus); + }); + } + + @override + void dispose() { + _idController.dispose(); + _pwController.dispose(); + _idFocusNode.dispose(); + _pwFocusNode.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: const Color(0xFF1E1E1E), - body: Padding( - padding: const EdgeInsets.all(24.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const Icon(Icons.polymer, size: 80, color: Colors.white), - const SizedBox(height: 20), - const Text( - 'METAQLAB' - '', - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.white, - fontSize: 24, - fontWeight: FontWeight.bold, - letterSpacing: 1.2, - ), - ), - const SizedBox(height: 60), - - _buildTextField('User ID', Icons.person, controller: _idController), - const SizedBox(height: 16), - _buildTextField('Password', Icons.lock, isObscure: true, controller: _pwController), - - const SizedBox(height: 40), - - ElevatedButton( - onPressed: () { - String inputId = _idController.text; - String inputPw = _pwController.text; - - if (inputId == 'user' && inputPw == '1234') { - print('로그인 성공!'); - Navigator.pushReplacement( - context, - MaterialPageRoute(builder: (context) => const HomeScreen()), - ); - } else { - print('로그인 실패'); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('아이디 또는 비밀번호가 틀렸습니다.'), - backgroundColor: Colors.redAccent, - duration: Duration(seconds: 2), - ), - ); - } - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - foregroundColor: Colors.black, - padding: const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), + backgroundColor: Colors.white, + body: SafeArea( + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Icon(Icons.polymer, size: 80, color: mainBlueColor), + const SizedBox(height: 20), + Text( + 'METAQLAB', + textAlign: TextAlign.center, + style: TextStyle( + color: mainBlueColor, + fontSize: 24, + fontWeight: FontWeight.bold, + letterSpacing: 1.2, ), ), - child: const Text( - 'LOGIN', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - ), + const SizedBox(height: 60), - const SizedBox(height: 20), - TextButton( - onPressed: () {}, - child: const Text('Forgot Password?', style: TextStyle(color: Colors.grey)), - ), - ], + _buildCustomTextField( + label: '아이디', + controller: _idController, + focusNode: _idFocusNode, + isFocused: _isIdFocused, + ), + const SizedBox(height: 16), + + _buildCustomTextField( + label: '비밀번호', + controller: _pwController, + focusNode: _pwFocusNode, + isFocused: _isPwFocused, + isObscure: true, + ), + + const SizedBox(height: 40), + + ElevatedButton( + onPressed: () { + String inputId = _idController.text; + String inputPw = _pwController.text; + + if (inputId == 'user' && inputPw == '1234') { + print('로그인 성공!'); + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => const HomeScreen()), + ); + } else { + print('로그인 실패'); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('아이디 또는 비밀번호가 틀렸습니다.'), + backgroundColor: Colors.redAccent, + duration: Duration(seconds: 2), + ), + ); + } + }, + style: ElevatedButton.styleFrom( + backgroundColor: mainBlueColor, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 0, + ), + child: const Text( + '로그인', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + ), + + const SizedBox(height: 20), + TextButton( + onPressed: () {}, + child: Text( + '비밀번호 찾기 / 회원가입', + style: TextStyle(color: mainBlueColor, fontWeight: FontWeight.w500), + ), + ), + ], + ), ), ), ); } - Widget _buildTextField(String hint, IconData icon, {bool isObscure = false, required TextEditingController controller}) { - return TextField( - controller: controller, - obscureText: isObscure, - style: const TextStyle(color: Colors.white), - decoration: InputDecoration( - prefixIcon: Icon(icon, color: Colors.grey), - hintText: hint, - hintStyle: const TextStyle(color: Colors.grey), - filled: true, - fillColor: const Color(0xFF2C2C2E), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide.none, + Widget _buildCustomTextField({ + required String label, + required TextEditingController controller, + required FocusNode focusNode, + required bool isFocused, + bool isObscure = false, + }) { + return AnimatedContainer( + duration: const Duration(milliseconds: 200), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: Colors.white, + border: Border.all( + color: isFocused ? mainBlueColor : Colors.grey.shade300, + width: isFocused ? 1.5 : 1.0, ), + borderRadius: BorderRadius.circular(8), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: TextStyle( + color: isFocused ? mainBlueColor : Colors.grey.shade600, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 4), + TextField( + controller: controller, + focusNode: focusNode, + obscureText: isObscure, + decoration: const InputDecoration( + isDense: true, + contentPadding: EdgeInsets.zero, + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + ), + style: const TextStyle( + fontSize: 16, + color: Colors.black87, + ), + cursorColor: mainBlueColor, + ), + ], ), ); } diff --git a/lib/main.dart b/lib/main.dart index 44f6fe2..22b59f3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,6 +7,12 @@ import 'package:smarthelmet_app/rent_return_screen.dart'; import 'package:smarthelmet_app/settings_screen.dart'; import 'package:smarthelmet_app/login_screen.dart'; +final Color _mainBlueColor = const Color(0xFF002FA7); +final Color _mainTextColor = const Color(0xFF1C1C1E); +final Color _subTextColor = const Color(0xFF6A717B); +final Color _pageBackgroundColor = const Color(0xFFF5F7F9); +final Color _cardBackgroundColor = Colors.white; + void main() { runApp(const SmartHelmetApp()); } @@ -18,22 +24,22 @@ class SmartHelmetApp extends StatelessWidget { Widget build(BuildContext context) { SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( statusBarColor: Colors.transparent, - statusBarIconBrightness: Brightness.light, + statusBarIconBrightness: Brightness.dark, )); return MaterialApp( debugShowCheckedModeBanner: false, theme: ThemeData( - brightness: Brightness.dark, - scaffoldBackgroundColor: const Color(0xFF27292B), - primaryColor: const Color(0xFF30343B), + brightness: Brightness.light, + scaffoldBackgroundColor: _pageBackgroundColor, + primaryColor: _mainBlueColor, fontFamily: 'Pretendard', - textTheme: const TextTheme( - bodyLarge: TextStyle(color: Colors.white, fontWeight: FontWeight.w500), - bodyMedium: TextStyle(color: Colors.white70, fontWeight: FontWeight.w400), + textTheme: TextTheme( + bodyLarge: TextStyle(color: _mainTextColor, fontWeight: FontWeight.w500), + bodyMedium: TextStyle(color: _subTextColor, fontWeight: FontWeight.w400), ), ), - home: LoginScreen(), + home: const HomeScreen(), ); } } @@ -81,19 +87,37 @@ class _HomeScreenState extends State { }); }, type: BottomNavigationBarType.fixed, - backgroundColor: const Color(0xFF1C1C1E), + backgroundColor: _mainBlueColor, elevation: 0, selectedItemColor: Colors.white, - unselectedItemColor: Colors.grey, + unselectedItemColor: Colors.white70, showUnselectedLabels: true, selectedFontSize: 12, unselectedFontSize: 12, + selectedLabelStyle: const TextStyle(fontWeight: FontWeight.bold, height: 1.5), + unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.bold, height: 1.5), + iconSize: 22, items: const [ - BottomNavigationBarItem(icon: Icon(Icons.home), label: 'HOME'), - BottomNavigationBarItem(icon: Icon(Icons.history), label: 'HISTORY'), - BottomNavigationBarItem(icon: Icon(Icons.settings_input_component), label: 'CONTROL'), - BottomNavigationBarItem(icon: Icon(Icons.assignment_return_outlined), label: 'RENT/RETURN'), - BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'SETTINGS'), + BottomNavigationBarItem( + icon: Padding(padding: EdgeInsets.only(bottom: 2.0), child: Icon(Icons.home)), + label: '홈', + ), + BottomNavigationBarItem( + icon: Padding(padding: EdgeInsets.only(bottom: 2.0), child: Icon(Icons.history)), + label: '기록', + ), + BottomNavigationBarItem( + icon: Padding(padding: EdgeInsets.only(bottom: 2.0), child: Icon(Icons.settings_input_component)), + label: '제어', + ), + BottomNavigationBarItem( + icon: Padding(padding: EdgeInsets.only(bottom: 2.0), child: Icon(Icons.assignment_return_outlined)), + label: '대여 / 반납', + ), + BottomNavigationBarItem( + icon: Padding(padding: EdgeInsets.only(bottom: 2.0), child: Icon(Icons.settings)), + label: '설정', + ), ], ), ), diff --git a/lib/rent_return_screen.dart b/lib/rent_return_screen.dart index b313843..8cf3ac6 100644 --- a/lib/rent_return_screen.dart +++ b/lib/rent_return_screen.dart @@ -3,6 +3,21 @@ import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; import 'rental_process_screen.dart'; +final Color _mainBlueColor = const Color(0xFF002FA7); +final Color _mainTextColor = const Color(0xFF1C1C1E); +final Color _subTextColor = const Color(0xFF6A717B); +final Color _pageBackgroundColor = const Color(0xFFF5F7F9); +final Color _cardBackgroundColor = Colors.white; +final Color _accentContainerColor = const Color(0xFFF0F2F5); +final Color _borderColor = const Color(0xFF007AFF).withOpacity(0.8); + +const BoxShadow _cleanShadow = BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.07), + blurRadius: 8, + offset: Offset(0, 4), + spreadRadius: 0, +); + class StationInfo { final String name; final String address; @@ -22,8 +37,7 @@ class RentReturnScreen extends StatelessWidget { @override Widget build(BuildContext context) { - final LatLng centerLocation = const LatLng(35.1595, 126.8526); - + final LatLng centerLocation = LatLng(35.1595, 126.8526); final List stations = [ StationInfo( name: 'STATION A - GU.UNIV', @@ -48,91 +62,114 @@ class RentReturnScreen extends StatelessWidget { ]; return Scaffold( - backgroundColor: const Color(0xFF27292B), - appBar: AppBar( - title: const Text( - 'RENT/ RETURN', - style: TextStyle( - fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white), - ), - backgroundColor: const Color(0xFF2C2C2E), - elevation: 0, - actions: [ - IconButton( - icon: const Icon(Icons.more_vert, color: Colors.white), - onPressed: () {}, - ) - ], - ), - body: Column( - children: [ - Expanded( - flex: 50, - child: FlutterMap( - options: MapOptions( - initialCenter: centerLocation, - initialZoom: 15.0, + backgroundColor: _pageBackgroundColor, + appBar: PreferredSize( + preferredSize: const Size.fromHeight(kToolbarHeight), + child: Container( + decoration: BoxDecoration( + color: _pageBackgroundColor, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 8, + offset: const Offset(0, 4), ), - children: [ - TileLayer( - urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', - userAgentPackageName: 'com.example.helmet_app', - ), - MarkerLayer( - markers: [ - _buildBlackMarker(centerLocation), - _buildBlackMarker(const LatLng(35.162, 126.855)), - _buildBlackMarker(const LatLng(35.157, 126.850)), - _buildBlackMarker(const LatLng(35.160, 126.858)), - ], - ), - ], - ), + ], ), - - Expanded( - flex: 45, - child: Container( - color: const Color(0xFF27292B), + child: AppBar( + scrolledUnderElevation: 0, + title: Text( + '대여/ 반납', + style: TextStyle( + fontSize: 18, + letterSpacing: 0.5, + fontWeight: FontWeight.bold, + color: _mainTextColor), + ), + backgroundColor: Colors.transparent, + elevation: 0, + actions: [ + IconButton( + icon: Icon(Icons.more_vert, color: _mainTextColor), + onPressed: () {}, + ) + ], + ), + ), + ), + body: SingleChildScrollView( + child: Column( + children: [ + SizedBox( + height: 300.0, + width: double.infinity, + child: FlutterMap( + options: MapOptions( + initialCenter: centerLocation, + initialZoom: 15.0, + ), + children: [ + TileLayer( + urlTemplate: + 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', + userAgentPackageName: 'com.example.helmet_app', + subdomains: const ['a', 'b', 'c', 'd'], + retinaMode: true, + ), + MarkerLayer( + markers: [ + _buildBlueMarker(centerLocation), + _buildBlueMarker(const LatLng(35.162, 126.855)), + _buildBlueMarker(const LatLng(35.157, 126.850)), + _buildBlueMarker(const LatLng(35.160, 126.858)), + ], + ), + ], + ), + ), + Container( + color: _pageBackgroundColor, padding: const EdgeInsets.fromLTRB(16, 20, 16, 0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'LIST VIEW', style: TextStyle( - color: Colors.white, + color: _mainTextColor, fontSize: 12, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 10), - - Expanded( - child: ListView.builder( - padding: const EdgeInsets.only(bottom: 20), - itemCount: stations.length, - itemBuilder: (context, index) { - return _buildStationItem(context, stations[index]); - }, - ), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.only(bottom: 20), + itemCount: stations.length, + itemBuilder: (context, index) { + return _buildStationItem(context, stations[index]); + }, ), ], ), ), - ), - ], + ], + ), ), ); } Widget _buildStationItem(BuildContext context, StationInfo station) { + bool isOffline = station.status.startsWith('OFFLINE'); + return Container( margin: const EdgeInsets.only(bottom: 16), padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: const Color(0xFF30343B), + color: _cardBackgroundColor, borderRadius: BorderRadius.circular(10), + boxShadow: [_cleanShadow], ), child: Column( children: [ @@ -145,7 +182,7 @@ class RentReturnScreen extends StatelessWidget { child: Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( - color: Colors.black.withValues(alpha: 0.3), + color: _pageBackgroundColor, borderRadius: BorderRadius.circular(5), ), child: Stack( @@ -153,8 +190,8 @@ class RentReturnScreen extends StatelessWidget { children: [ Container( decoration: BoxDecoration( - color: const Color(0xFF2D2F33), - borderRadius: BorderRadius.circular(5) + color: _cardBackgroundColor, + borderRadius: BorderRadius.circular(5), ), ), Padding( @@ -163,7 +200,7 @@ class RentReturnScreen extends StatelessWidget { 'assets/images/storage.png', fit: BoxFit.contain, errorBuilder: (context, error, stackTrace) => - const Icon(Icons.inventory_2, color: Colors.white54), + Icon(Icons.inventory_2, color: _subTextColor), ), ), ], @@ -171,15 +208,14 @@ class RentReturnScreen extends StatelessWidget { ), ), const SizedBox(width: 12), - Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( station.name, - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: _mainTextColor, fontWeight: FontWeight.bold, fontSize: 13, ), @@ -189,30 +225,29 @@ class RentReturnScreen extends StatelessWidget { const SizedBox(height: 4), Text( station.address, - style: const TextStyle( - color: Colors.grey, fontSize: 11, height: 1.2), + style: TextStyle( + color: _subTextColor, fontSize: 11, height: 1.2), maxLines: 2, ), const SizedBox(height: 2), Text( 'STATUS: ${station.status}', style: TextStyle( - color: station.status.startsWith('OFFLINE') - ? Colors.redAccent - : Colors.grey, + color: isOffline ? Colors.redAccent : _mainBlueColor, fontSize: 11, ), ), Text( station.distance, - style: const TextStyle(color: Colors.grey, fontSize: 11), + style: TextStyle(color: _subTextColor, fontSize: 11), ), ], ), ), - ElevatedButton( - onPressed: () { + onPressed: isOffline + ? null + : () { Navigator.push( context, MaterialPageRoute( @@ -223,29 +258,31 @@ class RentReturnScreen extends StatelessWidget { ); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - foregroundColor: Colors.black, + backgroundColor: + isOffline ? Colors.grey.shade400 : _mainBlueColor, + foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5), ), - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 10), minimumSize: const Size(80, 40), + elevation: 0, ), - child: const Text( - 'SELECT', - style: TextStyle(fontWeight: FontWeight.w500, letterSpacing: 0.8, fontSize: 14) - ), + child: const Text('SELECT', + style: TextStyle( + fontWeight: FontWeight.w500, + letterSpacing: 0.8, + fontSize: 14)), ), ], ), - const SizedBox(height: 12), - Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: const Color(0xFF1E1E1E), + color: _accentContainerColor, borderRadius: BorderRadius.circular(5), ), child: Column( @@ -254,10 +291,10 @@ class RentReturnScreen extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text( - 'LOGS', + Text( + '기록', style: TextStyle( - color: Colors.white, + color: _mainTextColor, fontWeight: FontWeight.bold, fontSize: 12, ), @@ -267,7 +304,7 @@ class RentReturnScreen extends StatelessWidget { child: Text( 'VIEW MORE >', style: TextStyle( - color: Colors.grey[400], + color: _subTextColor, fontSize: 10, ), ), @@ -275,13 +312,13 @@ class RentReturnScreen extends StatelessWidget { ], ), const SizedBox(height: 6), - const Text( + Text( 'Available: Door Fully Closed', - style: TextStyle(color: Colors.grey, fontSize: 11), + style: TextStyle(color: _subTextColor, fontSize: 11), ), - const Text( + Text( '(2025-11-19)/(08:58)', - style: TextStyle(color: Colors.grey, fontSize: 11), + style: TextStyle(color: _subTextColor, fontSize: 11), ), ], ), @@ -291,14 +328,14 @@ class RentReturnScreen extends StatelessWidget { ); } - Marker _buildBlackMarker(LatLng point) { + Marker _buildBlueMarker(LatLng point) { return Marker( point: point, width: 40, height: 40, - child: const Icon( + child: Icon( Icons.location_on, - color: Colors.black, + color: _mainBlueColor, size: 40, ), ); @@ -312,31 +349,44 @@ class RentReturnScreen extends StatelessWidget { builder: (context) { return Container( height: MediaQuery.of(context).size.height * 0.6, - decoration: const BoxDecoration( - color: Color(0xFF1E1E1E), - borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + decoration: BoxDecoration( + color: _cardBackgroundColor, + borderRadius: const BorderRadius.vertical(top: Radius.circular(20)), + border: Border.all(color: _borderColor, width: 1.5), ), child: Column( children: [ const SizedBox(height: 12), Container( - width: 40, height: 4, - decoration: BoxDecoration(color: Colors.grey[600], borderRadius: BorderRadius.circular(2)), + width: 40, + height: 4, + decoration: BoxDecoration( + color: _subTextColor, + borderRadius: BorderRadius.circular(2)), ), Padding( padding: const EdgeInsets.all(20.0), - child: Text("LOGS: $stationName", style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold)), + child: Text("LOGS: $stationName", + style: TextStyle( + color: _mainTextColor, + fontSize: 16, + fontWeight: FontWeight.bold)), ), - const Divider(color: Colors.white24, height: 1), + Divider(color: _subTextColor.withOpacity(0.5), height: 1), Expanded( child: ListView( padding: const EdgeInsets.all(20), children: [ - _buildLogItem("08:58:33", "Door Fully Closed", Icons.door_front_door, Colors.green), - _buildLogItem("08:58:30", "Helmet Returned", Icons.check_circle_outline, Colors.blue), - _buildLogItem("08:55:12", "User Unlocked Door", Icons.lock_open, Colors.white), - _buildLogItem("08:30:00", "UV Sanitization Complete", Icons.cleaning_services, Colors.purpleAccent), - _buildLogItem("08:00:00", "System Boot Up", Icons.power_settings_new, Colors.grey), + _buildLogItem("08:58:33", "Door Fully Closed", + Icons.door_front_door, _mainBlueColor), + _buildLogItem("08:58:30", "Helmet Returned", + Icons.check_circle_outline, _mainBlueColor), + _buildLogItem("08:55:12", "User Unlocked Door", + Icons.lock_open, _mainTextColor), + _buildLogItem("08:30:00", "UV Sanitization Complete", + Icons.cleaning_services, _mainBlueColor), + _buildLogItem("08:00:00", "System Boot Up", + Icons.power_settings_new, _subTextColor), ], ), ), @@ -347,23 +397,26 @@ class RentReturnScreen extends StatelessWidget { ); } - Widget _buildLogItem(String time, String message, IconData icon, Color color) { + Widget _buildLogItem( + String time, String message, IconData icon, Color color) { return Padding( padding: const EdgeInsets.only(bottom: 20.0), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(time, style: const TextStyle(color: Colors.grey, fontSize: 12)), + Text(time, style: TextStyle(color: _subTextColor, fontSize: 12)), const SizedBox(width: 16), Column( children: [ Icon(icon, color: color, size: 20), - Container(width: 2, height: 20, color: Colors.white12), + Container( + width: 2, height: 20, color: _subTextColor.withOpacity(0.5)), ], ), const SizedBox(width: 16), Expanded( - child: Text(message, style: const TextStyle(color: Colors.white70, fontSize: 14)), + child: Text(message, + style: TextStyle(color: _mainTextColor, fontSize: 14)), ), ], ), diff --git a/lib/rental_process_screen.dart b/lib/rental_process_screen.dart index 23eb264..1ce62d0 100644 --- a/lib/rental_process_screen.dart +++ b/lib/rental_process_screen.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:latlong2/latlong.dart'; class RentalProcessScreen extends StatelessWidget { final String stationName; @@ -8,25 +10,34 @@ class RentalProcessScreen extends StatelessWidget { required this.stationName, }); - static const Color kBackgroundColor = Color(0xFF1E1E1E); - static const Color kCardColor = Color(0xFF2C2C2E); - static const Color kPrimaryGreen = Color(0xFF4CAF50); - static const Color kPrimaryOrange = Color(0xFFFF3D00); + static const Color _mainBlueColor = Color(0xFF002FA7); + static const Color _mainTextColor = Color(0xFF1C1C1E); + static const Color _subTextColor = Color(0xFF6A717B); + static const Color _pageBackgroundColor = Color(0xFFF5F7F9); + static const Color _cardBackgroundColor = Colors.white; + static const Color _accentContainerColor = Color(0xFFF0F2F5); + static const Color _borderColor = Color(0xFF007AFF); + static const Color _primaryGreen = Color(0xFF4CAF50); + static const Color _primaryOrange = Color(0xFFFF3D00); @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: kBackgroundColor, + backgroundColor: _pageBackgroundColor, appBar: AppBar( + scrolledUnderElevation: 0, title: Text( stationName, - style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: Colors.white), + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + color: _mainTextColor), ), - backgroundColor: kCardColor, + backgroundColor: _cardBackgroundColor, elevation: 0, actions: [ IconButton( - icon: const Icon(Icons.more_vert, color: Colors.white), + icon: const Icon(Icons.more_vert, color: _mainTextColor), onPressed: () {}, ), ], @@ -39,52 +50,51 @@ class RentalProcessScreen extends StatelessWidget { children: [ _buildStatusCard(context), const SizedBox(height: 24), - - _buildProcessSectionTitle('HELMET RENTAL PROCESS'), + _buildProcessSectionTitle('헬멧 대여 순서'), const SizedBox(height: 12), Container( decoration: BoxDecoration( - color: kCardColor, + color: _cardBackgroundColor, borderRadius: BorderRadius.circular(12), ), child: Column( children: [ - _buildStepRow(1, 'UNLOCK & OPEN', Icons.lock_open, isCompleted: true), + _buildStepRow(1, '잠금 해제 & 문 열림', Icons.lock_open, + isCompleted: true), _buildDivider(), - _buildStepRow(2, 'DOOR STATUS', Icons.sensor_door_outlined), + _buildStepRow(2, '문 상태 확인', Icons.sensor_door_outlined), _buildDivider(), - _buildStepRow(3, 'TAKE HELMET', Icons.outbox), + _buildStepRow(3, '헬멧 꺼내기', Icons.outbox), _buildDivider(), - _buildStepRow(4, 'ENJOY RIDING!', Icons.sentiment_satisfied_alt, showDivider: false), + _buildStepRow(4, '주행 시작 !', Icons.sentiment_satisfied_alt, + showDivider: false), ], ), ), const SizedBox(height: 24), - - _buildProcessSectionTitle('HELMET RETURN PROCESS'), + _buildProcessSectionTitle('헬멧 반납 과정'), const SizedBox(height: 12), Container( decoration: BoxDecoration( - color: kCardColor, + color: _cardBackgroundColor, borderRadius: BorderRadius.circular(12), ), child: Column( children: [ - _buildStepRow(1, 'UNLOCK & OPEN', Icons.lock_open), + _buildStepRow(1, '잠금해제 & 문열림', Icons.lock_open), _buildDivider(), - _buildStepRow(2, 'INSERT HELMET', Icons.move_to_inbox), + _buildStepRow(2, '헬멧 넣기', Icons.move_to_inbox), _buildDivider(), - _buildStepRow(3, 'SENSOR SCANNING\n& RETURN COMPLETE', Icons.sync), + _buildStepRow(3, '센서 스캔 & 반납 완료', Icons.sync), _buildDivider(), - _buildStepRow(4, 'SANITIZING', Icons.shield_outlined, showDivider: false), + _buildStepRow(4, '살균 시작', Icons.shield_outlined, + showDivider: false), ], ), ), const SizedBox(height: 24), - _buildErrorAlertBox(), const SizedBox(height: 24), - _buildLogsCard(context), const SizedBox(height: 20), ], @@ -98,16 +108,18 @@ class RentalProcessScreen extends StatelessWidget { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( - color: kCardColor, + color: _cardBackgroundColor, borderRadius: BorderRadius.circular(12), ), child: Column( children: [ Row( children: const [ - Icon(Icons.circle, color: kPrimaryOrange, size: 16), + Icon(Icons.circle, color: _primaryOrange, size: 16), SizedBox(width: 6), - Text('IN USE', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), + Text('사용 중', + style: TextStyle( + color: _mainTextColor, fontWeight: FontWeight.bold)), ], ), const SizedBox(height: 20), @@ -119,13 +131,16 @@ class RentalProcessScreen extends StatelessWidget { height: 150, padding: const EdgeInsets.all(10), decoration: BoxDecoration( - color: kBackgroundColor, + color: _accentContainerColor, borderRadius: BorderRadius.circular(12), ), child: Image.asset( 'assets/images/open.png', fit: BoxFit.contain, - errorBuilder: (context, error, stackTrace) => const Icon(Icons.inventory_2, size: 50, color: Colors.white54), + errorBuilder: (context, error, stackTrace) => const Icon( + Icons.inventory_2, + size: 50, + color: _subTextColor), ), ), const SizedBox(width: 20), @@ -133,7 +148,8 @@ class RentalProcessScreen extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('NOW', style: TextStyle(color: Colors.grey, fontSize: 12)), + const Text('NOW', + style: TextStyle(color: _subTextColor, fontSize: 12)), const SizedBox(height: 8), SizedBox( width: double.infinity, @@ -143,17 +159,20 @@ class RentalProcessScreen extends StatelessWidget { const SnackBar( content: Text('문이 열렸습니다! (OPEN 버튼 눌림)'), duration: Duration(seconds: 1), - backgroundColor: Colors.blue, + backgroundColor: _mainBlueColor, ), ); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - foregroundColor: Colors.black, + backgroundColor: _mainBlueColor, + foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 12), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8)), ), - child: const Text('OPEN', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), + child: const Text('OPEN', + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 16)), ), ), const SizedBox(height: 12), @@ -161,13 +180,16 @@ class RentalProcessScreen extends StatelessWidget { width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( - color: kBackgroundColor, + color: _accentContainerColor, borderRadius: BorderRadius.circular(8), ), child: const Center( child: Text( - 'SENSOR ERROR:\nLOCK FAIL', - style: TextStyle(color: kPrimaryOrange, fontWeight: FontWeight.bold, height: 1.2), + 'ERROR:\nLOCK FAIL', + style: TextStyle( + color: _primaryOrange, + fontWeight: FontWeight.bold, + height: 1.2), textAlign: TextAlign.center, ), ), @@ -185,11 +207,13 @@ class RentalProcessScreen extends StatelessWidget { Widget _buildProcessSectionTitle(String title) { return Text( title, - style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16), + style: const TextStyle( + color: _mainTextColor, fontWeight: FontWeight.bold, fontSize: 16), ); } - Widget _buildStepRow(int step, String title, IconData icon, {bool isCompleted = false, bool showDivider = true}) { + Widget _buildStepRow(int step, String title, IconData icon, + {bool isCompleted = false, bool showDivider = true}) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), child: Row( @@ -199,45 +223,64 @@ class RentalProcessScreen extends StatelessWidget { children: [ Row( children: [ - Text('STEP $step', style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), + Text('STEP $step', + style: const TextStyle( + color: _mainTextColor, fontWeight: FontWeight.bold)), if (isCompleted) ...[ const SizedBox(width: 8), - const Icon(Icons.check_circle, color: kPrimaryGreen, size: 16), + const Icon(Icons.check_circle, + color: _primaryGreen, size: 16), ], ], ), const SizedBox(height: 4), - Text(title, style: const TextStyle(color: Colors.white, fontSize: 18, letterSpacing: 0.6)), + Text(title, + style: const TextStyle( + color: _mainTextColor, fontSize: 18, letterSpacing: 0.6)), ], ), const Spacer(), - Icon(icon, color: isCompleted ? Colors.white : Colors.grey, size: 48), + Icon(icon, + color: isCompleted ? _mainBlueColor : _subTextColor, size: 48), ], ), ); } Widget _buildDivider() { - return const Divider(color: Colors.white, height: 1, thickness: 1, indent: 20, endIndent: 20); + return Divider( + color: _subTextColor.withOpacity(0.5), + height: 1, + thickness: 1, + indent: 20, + endIndent: 20); } Widget _buildErrorAlertBox() { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: kCardColor.withOpacity(0.5), - border: Border.all(color: kPrimaryOrange, width: 2), + color: _accentContainerColor, + border: Border.all(color: _primaryOrange, width: 2), borderRadius: BorderRadius.circular(12), ), child: Row( children: const [ - Icon(Icons.warning_amber_rounded, color: kPrimaryOrange, size: 40), + Icon(Icons.warning_amber_rounded, color: _primaryOrange, size: 40), SizedBox(width: 16), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('SENSOR ERROR:', style: TextStyle(color: kPrimaryOrange, fontWeight: FontWeight.bold, fontSize: 16)), - Text('LOCK FAILURE', style: TextStyle(color: kPrimaryOrange, fontWeight: FontWeight.bold, fontSize: 16)), + Text('SENSOR ERROR:', + style: TextStyle( + color: _primaryOrange, + fontWeight: FontWeight.bold, + fontSize: 16)), + Text('LOCK FAILURE', + style: TextStyle( + color: _primaryOrange, + fontWeight: FontWeight.bold, + fontSize: 16)), ], ), ], @@ -249,7 +292,7 @@ class RentalProcessScreen extends StatelessWidget { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( - color: kCardColor, + color: _cardBackgroundColor, borderRadius: BorderRadius.circular(12), ), child: Column( @@ -257,24 +300,29 @@ class RentalProcessScreen extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text('LOGS', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), + const Text('LOGS', + style: TextStyle( + color: _mainTextColor, fontWeight: FontWeight.bold)), InkWell( onTap: () => _showLogHistory(context), - child: Text('VIEW MORE >', style: TextStyle(color: Colors.grey[400], fontSize: 12)), + child: Text('VIEW MORE >', + style: TextStyle(color: _subTextColor, fontSize: 12)), ), ], ), const SizedBox(height: 12), Row( - children: const [ - Icon(Icons.check_circle, color: kPrimaryGreen, size: 16), - SizedBox(width: 8), - Text('Available: Door Fully Closed', style: TextStyle(color: Colors.white70)), + children: [ + const Icon(Icons.check_circle, color: _mainBlueColor, size: 16), + const SizedBox(width: 8), + const Text('Available: Door Fully Closed', + style: TextStyle(color: _mainTextColor)), ], ), Padding( padding: const EdgeInsets.only(left: 24.0, top: 4), - child: Text('(2025-11-19)/(08:58)', style: TextStyle(color: Colors.grey[600], fontSize: 12)), + child: Text('(2025-11-19)/(08:58)', + style: TextStyle(color: _subTextColor, fontSize: 12)), ), ], ), @@ -289,31 +337,43 @@ class RentalProcessScreen extends StatelessWidget { builder: (context) { return Container( height: MediaQuery.of(context).size.height * 0.6, - decoration: const BoxDecoration( - color: Color(0xFF1E1E1E), - borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + decoration: BoxDecoration( + color: _cardBackgroundColor, + borderRadius: const BorderRadius.vertical(top: Radius.circular(20)), ), child: Column( children: [ const SizedBox(height: 12), Container( - width: 40, height: 4, - decoration: BoxDecoration(color: Colors.grey[600], borderRadius: BorderRadius.circular(2)), + width: 40, + height: 4, + decoration: BoxDecoration( + color: _subTextColor, + borderRadius: BorderRadius.circular(2)), ), const Padding( padding: EdgeInsets.all(20.0), - child: Text("DEVICE LOGS", style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)), + child: Text("DEVICE LOGS", + style: TextStyle( + color: _mainTextColor, + fontSize: 18, + fontWeight: FontWeight.bold)), ), - const Divider(color: Colors.white24, height: 1), + Divider(color: _subTextColor.withOpacity(0.5), height: 1), Expanded( child: ListView( padding: const EdgeInsets.all(20), children: [ - _buildLogItem("08:58:33", "Door Fully Closed", Icons.door_front_door, Colors.green), - _buildLogItem("08:58:30", "Helmet Returned (Sensor A)", Icons.check_circle_outline, Colors.blue), - _buildLogItem("08:55:12", "User Unlocked Door", Icons.lock_open, Colors.white), - _buildLogItem("08:30:00", "UV Sanitization Complete", Icons.cleaning_services, Colors.purpleAccent), - _buildLogItem("08:00:00", "System Boot Up", Icons.power_settings_new, Colors.grey), + _buildLogItem("08:58:33", "Door Fully Closed", + Icons.door_front_door, _mainBlueColor), + _buildLogItem("08:58:30", "Helmet Returned (Sensor A)", + Icons.check_circle_outline, _mainBlueColor), + _buildLogItem("08:55:12", "User Unlocked Door", + Icons.lock_open, _mainTextColor), + _buildLogItem("08:30:00", "UV Sanitization Complete", + Icons.cleaning_services, _mainBlueColor), + _buildLogItem("08:00:00", "System Boot Up", + Icons.power_settings_new, _subTextColor), ], ), ), @@ -324,23 +384,27 @@ class RentalProcessScreen extends StatelessWidget { ); } - Widget _buildLogItem(String time, String message, IconData icon, Color color) { + Widget _buildLogItem( + String time, String message, IconData icon, Color color) { return Padding( padding: const EdgeInsets.only(bottom: 20.0), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(time, style: const TextStyle(color: Colors.grey, fontSize: 12)), + Text(time, + style: const TextStyle(color: _subTextColor, fontSize: 12)), const SizedBox(width: 16), Column( children: [ Icon(icon, color: color, size: 20), - Container(width: 2, height: 20, color: Colors.white12), + Container( + width: 2, height: 20, color: _subTextColor.withOpacity(0.5)), ], ), const SizedBox(width: 16), Expanded( - child: Text(message, style: const TextStyle(color: Colors.white70, fontSize: 14)), + child: Text(message, + style: const TextStyle(color: _mainTextColor, fontSize: 14)), ), ], ), diff --git a/lib/settings_screen.dart b/lib/settings_screen.dart index 1498956..5b8ee84 100644 --- a/lib/settings_screen.dart +++ b/lib/settings_screen.dart @@ -1,14 +1,268 @@ import 'package:flutter/material.dart'; -class SettingsScreen extends StatelessWidget { +final Color _mainBlueColor = const Color(0xFF002FA7); +final Color _mainTextColor = const Color(0xFF1C1C1E); +final Color _subTextColor = const Color(0xFF6A717B); +final Color _pageBackgroundColor = const Color(0xFFF5F7F9); +final Color _cardBackgroundColor = Colors.white; +final Color _accentContainerColor = const Color(0xFFF0F2F5); +final Color _warningColor = Colors.redAccent; + +const BoxShadow _cleanShadow = BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.07), + blurRadius: 8, + offset: Offset(0, 4), + spreadRadius: 0, +); + + +class SettingsScreen extends StatefulWidget { const SettingsScreen({super.key}); + @override + State createState() => _SettingsScreenState(); +} + +class _SettingsScreenState extends State { + + bool _isPushEnabled = true; + bool _isRentalAlertEnabled = true; + bool _isStorageStatusAlert = true; + bool _isAutoSyncEnabled = false; + + final String _currentAppVersion = "v1.0.0"; + + @override Widget build(BuildContext context) { - return const Center( + return Scaffold( + backgroundColor: _pageBackgroundColor, + appBar: AppBar( + scrolledUnderElevation: 0, + title: Text( + '설정', + style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 16, + color: _mainTextColor, + ), + ), + backgroundColor: _pageBackgroundColor, + elevation: 0, + centerTitle: false, + actions: [ + IconButton( + icon: Icon(Icons.more_vert, color: _mainTextColor), + onPressed: () {}, + ), + ], + ), + body: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildSectionTitle('계정 및 보안'), + const SizedBox(height: 12), + _buildAccountSecurityCard(), + + const SizedBox(height: 24), + _buildSectionTitle('알림 설정'), + const SizedBox(height: 12), + _buildNotificationCard(), + + const SizedBox(height: 24), + _buildSectionTitle('앱 정보'), + const SizedBox(height: 12), + _buildAppInfoCard(), + + const SizedBox(height: 40), + _buildWithdrawalButton(), + ], + ), + ), + ); + } + + Widget _buildSectionTitle(String title) { + return Padding( + padding: const EdgeInsets.only(bottom: 8.0), child: Text( - '5. 설정 (Settings) 페이지', - style: TextStyle(color: Colors.white70, fontSize: 20), + title, + style: TextStyle( + color: _mainTextColor, + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + ); + } + + Widget _buildCardWrapper({required Widget child}) { + return Container( + decoration: BoxDecoration( + color: _cardBackgroundColor, + borderRadius: BorderRadius.circular(8), + boxShadow: [_cleanShadow], + ), + child: child, + ); + } + + Widget _buildNotificationCard() { + return _buildCardWrapper( + child: Column( + children: [ + _buildToggleItem( + '알림 전체 수신', + '모든 알림을 켜고 끕니다.', + _isPushEnabled, + (val) => setState(() => _isPushEnabled = val), + showDivider: true, + ), + _buildToggleItem( + '대여/반납 알림', + '대여 시작/종료, 반납 실패 알림', + _isRentalAlertEnabled, + (val) => setState(() => _isRentalAlertEnabled = val), + showDivider: true, + ), + _buildToggleItem( + '보관함 상태 알림', + '살균 완료, 시스템 오류 등 상태 알림', + _isStorageStatusAlert, + (val) => setState(() => _isStorageStatusAlert = val), + showDivider: true, + ), + _buildInfoLink('알림 시간대 설정', Icons.schedule_outlined), + _buildDivider(16), + _buildInfoLink('글자 크기 조정', Icons.format_size), + ], + ), + ); + } + + Widget _buildAccountSecurityCard() { + return _buildCardWrapper( + child: Column( + children: [ + _buildInfoLink('내 정보 수정', Icons.person_outline), + _buildDivider(16), + _buildInfoLink('비밀번호 변경', Icons.lock_outline), + _buildDivider(16), + _buildInfoLink('캐시 데이터 삭제', Icons.cleaning_services_outlined), + ], + ), + ); + } + + Widget _buildAppInfoCard() { + return _buildCardWrapper( + child: Column( + children: [ + _buildInfoLink('버전 정보', null, value: _currentAppVersion), + _buildDivider(16), + _buildInfoLink('이용 약관 및 정책', Icons.gavel), + _buildDivider(16), + _buildInfoLink('고객센터 및 FAQ', Icons.headset_mic_outlined), + _buildDivider(16), + _buildInfoLink('위치 권한 설정', Icons.location_on_outlined), + _buildDivider(16), + _buildInfoLink('서비스 지역 지도 보기', Icons.map_outlined), + ], + ), + ); + } + + Widget _buildToggleItem(String title, String subtitle, bool value, ValueChanged onChanged, {required bool showDivider}) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + color: _mainTextColor, + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 4), + Text( + subtitle, + style: TextStyle( + color: _subTextColor, + fontSize: 12, + ), + ), + ], + ), + ), + Switch( + value: value, + onChanged: onChanged, + activeColor: _mainBlueColor, + inactiveThumbColor: _accentContainerColor, + inactiveTrackColor: Colors.grey.shade400, + ), + ], + ), + ), + if (showDivider) + _buildDivider(0), + ], + ); + } + + Widget _buildInfoLink(String title, IconData? icon, {String? value}) { + return InkWell( + onTap: () { /* Navigation logic */ }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), + child: Row( + children: [ + if (icon != null) ...[ + Icon(icon, color: _mainTextColor, size: 20), + const SizedBox(width: 12), + ], + Expanded( + child: Text( + title, + style: TextStyle(color: _mainTextColor, fontSize: 14), + ), + ), + if (value != null) + Text( + value, + style: TextStyle(color: _subTextColor, fontSize: 14), + ) + else + Icon(Icons.arrow_forward_ios, color: _subTextColor, size: 16), + ], + ), + ), + ); + } + + Widget _buildDivider(double indent) { + return Divider(color: _subTextColor.withOpacity(0.4), height: 1, thickness: 0.5, indent: indent, endIndent: 16); + } + + Widget _buildWithdrawalButton() { + return Center( + child: TextButton( + onPressed: () {}, + child: Text( + '회원 탈퇴 신청', + style: TextStyle(color: _warningColor, fontWeight: FontWeight.bold), + ), ), ); } diff --git a/lib/widgets/custom_header.dart b/lib/widgets/custom_header.dart index c318c42..d1e3bbe 100644 --- a/lib/widgets/custom_header.dart +++ b/lib/widgets/custom_header.dart @@ -1,5 +1,10 @@ import 'package:flutter/material.dart'; +final Color _mainBlueColor = const Color(0xFF0033CC); +final Color _mainTextColor = const Color(0xFF1C1C1E); +final Color _subTextColor = const Color(0xFF6A717B); +final Color _pageBackgroundColor = const Color(0xFFF5F7F9); +final Color _cardBackgroundColor = const Color(0xFFF0F2F5); class CustomHeader extends StatelessWidget { const CustomHeader({super.key}); @@ -8,24 +13,42 @@ class CustomHeader extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - height: 60.0, - color: Theme.of(context).scaffoldBackgroundColor, - padding: const EdgeInsets.symmetric(horizontal: _uniformGap), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row(children: [ - Icon(Icons.settings_input_component, color: Colors.grey[400]), - const SizedBox(width: 12), - const Text('SMART HELMET SYSTEMS', - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.bold, - color: Colors.white)) - ]), - Text('2025/09/26 - 10:44 AM', - style: TextStyle(color: Colors.grey[400], fontSize: 11)) - ])); + height: 50.0, + decoration: BoxDecoration( + color: Colors.white, + boxShadow: const [ + BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.07), + offset: Offset(0, 4), + blurRadius: 6, + spreadRadius: 0, + ), + ], + ), + padding: const EdgeInsets.symmetric(horizontal: _uniformGap), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row(children: [ + Icon(Icons.settings_input_component, color: Colors.black), + const SizedBox(width: 10), + Text( + '스마트 헬멧', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: _mainTextColor, + letterSpacing: 0.5, + ), + ) + ]), + Text( + '2025/09/26 - 10:44 AM', + style: TextStyle(color: _subTextColor, fontSize: 11), + ), + ], + ), + ); } -} \ No newline at end of file +}