1.테마/컬러 수정

2. UI 텍스트 한국어 번역
3. SettingScreen(설정 페이지) -현재 페이지는 레이아웃만 완성되었으며, 토글 스위치 및 링크 연결 기능은 미완성 상태입니다.
This commit is contained in:
KIMGYEONGRAN
2025-12-01 17:55:38 +09:00
parent c1d8fc597d
commit acb297fec1
9 changed files with 1178 additions and 659 deletions

View File

@@ -8,10 +8,20 @@ class ControlScreen extends StatefulWidget {
} }
class _ControlScreenState extends State<ControlScreen> { class _ControlScreenState extends State<ControlScreen> {
final Color _bgColor = const Color(0xFF27292B); final Color _mainBlueColor = const Color(0xFF002FA7);
final Color _cardColor = const Color(0xFF30343B); final Color _mainTextColor = const Color(0xFF1C1C1E);
final Color _buttonDarkColor = const Color(0xFF212327); final Color _subTextColor = const Color(0xFF6A717B);
final Color _errorColor = Colors.redAccent; 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; bool _isSecurityLocked = true;
int _selectedDoorIndex = 0; int _selectedDoorIndex = 0;
@@ -19,23 +29,24 @@ class _ControlScreenState extends State<ControlScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: _bgColor, backgroundColor: _pageBackgroundColor,
appBar: AppBar( appBar: AppBar(
title: const Text( title: Text(
'CONTROL CENTER', '제어 센터',
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
fontSize: 16, fontSize: 18,
color: Colors.white, letterSpacing: 0.5,
color: _mainTextColor,
), ),
), ),
backgroundColor: _bgColor, backgroundColor: _pageBackgroundColor,
scrolledUnderElevation: 0, scrolledUnderElevation: 0,
elevation: 0, elevation: 0,
centerTitle: false, centerTitle: false,
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Icons.more_vert, color: Colors.white), icon: Icon(Icons.more_vert, color: _mainTextColor),
onPressed: () {}, onPressed: () {},
), ),
], ],
@@ -67,16 +78,17 @@ class _ControlScreenState extends State<ControlScreen> {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _cardColor, color: _cardBackgroundColor,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(8),
boxShadow: [_cleanShadow],
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'SYSTEM STATUS', '보관함 상태',
style: TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
@@ -92,7 +104,7 @@ class _ControlScreenState extends State<ControlScreen> {
height: double.infinity, height: double.infinity,
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _buttonDarkColor, color: _accentContainerColor,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Image.asset( child: Image.asset(
@@ -105,10 +117,10 @@ class _ControlScreenState extends State<ControlScreen> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'NOW', 'NOW',
style: TextStyle( style: TextStyle(
color: Colors.white, color: _subTextColor,
fontSize: 11, fontSize: 11,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -116,17 +128,17 @@ class _ControlScreenState extends State<ControlScreen> {
const SizedBox(height: 8), const SizedBox(height: 8),
Expanded( Expanded(
child: _buildStatusButton( child: _buildStatusButton(
text: 'ONLINE', text: '사용 중',
textColor: Colors.white, textColor: _mainBlueColor,
bgColor: _buttonDarkColor, bgColor: _accentContainerColor,
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
Expanded( Expanded(
child: _buildStatusButton( child: _buildStatusButton(
text: 'SENSOR ERROR:\nLock Failure', text: 'ERROR: 문 열림',
textColor: _errorColor, textColor: _warningColor,
bgColor: _buttonDarkColor, bgColor: _accentContainerColor,
isError: true, isError: true,
), ),
), ),
@@ -171,16 +183,17 @@ class _ControlScreenState extends State<ControlScreen> {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _cardColor, color: _cardBackgroundColor,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(8),
boxShadow: [_cleanShadow],
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'DOOR CONTROL', '문 잠금 제어',
style: TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
@@ -213,14 +226,14 @@ class _ControlScreenState extends State<ControlScreen> {
child: Container( child: Container(
height: 80, height: 80,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected ? Colors.white : _buttonDarkColor, color: isSelected ? _mainBlueColor : _accentContainerColor,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Center( child: Center(
child: Text( child: Text(
text, text,
style: TextStyle( style: TextStyle(
color: isSelected ? Colors.black : Colors.white, color: isSelected ? Colors.white : _mainTextColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 18, fontSize: 18,
), ),
@@ -235,16 +248,17 @@ class _ControlScreenState extends State<ControlScreen> {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _cardColor, color: _cardBackgroundColor,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(8),
boxShadow: [_cleanShadow],
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'Control Results & Alerts', '조작 기록 및 경고',
style: TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
@@ -253,7 +267,7 @@ class _ControlScreenState extends State<ControlScreen> {
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _buttonDarkColor, color: _accentContainerColor,
borderRadius: BorderRadius.circular(6), borderRadius: BorderRadius.circular(6),
), ),
child: Column( child: Column(
@@ -262,24 +276,24 @@ class _ControlScreenState extends State<ControlScreen> {
Row( Row(
children: [ children: [
Icon(Icons.warning_amber_rounded, Icon(Icons.warning_amber_rounded,
color: _errorColor, size: 16), color: _warningColor, size: 16),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded( Expanded(
child: RichText( child: RichText(
text: TextSpan( text: TextSpan(
children: [ children: [
TextSpan( TextSpan(
text: 'Sensor Alert: ', text: '센서 경고: ',
style: TextStyle( style: TextStyle(
color: _errorColor, color: _warningColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 12, fontSize: 12,
), ),
), ),
const TextSpan( TextSpan(
text: 'Door Not Fully Closed', text: '문 닫힘 불완전',
style: TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -295,8 +309,8 @@ class _ControlScreenState extends State<ControlScreen> {
borderRadius: BorderRadius.circular(4), borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator( child: LinearProgressIndicator(
value: 0.6, value: 0.6,
backgroundColor: Colors.grey[800], backgroundColor: _pageBackgroundColor,
valueColor: AlwaysStoppedAnimation<Color>(_errorColor), valueColor: AlwaysStoppedAnimation<Color>(_warningColor),
minHeight: 6, minHeight: 6,
), ),
), ),
@@ -312,16 +326,17 @@ class _ControlScreenState extends State<ControlScreen> {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _cardColor, color: _cardBackgroundColor,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(8),
boxShadow: [_cleanShadow],
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'Alert & Logs', '조작 기록 및 경고',
style: TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
@@ -330,36 +345,36 @@ class _ControlScreenState extends State<ControlScreen> {
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _buttonDarkColor, color: _accentContainerColor,
borderRadius: BorderRadius.circular(6), borderRadius: BorderRadius.circular(6),
), ),
child: const Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
children: [ children: [
Icon(Icons.warning_amber_rounded, Icon(Icons.warning_amber_rounded,
color: Colors.redAccent, size: 14), color: _warningColor, size: 14),
SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'Sensor Alert: Door Not Fully Closed', '센서 경고: 문 열림',
style: TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
], ],
), ),
SizedBox(height: 8), const SizedBox(height: 8),
Row( Row(
children: [ children: [
Icon(Icons.bookmark, color: Colors.white70, size: 14), Icon(Icons.bookmark, color: _subTextColor, size: 14),
SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'2:02 PM - UV LED Activated', '2:02 PM - UV LED 작동',
style: TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -378,16 +393,17 @@ class _ControlScreenState extends State<ControlScreen> {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _cardColor, color: _cardBackgroundColor,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(8),
boxShadow: [_cleanShadow],
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'Security Lock Mode', '도난 방지 잠금',
style: TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
@@ -397,7 +413,7 @@ class _ControlScreenState extends State<ControlScreen> {
padding: padding:
const EdgeInsets.symmetric(horizontal: 22, vertical: 16), const EdgeInsets.symmetric(horizontal: 22, vertical: 16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _buttonDarkColor, color: _accentContainerColor,
borderRadius: BorderRadius.circular(6), borderRadius: BorderRadius.circular(6),
), ),
child: Row( child: Row(
@@ -405,10 +421,10 @@ class _ControlScreenState extends State<ControlScreen> {
children: [ children: [
Text( Text(
_isSecurityLocked _isSecurityLocked
? 'ON (Activated)' ? 'ON'
: 'OFF (Deactivated)', : 'OFF',
style: const TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -422,10 +438,10 @@ class _ControlScreenState extends State<ControlScreen> {
_isSecurityLocked = val; _isSecurityLocked = val;
}); });
}, },
activeThumbColor: Colors.white, activeThumbColor: _mainBlueColor,
activeTrackColor: _errorColor, activeTrackColor: _mainBlueColor.withOpacity(0.5),
inactiveThumbColor: Colors.white, inactiveThumbColor: Colors.white,
inactiveTrackColor: Colors.grey, inactiveTrackColor: _accentContainerColor,
), ),
), ),
], ],

View File

@@ -32,16 +32,28 @@ class HistoryScreen extends StatefulWidget {
} }
class _HistoryScreenState extends State<HistoryScreen> { class _HistoryScreenState extends State<HistoryScreen> {
static const Color kBackgroundColor = Color(0xFF27292B); // 🟦 최종 확정 테마 컬러 정의 (대비 구조 교체)
static const Color kCardColor = Color(0xFF30343B); final Color _mainBlueColor = const Color(0xFF002FA7); // 진한 블루 (0xFF002FA7 -> 0xFF0A68FF로 통일)
static const Color kAccentColor = Colors.white; final Color _mainTextColor = const Color(0xFF1C1C1E); // 어두운 텍스트
static const Color kRedAccent = Color(0xFFFF5252); 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; int _selectedFilterIndex = 0;
final List<UsageHistory> _allHistoryList = [ final List<UsageHistory> _allHistoryList = [
UsageHistory( UsageHistory(
date: '2025.11.25 (Tue)', date: '2025.11.25 ()', // 🇰🇷 요일 변경
startTime: '18:30', startTime: '18:30',
endTime: '19:15', endTime: '19:15',
duration: 45, duration: 45,
@@ -52,7 +64,7 @@ class _HistoryScreenState extends State<HistoryScreen> {
isDried: true, isDried: true,
), ),
UsageHistory( UsageHistory(
date: '2025.11.23 (Sun)', date: '2025.11.23 ()', // 🇰🇷 요일 변경
startTime: '14:00', startTime: '14:00',
endTime: '15:30', endTime: '15:30',
duration: 90, duration: 90,
@@ -63,7 +75,7 @@ class _HistoryScreenState extends State<HistoryScreen> {
isDried: false, isDried: false,
), ),
UsageHistory( UsageHistory(
date: '2025.11.06 (Thu)', date: '2025.11.06 ()', // 🇰🇷 요일 변경
startTime: '09:00', startTime: '09:00',
endTime: '09:30', endTime: '09:30',
duration: 30, duration: 30,
@@ -74,7 +86,7 @@ class _HistoryScreenState extends State<HistoryScreen> {
isDried: true, isDried: true,
), ),
UsageHistory( UsageHistory(
date: '2025.09.20 (Sat)', date: '2025.09.20 ()', // 🇰🇷 요일 변경
startTime: '20:00', startTime: '20:00',
endTime: '21:00', endTime: '21:00',
duration: 60, duration: 60,
@@ -120,16 +132,16 @@ class _HistoryScreenState extends State<HistoryScreen> {
final currentList = _filteredList; final currentList = _filteredList;
return Scaffold( return Scaffold(
backgroundColor: kBackgroundColor, backgroundColor: _pageBackgroundColor,
appBar: AppBar( appBar: AppBar(
title: const Text('HISTORY', style: TextStyle(fontWeight: FontWeight title: Text('나의 기록', style: TextStyle(fontWeight: FontWeight
.bold, fontSize: 16, color: Colors.white)), .bold, fontSize: 18, letterSpacing: 0.5, color: _mainTextColor)),
backgroundColor: kBackgroundColor, backgroundColor: _pageBackgroundColor,
scrolledUnderElevation: 0, scrolledUnderElevation: 0,
elevation: 0, elevation: 0,
centerTitle: false, centerTitle: false,
actions: [ actions: [
IconButton(icon: const Icon(Icons.filter_list, color: Colors.white), IconButton(icon: Icon(Icons.filter_list, color: _mainTextColor),
onPressed: () {}), onPressed: () {}),
], ],
), ),
@@ -138,8 +150,8 @@ class _HistoryScreenState extends State<HistoryScreen> {
_buildFilterTabs(), _buildFilterTabs(),
Expanded( Expanded(
child: currentList.isEmpty child: currentList.isEmpty
? const Center( ? Center(
child: Text("기록이 없습니다.", style: TextStyle(color: Colors.grey))) child: Text("기록이 없습니다.", style: TextStyle(color: _subTextColor)))
: ListView.builder( : ListView.builder(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
itemCount: currentList.length, itemCount: currentList.length,
@@ -183,15 +195,15 @@ class _HistoryScreenState extends State<HistoryScreen> {
onSelected: (selected) { onSelected: (selected) {
setState(() => _selectedFilterIndex = index); setState(() => _selectedFilterIndex = index);
}, },
backgroundColor: kCardColor, backgroundColor: _cardBackgroundColor,
selectedColor: kAccentColor, selectedColor: _mainBlueColor,
checkmarkColor: Colors.black, checkmarkColor: Colors.white,
labelStyle: TextStyle( labelStyle: TextStyle(
color: isSelected ? Colors.black : Colors.white, color: isSelected ? Colors.white : _mainTextColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20), side: BorderSide.none), borderRadius: BorderRadius.circular(8), side: BorderSide.none),
); );
}, },
), ),
@@ -203,8 +215,8 @@ class _HistoryScreenState extends State<HistoryScreen> {
padding: const EdgeInsets.symmetric(vertical: 16.0), padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Text( child: Text(
date, date,
style: const TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.bold fontWeight: FontWeight.bold
), ),
@@ -216,12 +228,13 @@ class _HistoryScreenState extends State<HistoryScreen> {
return Container( return Container(
margin: const EdgeInsets.only(bottom: 12), margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: kCardColor, color: _cardBackgroundColor,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(8),
boxShadow: [_cleanShadow]
), ),
child: InkWell( child: InkWell(
onTap: () => _showHistoryDetail(context, history), onTap: () => _showHistoryDetail(context, history),
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(8),
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
@@ -231,7 +244,7 @@ class _HistoryScreenState extends State<HistoryScreen> {
children: [ children: [
Text( Text(
'${history.startTime} - ${history.endTime}', '${history.startTime} - ${history.endTime}',
style: const TextStyle(color: Colors.white, style: TextStyle(color: _mainTextColor,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold), fontWeight: FontWeight.bold),
), ),
@@ -239,12 +252,12 @@ class _HistoryScreenState extends State<HistoryScreen> {
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4), horizontal: 8, vertical: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: _mainBlueColor,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Text( child: Text(
'${history.duration} min', '${history.duration}', // 🇰🇷 min -> 분
style: const TextStyle(color: Colors.black, style: const TextStyle(color: Colors.white,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600), fontWeight: FontWeight.w600),
), ),
@@ -254,54 +267,53 @@ class _HistoryScreenState extends State<HistoryScreen> {
const SizedBox(height: 12), const SizedBox(height: 12),
Row( Row(
children: [ children: [
_buildDot(Colors.grey), _buildDot(_subTextColor),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded(child: Text(history.startStation, Expanded(child: Text(history.startStation,
style: const TextStyle(color: Colors.white, fontSize: 13), style: TextStyle(color: _mainTextColor, fontSize: 13),
overflow: TextOverflow.ellipsis)), overflow: TextOverflow.ellipsis)),
], ],
), ),
Container( Container(
margin: const EdgeInsets.only(left: 3), margin: const EdgeInsets.only(left: 3),
height: 10, height: 10,
decoration: const BoxDecoration( decoration: BoxDecoration(
border: Border( border: Border(
left: BorderSide(color: Colors.grey, width: 1)), left: BorderSide(color: _subTextColor.withOpacity(0.5), width: 1)),
), ),
), ),
Row( Row(
children: [ children: [
_buildDot(kRedAccent), _buildDot(_mainBlueColor),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded(child: Text(history.endStation, Expanded(child: Text(history.endStation,
style: const TextStyle(color: Colors.white, fontSize: 13), style: TextStyle(color: _mainTextColor, fontSize: 13),
overflow: TextOverflow.ellipsis)), overflow: TextOverflow.ellipsis)),
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
const Divider(color: Colors.white, thickness: 0.5,), Divider(color: _subTextColor.withOpacity(0.5), thickness: 0.5,),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Row( Row(
children: [ children: [
if (history.isSanitized) if (history.isSanitized)
_buildDot(kRedAccent), _buildDot(_mainBlueColor),
if (history.isSanitized) if (history.isSanitized)
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
history.isSanitized ? "살균 완료" : "Pending", history.isSanitized ? "살균 완료" : "진행 중", // 🇰🇷 Pending -> 진행 중
style: TextStyle( style: TextStyle(
color: history.isSanitized ? Colors.white : Colors color: history.isSanitized ? _mainTextColor : _subTextColor,
.white,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.bold), fontWeight: FontWeight.bold),
), ),
], ],
), ),
const Text( Text(
"VIEW DETAILS >", "상세 보기 >", // 🇰🇷 VIEW DETAILS > -> 상세 보기 >
style: TextStyle(color: Colors.white, fontSize: 11), style: TextStyle(color: _subTextColor, fontSize: 11),
), ),
], ],
), ),
@@ -330,16 +342,17 @@ class _HistoryScreenState extends State<HistoryScreen> {
.of(context) .of(context)
.size .size
.height * 0.75, .height * 0.75,
decoration: const BoxDecoration( decoration: BoxDecoration(
color: kBackgroundColor, color: _cardBackgroundColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(24)), borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
boxShadow: [_cleanShadow]
), ),
child: Column( child: Column(
children: [ children: [
const SizedBox(height: 12), const SizedBox(height: 12),
Container(width: 40, Container(width: 40,
height: 4, height: 4,
decoration: BoxDecoration(color: Colors.grey[600], decoration: BoxDecoration(color: _subTextColor,
borderRadius: BorderRadius.circular(2))), borderRadius: BorderRadius.circular(2))),
Expanded( Expanded(
child: SingleChildScrollView( child: SingleChildScrollView(
@@ -347,45 +360,45 @@ class _HistoryScreenState extends State<HistoryScreen> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text("HISTORY DETAILS", style: TextStyle( Text("이용 상세 정보", style: TextStyle( // 🇰🇷 HISTORY DETAILS
color: Colors.white, color: _mainTextColor,
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.bold)), fontWeight: FontWeight.bold)),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
history.date, history.date,
style: const TextStyle(color: Colors.white), style: TextStyle(color: _subTextColor),
), ),
const SizedBox(height: 30), const SizedBox(height: 30),
Container( Container(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
decoration: BoxDecoration(color: kCardColor, decoration: BoxDecoration(color: _accentContainerColor,
borderRadius: BorderRadius.circular(16)), borderRadius: BorderRadius.circular(8)),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text("이용 시간", style: TextStyle( Text("이용 시간", style: TextStyle( // 🇰🇷 이용 시간
color: Colors.white, fontSize: 12)), color: _subTextColor, fontSize: 12)),
const SizedBox(height: 4), const SizedBox(height: 4),
Text("${history.duration} min", Text("${history.duration}", // 🇰🇷 min -> 분
style: const TextStyle(color: Colors.white, style: TextStyle(color: _mainTextColor,
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.bold)), fontWeight: FontWeight.bold)),
], ],
), ),
Container( Container(
width: 1, height: 40, color: Colors.white24), width: 1, height: 40, color: _subTextColor.withOpacity(0.5)),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text("이용 금액", style: TextStyle( Text("이용 금액", style: TextStyle( // 🇰🇷 이용 금액
color: Colors.white, fontSize: 12)), color: _subTextColor, fontSize: 12)),
const SizedBox(height: 4), const SizedBox(height: 4),
Text("$cost", style: const TextStyle( Text("$cost", style: TextStyle( // 🇰🇷 ₩ 위치 수정
color: Colors.white, color: _mainTextColor,
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.bold)), fontWeight: FontWeight.bold)),
], ],
@@ -394,8 +407,8 @@ class _HistoryScreenState extends State<HistoryScreen> {
), ),
), ),
const SizedBox(height: 30), const SizedBox(height: 30),
const Text("TIMELINE", style: TextStyle( Text("시간별 기록", style: TextStyle( // 🇰🇷 TIMELINE
color: Colors.white, color: _mainTextColor,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
letterSpacing: 1.0)), letterSpacing: 1.0)),
@@ -407,14 +420,14 @@ class _HistoryScreenState extends State<HistoryScreen> {
history.endTime, "반납 완료", history.endStation, history.endTime, "반납 완료", history.endStation,
isCompleted: true), isCompleted: true),
_buildTimelineItem( _buildTimelineItem(
history.endTime, "살균 시작", "Auto-Cleaning System", history.endTime, "살균 시작", "자동 살균 시스템", // 🇰🇷 Auto-Cleaning System
isCompleted: true), isCompleted: true),
_buildTimelineItem( _buildTimelineItem(
"15 min later", "살균 완료", "Ready for next user", "15분 후", "살균 완료", "다음 사용자 준비 완료", // 🇰🇷 15 min later, Ready for next user
isLast: true, isCompleted: history.isSanitized), isLast: true, isCompleted: history.isSanitized),
const SizedBox(height: 30), const SizedBox(height: 30),
const Text("CONDITION", style: TextStyle( Text("살균/건조 상태", style: TextStyle( // 🇰🇷 CONDITION
color: Colors.white, color: _mainTextColor,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold)), fontWeight: FontWeight.bold)),
const SizedBox(height: 8), const SizedBox(height: 8),
@@ -442,25 +455,28 @@ class _HistoryScreenState extends State<HistoryScreen> {
Widget _buildTimelineItem(String time, String title, String subtitle, Widget _buildTimelineItem(String time, String title, String subtitle,
{bool isFirst = false, bool isLast = false, bool isCompleted = false}) { {bool isFirst = false, bool isLast = false, bool isCompleted = false}) {
// 🇰🇷 15 min later -> 15분 후 로직 처리
String displayTime = time.contains("min later") ? "15분 후" : time;
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox( SizedBox(
width: 60, width: 60,
child: Text( child: Text(
time, style: const TextStyle(color: Colors.white, fontSize: 12)), displayTime, style: TextStyle(color: _mainTextColor, fontSize: 12)),
), ),
Column( Column(
children: [ children: [
Icon( Icon(
isCompleted ? Icons.check_circle : Icons.radio_button_unchecked, isCompleted ? Icons.check_circle : Icons.radio_button_unchecked,
color: isCompleted ? kRedAccent : Colors.grey, size: 20), color: isCompleted ? _mainBlueColor : _subTextColor, size: 20),
if (!isLast) if (!isLast)
Container(width: 2, Container(width: 2,
height: 40, height: 40,
color: isCompleted color: isCompleted
? kRedAccent.withValues(alpha: 0.5) ? _mainBlueColor.withOpacity(0.5)
: Colors.grey[800]), : Colors.grey.shade300),
], ],
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
@@ -469,12 +485,12 @@ class _HistoryScreenState extends State<HistoryScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(title, style: TextStyle( Text(title, style: TextStyle(
color: isCompleted ? Colors.white : Colors.grey, color: isCompleted ? _mainTextColor : _subTextColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 14)), fontSize: 14)),
const SizedBox(height: 4), const SizedBox(height: 4),
Text(subtitle, Text(subtitle,
style: TextStyle(color: Colors.white, fontSize: 12)), style: TextStyle(color: _subTextColor, fontSize: 12)),
const SizedBox(height: 24), const SizedBox(height: 24),
], ],
), ),
@@ -489,29 +505,30 @@ class _HistoryScreenState extends State<HistoryScreen> {
width: double.infinity, width: double.infinity,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: kCardColor, color: _accentContainerColor, // 내부 상세 박스 배경 (중간 그레이)
boxShadow: isDone ? [_cleanShadow] : null,
border: Border.all( border: Border.all(
color: isDone ? Colors.white : Colors.transparent, width: 1.0), color: isDone ? _mainBlueColor.withOpacity(0.9) : _subTextColor.withOpacity(0.3), width: 1.0),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(8),
), ),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon(icon, color: Colors.white, size: 28), Icon(icon, color: _mainBlueColor, size: 28),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
title, title,
style: const TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
isDone ? "Completed" : "In Progress", isDone ? "완료됨" : "진행 중", // 🇰🇷 Completed/In Progress
style: const TextStyle( style: TextStyle(
color: Colors.white, color: _subTextColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 14 fontSize: 14
), ),

View File

@@ -14,7 +14,22 @@ class HomeScreenContent extends StatefulWidget {
class _HomeScreenContentState extends State<HomeScreenContent> { class _HomeScreenContentState extends State<HomeScreenContent> {
static const double _uniformGap = 16.0; 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<String, bool> _controlToggles = { final Map<String, bool> _controlToggles = {
'UV LED': false, 'UV LED': false,
@@ -25,53 +40,60 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Container(
children: [ color: _pageBackgroundColor,
const CustomHeader(), child: Column(
children: [
Expanded( const CustomHeader(),
child: SingleChildScrollView( Expanded(
padding: const EdgeInsets.symmetric(horizontal: _uniformGap), child: SingleChildScrollView(
child: Column( padding: const EdgeInsets.symmetric(horizontal: _uniformGap),
children: [ child: Column(
const SizedBox(height: _uniformGap), children: [
_buildOverviewSection(), const SizedBox(height: _uniformGap),
const SizedBox(height: _uniformGap), _buildOverviewSection(),
_buildBatteryStatusCard(), const SizedBox(height: _uniformGap),
const SizedBox(height: _uniformGap), _buildBatteryStatusCard(),
_buildControlCard(), const SizedBox(height: _uniformGap),
const SizedBox(height: _uniformGap), _buildControlCard(),
_buildEnvironmentSensorsCard(), const SizedBox(height: _uniformGap),
const SizedBox(height: _uniformGap), _buildEnvironmentSensorsCard(),
_buildMyLocationCard(), const SizedBox(height: _uniformGap),
const SizedBox(height: _uniformGap), _buildMyLocationCard(),
_buildActivityCard(), const SizedBox(height: _uniformGap),
const SizedBox(height: _uniformGap * 2), _buildActivityCard(),
], const SizedBox(height: _uniformGap * 2),
],
),
), ),
), ),
), ],
], ),
); );
} }
Widget _buildOverviewSection() { Widget _buildOverviewSection() {
return Card( return Container(
child: Column( margin: const EdgeInsets.only(top: 5),
children: [ child: Card(
_buildOverviewHeader(), shadow: _cleanShadow,
Padding( cardColor: _cardBackgroundColor,
padding: const EdgeInsets.all(12.0), child: Column(
child: Row( children: [
crossAxisAlignment: CrossAxisAlignment.start, _buildOverviewHeader(),
children: [ Padding(
Expanded(child: _buildImageCard()), padding: const EdgeInsets.all(12.0),
const SizedBox(width: 12), child: Row(
Expanded(child: _buildInfoCard()), crossAxisAlignment: CrossAxisAlignment.start,
], children: [
Expanded(child: _buildImageCard()),
const SizedBox(width: 12),
Expanded(child: _buildInfoCard()),
],
),
), ),
), ],
], ),
), ),
); );
} }
@@ -81,15 +103,15 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), padding: const EdgeInsets.fromLTRB(12, 12, 12, 0),
child: Row( child: Row(
children: [ children: [
const Text('SYSTEM OVERVIEW', Text('장치 개요',
style: TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontSize: 11, fontSize: 11,
fontWeight: FontWeight.bold)), fontWeight: FontWeight.bold)),
const Spacer(), const Spacer(),
Icon(Icons.search, color: Colors.grey[400], size: 20), Icon(Icons.search, color: _subTextColor, size: 20),
const SizedBox(width: 8), 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<HomeScreenContent> {
child: Container( child: Container(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.black.withOpacity(0.3), color: _accentContainerColor,
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(8),
), ),
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF2D2F33), color: _cardBackgroundColor,
borderRadius: BorderRadius.circular(5) borderRadius: BorderRadius.circular(5),
), ),
), ),
Padding( Padding(
padding: const EdgeInsets.only(bottom: 20.0), padding: const EdgeInsets.only(bottom: 20.0),
child: Image.asset( child: Image.asset(
'assets/images/open.png', 'assets/images/storage.png',
width: 120, width: 90,
), ),
), ),
Positioned( Positioned(
bottom: 12, bottom: 12,
child: Row( child: Row(
children: [ children: [
_buildLedIndicator(Colors.grey.shade700), _buildLedIndicator(Colors.grey.shade300),
const SizedBox(width: 4), const SizedBox(width: 4),
_buildLedIndicator(Colors.grey.shade700), _buildLedIndicator(Colors.grey.shade300),
const SizedBox(width: 4), const SizedBox(width: 4),
_buildLedIndicator(Colors.grey.shade700), _buildLedIndicator(Colors.grey.shade300),
const SizedBox(width: 4), const SizedBox(width: 4),
_buildLedIndicator(Colors.white), _buildLedIndicator(_mainTextColor),
], ],
), ),
) )
@@ -153,25 +175,28 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
Widget _buildInfoCard() { Widget _buildInfoCard() {
return SizedBox( return SizedBox(
height: 160, height: 164,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ 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), 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( return Expanded(
child: Container( child: Container(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.black.withOpacity(0.3), color: _accentContainerColor,
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(8),
), ),
child: Row( child: Row(
children: [ children: [
@@ -179,18 +204,18 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
flex: 2, flex: 2,
child: Text(title, child: Text(title,
textAlign: TextAlign.center, 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( Expanded(
flex: 3, flex: 3,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
if (icon != null) ...[icon, const SizedBox(height: 4)], 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), 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<HomeScreenContent> {
Widget _buildBatteryStatusCard() { Widget _buildBatteryStatusCard() {
return Card( return Card(
shadow: _cleanShadow,
cardColor: _cardBackgroundColor,
child: Padding( child: Padding(
padding: const EdgeInsets.all(_uniformGap), padding: const EdgeInsets.all(_uniformGap),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('BATTERY STATUS (%)', Text('배터리 상태 (%)', style: TextStyle(color: _mainTextColor, fontSize: 12, fontWeight: FontWeight.bold)),
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold)),
const SizedBox(height: 16), const SizedBox(height: 16),
Row( Row(
children: [ children: [
@@ -224,15 +247,13 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
SizedBox.expand( SizedBox.expand(
child: CustomPaint( child: CustomPaint(
painter: _BatteryArcPainter( painter: _BatteryArcPainter(
backgroundColor: Colors.grey.shade800, backgroundColor: _accentContainerColor,
color: Colors.white, color: _mainBlueColor,
percentage: 1.0, percentage: 1.0,
), ),
), ),
), ),
const Text('86', Text('86', style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: _mainTextColor)),
style: TextStyle(
fontSize: 24, fontWeight: FontWeight.w600)),
], ],
), ),
), ),
@@ -243,55 +264,30 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Text('NOW', Text('현재', style: TextStyle(color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.bold)),
style: TextStyle( Text('사용 중', style: TextStyle(color: _mainBlueColor, fontSize: 14, fontWeight: FontWeight.bold)),
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.bold)),
Text('사용 중',
style: TextStyle(
color: _pointColor,
fontSize: 14,
fontWeight: FontWeight.bold)),
], ],
), ),
const Divider( Divider(color: _subTextColor.withOpacity(0.5), height: 20, thickness: 1),
color: Color(0xFF555555),
height: 20,
thickness: 1,
),
Row( Row(
children: [ children: [
const Expanded( Expanded(
flex: 4, flex: 4,
child: Text('Solar Panel', child: Text('태양광 패널', style: TextStyle(color: _mainTextColor, fontSize: 14)),
style: TextStyle(
color: Colors.white, fontSize: 14)),
), ),
Expanded( Expanded(
flex: 5, flex: 5,
child: Row( child: Row(
children: [ children: [
const Expanded( Expanded(child: Text('전압: 00', style: TextStyle(color: _subTextColor, fontSize: 14))),
child: Text('전압: 00',
style: TextStyle(
color: Colors.white70,
fontSize: 14)),
),
SizedBox( SizedBox(
height: 20, height: 20,
child: VerticalDivider( child: VerticalDivider(color: _subTextColor.withOpacity(0.5), thickness: 1),
color: Colors.grey[700],
thickness: 1,
),
), ),
const Expanded( Expanded(
child: Padding( child: Padding(
padding: EdgeInsets.only(left: 8.0), padding: const EdgeInsets.only(left: 8.0),
child: Text('전류: 00', child: Text('전류: 00', style: TextStyle(color: _subTextColor, fontSize: 14)),
style: TextStyle(
color: Colors.white70,
fontSize: 14)),
), ),
), ),
], ],
@@ -312,16 +308,14 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
Widget _buildControlCard() { Widget _buildControlCard() {
return Card( return Card(
shadow: _cleanShadow,
cardColor: _cardBackgroundColor,
child: Padding( child: Padding(
padding: const EdgeInsets.all(_uniformGap), padding: const EdgeInsets.all(_uniformGap),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('CONTROL', Text('', style: TextStyle(color: _mainTextColor, fontSize: 11, fontWeight: FontWeight.bold)),
style: TextStyle(
color: Colors.white,
fontSize: 11,
fontWeight: FontWeight.bold)),
const SizedBox(height: 16), const SizedBox(height: 16),
SizedBox( SizedBox(
height: 70, height: 70,
@@ -332,24 +326,22 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
'UV LED', 'UV LED',
_controlToggles['UV LED']!, _controlToggles['UV LED']!,
(val) => setState(() => _controlToggles['UV LED'] = val))), (val) => setState(() => _controlToggles['UV LED'] = val))),
VerticalDivider( VerticalDivider(color: _subTextColor.withOpacity(0.5), indent: 10, endIndent: 10),
color: Colors.grey[700], indent: 10, endIndent: 10),
Expanded( Expanded(
child: _buildStyledToggleSwitch( child: _buildStyledToggleSwitch(
'CHARGING', 'CHARGING',
_controlToggles['CHARGING']!, _controlToggles['CHARGING']!,
(val) => setState(() => _controlToggles['CHARGING'] = val))), (val) => setState(() => _controlToggles['CHARGING'] = val))),
VerticalDivider( VerticalDivider(color: _subTextColor.withOpacity(0.5), indent: 10, endIndent: 10),
color: Colors.grey[700], indent: 10, endIndent: 10),
Expanded( Expanded(
child: _buildStyledToggleSwitch( child: _buildStyledToggleSwitch(
'HELMET', 'HELMET',
_controlToggles['HELMET']!, _controlToggles['HELMET']!,
(val) => setState(() => _controlToggles['HELMET'] = val))), (val) => setState(() => _controlToggles['HELMET'] = val))),
VerticalDivider( VerticalDivider(color: _subTextColor.withOpacity(0.5), indent: 10, endIndent: 10),
color: Colors.grey[700], indent: 10, endIndent: 10),
Expanded( Expanded(
child: _buildStyledToggleSwitch('FAN', _controlToggles['FAN']!, child: _buildStyledToggleSwitch('FAN',
_controlToggles['FAN']!,
(val) => setState(() => _controlToggles['FAN'] = val))), (val) => setState(() => _controlToggles['FAN'] = val))),
], ],
), ),
@@ -360,16 +352,11 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
); );
} }
Widget _buildStyledToggleSwitch( Widget _buildStyledToggleSwitch(String title, bool value, ValueChanged<bool> onChanged) {
String title, bool value, ValueChanged<bool> onChanged) {
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text(title, Text(title, style: TextStyle(color: _mainTextColor, fontSize: 13, fontWeight: FontWeight.bold)),
style: const TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.bold)),
const SizedBox(height: 12), const SizedBox(height: 12),
GestureDetector( GestureDetector(
onTap: () => onChanged(!value), onTap: () => onChanged(!value),
@@ -380,7 +367,7 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
height: 30, height: 30,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15), borderRadius: BorderRadius.circular(15),
color: value ? Colors.white : Colors.grey.shade700, color: value ? _mainTextColor.withOpacity(0.8) : _toggleOffTrackColor,
), ),
child: Stack( child: Stack(
children: [ children: [
@@ -395,7 +382,8 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
height: 26, height: 26,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, 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<HomeScreenContent> {
style: TextStyle( style: TextStyle(
fontSize: 10, fontSize: 10,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: value color: value ? Colors.white : Colors.transparent)))),
? const Color(0xFF27292B)
: Colors.transparent)))),
Expanded( Expanded(
child: Center( child: Center(
child: Text('OFF', child: Text('OFF',
style: TextStyle( style: TextStyle(
fontSize: 10, fontSize: 10,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: value color: value ? Colors.transparent : _mainTextColor)))),
? Colors.transparent
: Colors.white)))),
], ],
) )
], ],
@@ -432,6 +416,8 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
Widget _buildEnvironmentSensorsCard() { Widget _buildEnvironmentSensorsCard() {
return Card( return Card(
shadow: _cleanShadow,
cardColor: _cardBackgroundColor,
child: Padding( child: Padding(
padding: const EdgeInsets.all(_uniformGap), padding: const EdgeInsets.all(_uniformGap),
child: Column( child: Column(
@@ -439,20 +425,15 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
const Text('ENVIRONMENT SENSORS', Text('환경 센서', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: _mainTextColor)),
style:
TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
const Spacer(), const Spacer(),
InkWell( InkWell(
onTap: () {}, onTap: () {},
child: Row( child: Row(
children: [ children: [
Text('VIEW HISTORY', Text('기록 보기', style: TextStyle(color: _mainTextColor, fontSize: 10)),
style:
TextStyle(color: Colors.grey[400], fontSize: 10)),
const SizedBox(width: 4), const SizedBox(width: 4),
Icon(Icons.arrow_forward_ios, Icon(Icons.arrow_forward_ios, size: 10, color: _subTextColor),
size: 10, color: Colors.grey[400]),
], ],
), ),
), ),
@@ -466,17 +447,18 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildSensorInfoRow(Icons.water_drop_outlined, 'HUMID: 60%'), _buildSensorInfoRow(Icons.water_drop_outlined, '습도: 60%', '습도'),
const SizedBox(height: 24), const SizedBox(height: 24),
_buildSensorInfoRow(Icons.thermostat, 'TEMP: 24.5℃'), _buildSensorInfoRow(Icons.thermostat, '온도: 24.5℃', '온도'),
], ],
), ),
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
const Expanded( Expanded(
flex: 1, flex: 1,
child: SizedBox( child: SizedBox(
height: 60, child: _LineChartPlaceholder())), height: 60,
child: _LineChartPlaceholder(lineColor: _mainTextColor, subLabelColor: _subTextColor))),
], ],
), ),
], ],
@@ -485,29 +467,27 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
); );
} }
Widget _buildSensorInfoRow(IconData icon, String text) { Widget _buildSensorInfoRow(IconData icon, String text, String type) {
return Row(children: [ return Row(children: [
Container( Container(
width: 40, width: 40,
height: 40, height: 40,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: _accentContainerColor,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Icon(icon, color: Colors.black, size: 24), child: Icon(icon, color: _mainTextColor, size: 24),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Text(text, Text(text, style: TextStyle(fontSize: 14, color: _mainTextColor, fontWeight: FontWeight.w600))
style: const TextStyle(
fontSize: 14,
color: Colors.white,
fontWeight: FontWeight.w600))
]); ]);
} }
Widget _buildMyLocationCard() { Widget _buildMyLocationCard() {
const LatLng exampleLocation = LatLng(37.5665, 126.9780); const LatLng exampleLocation = LatLng(37.5665, 126.9780);
return Card( return Card(
shadow: _cleanShadow,
cardColor: _cardBackgroundColor,
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: SizedBox( child: SizedBox(
height: 200.0, height: 200.0,
@@ -518,30 +498,21 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('My Location', Text('현재 위치', style: TextStyle(fontSize: 13, color: _mainTextColor, fontWeight: FontWeight.bold)),
style: TextStyle( const SizedBox(height: 4),
fontSize: 13, Text('주소: 남구 효덕로 277', style: TextStyle(fontSize: 11, color: _mainTextColor)),
color: Colors.white,
fontWeight: FontWeight.bold)),
SizedBox(height: 4),
Text('주소: 남구 효덕로 277',
style:
TextStyle(fontSize: 11, color: Colors.white70)),
], ],
), ),
InkWell( InkWell(
onTap: () {}, onTap: () {},
child: Row( child: Row(
children: [ children: [
Text('VIEW MORE', Text('더 보기', style: TextStyle(color: _mainTextColor, fontSize: 9)),
style:
TextStyle(color: Colors.grey[400], fontSize: 9)),
const SizedBox(width: 4), const SizedBox(width: 4),
Icon(Icons.arrow_forward_ios, Icon(Icons.arrow_forward_ios, size: 10, color: _subTextColor),
size: 10, color: Colors.grey[400]),
], ],
), ),
), ),
@@ -553,25 +524,22 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
options: const MapOptions( options: const MapOptions(
initialCenter: exampleLocation, initialCenter: exampleLocation,
initialZoom: 15.0, initialZoom: 15.0,
interactionOptions: interactionOptions: InteractionOptions(flags: InteractiveFlag.none),
InteractionOptions(flags: InteractiveFlag.none),
), ),
children: [ children: [
TileLayer( TileLayer(
urlTemplate: urlTemplate: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',
'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
userAgentPackageName: 'com.example.app', userAgentPackageName: 'com.example.app',
subdomains: const ['a', 'b', 'c', 'd'], subdomains: const ['a', 'b', 'c', 'd'],
retinaMode: true, retinaMode: true,
), ),
const MarkerLayer( MarkerLayer(
markers: [ markers: [
Marker( Marker(
point: exampleLocation, point: exampleLocation,
width: 80, width: 80,
height: 80, height: 80,
child: Icon(Icons.location_pin, child: Icon(Icons.location_pin, size: 40, color: _mainTextColor),
size: 40, color: Colors.white),
), ),
], ],
), ),
@@ -586,15 +554,17 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
Widget _buildActivityCard() { Widget _buildActivityCard() {
return Card( return Card(
shadow: _cleanShadow,
cardColor: _cardBackgroundColor,
child: Padding( child: Padding(
padding: const EdgeInsets.all(_uniformGap), padding: const EdgeInsets.all(_uniformGap),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('Activity', Text('Activity',
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
color: Colors.white, color: _mainTextColor,
fontWeight: FontWeight.bold)), fontWeight: FontWeight.bold)),
const SizedBox(height: 12), const SizedBox(height: 12),
Row( Row(
@@ -630,35 +600,44 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
Widget _activityText(String text) { Widget _activityText(String text) {
return Text(text, return Text(text,
style: const TextStyle(fontSize: 11, color: Colors.white70)); style: TextStyle(fontSize: 11, color: _mainTextColor));
} }
} }
class _LineChartPlaceholder extends StatelessWidget { class _LineChartPlaceholder extends StatelessWidget {
const _LineChartPlaceholder(); final Color lineColor;
final Color subLabelColor;
const _LineChartPlaceholder({
super.key,
required this.lineColor,
required this.subLabelColor
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column(children: [ return Column(children: [
Expanded( Expanded(
child: child:
CustomPaint(painter: _LineChartPainter(), size: Size.infinite)), CustomPaint(painter: _LineChartPainter(color: lineColor), size: Size.infinite)),
const SizedBox(height: 4), const SizedBox(height: 4),
const Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Text('24H AGO', style: TextStyle(fontSize: 8, color: Colors.white54)), Text('24H AGO', style: TextStyle(fontSize: 8, color: subLabelColor)),
Text('12H AGO', style: TextStyle(fontSize: 8, color: Colors.white54)), Text('12H AGO', style: TextStyle(fontSize: 8, color: subLabelColor)),
Text('NOW', style: TextStyle(fontSize: 8, color: Colors.white54)) Text('NOW', style: TextStyle(fontSize: 8, color: subLabelColor))
]) ])
]); ]);
} }
} }
class _LineChartPainter extends CustomPainter { class _LineChartPainter extends CustomPainter {
final Color color;
_LineChartPainter({required this.color});
@override @override
void paint(ui.Canvas canvas, ui.Size size) { void paint(ui.Canvas canvas, ui.Size size) {
final paint = Paint() final paint = Paint()
..color = Colors.white.withOpacity(0.8) ..color = color
..strokeWidth = 1.5 ..strokeWidth = 1.5
..style = PaintingStyle.stroke; ..style = PaintingStyle.stroke;
@@ -683,12 +662,18 @@ class Card extends StatelessWidget {
final Widget child; final Widget child;
final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? padding;
final Clip clipBehavior; final Clip clipBehavior;
final Color? borderColor;
final Color? cardColor;
final BoxShadow? shadow;
const Card({ const Card({
super.key, super.key,
required this.child, required this.child,
this.padding, this.padding,
this.clipBehavior = Clip.none, this.clipBehavior = Clip.none,
this.borderColor,
this.cardColor,
this.shadow,
}); });
@override @override
@@ -696,8 +681,9 @@ class Card extends StatelessWidget {
return Container( return Container(
clipBehavior: clipBehavior, clipBehavior: clipBehavior,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).primaryColor, color: cardColor ?? Colors.white,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(8),
boxShadow: shadow != null ? [shadow!] : null,
), ),
child: child, child: child,
); );
@@ -723,7 +709,6 @@ class _BatteryArcPainter extends CustomPainter {
..style = PaintingStyle.stroke ..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round; ..strokeCap = StrokeCap.round;
final Paint foregroundPaint = Paint() final Paint foregroundPaint = Paint()
..color = color ..color = color
..strokeWidth = 8 ..strokeWidth = 8

View File

@@ -1,105 +1,188 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'main.dart'; import 'main.dart';
class LoginScreen extends StatelessWidget { class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final TextEditingController _idController = TextEditingController(); final TextEditingController _idController = TextEditingController();
final TextEditingController _pwController = 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: const Color(0xFF1E1E1E), backgroundColor: Colors.white,
body: Padding( body: SafeArea(
padding: const EdgeInsets.all(24.0), child: Padding(
child: Column( padding: const EdgeInsets.all(24.0),
mainAxisAlignment: MainAxisAlignment.center, child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center,
children: [ crossAxisAlignment: CrossAxisAlignment.stretch,
const Icon(Icons.polymer, size: 80, color: Colors.white), children: [
const SizedBox(height: 20), Icon(Icons.polymer, size: 80, color: mainBlueColor),
const Text( const SizedBox(height: 20),
'METAQLAB' Text(
'', 'METAQLAB',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
color: Colors.white, color: mainBlueColor,
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
letterSpacing: 1.2, 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),
), ),
), ),
child: const Text( const SizedBox(height: 60),
'LOGIN',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 20), _buildCustomTextField(
TextButton( label: '아이디',
onPressed: () {}, controller: _idController,
child: const Text('Forgot Password?', style: TextStyle(color: Colors.grey)), 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}) { Widget _buildCustomTextField({
return TextField( required String label,
controller: controller, required TextEditingController controller,
obscureText: isObscure, required FocusNode focusNode,
style: const TextStyle(color: Colors.white), required bool isFocused,
decoration: InputDecoration( bool isObscure = false,
prefixIcon: Icon(icon, color: Colors.grey), }) {
hintText: hint, return AnimatedContainer(
hintStyle: const TextStyle(color: Colors.grey), duration: const Duration(milliseconds: 200),
filled: true, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
fillColor: const Color(0xFF2C2C2E), decoration: BoxDecoration(
border: OutlineInputBorder( color: Colors.white,
borderRadius: BorderRadius.circular(12), border: Border.all(
borderSide: BorderSide.none, 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,
),
],
), ),
); );
} }

View File

@@ -7,6 +7,12 @@ import 'package:smarthelmet_app/rent_return_screen.dart';
import 'package:smarthelmet_app/settings_screen.dart'; import 'package:smarthelmet_app/settings_screen.dart';
import 'package:smarthelmet_app/login_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() { void main() {
runApp(const SmartHelmetApp()); runApp(const SmartHelmetApp());
} }
@@ -18,22 +24,22 @@ class SmartHelmetApp extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent, statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light, statusBarIconBrightness: Brightness.dark,
)); ));
return MaterialApp( return MaterialApp(
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
theme: ThemeData( theme: ThemeData(
brightness: Brightness.dark, brightness: Brightness.light,
scaffoldBackgroundColor: const Color(0xFF27292B), scaffoldBackgroundColor: _pageBackgroundColor,
primaryColor: const Color(0xFF30343B), primaryColor: _mainBlueColor,
fontFamily: 'Pretendard', fontFamily: 'Pretendard',
textTheme: const TextTheme( textTheme: TextTheme(
bodyLarge: TextStyle(color: Colors.white, fontWeight: FontWeight.w500), bodyLarge: TextStyle(color: _mainTextColor, fontWeight: FontWeight.w500),
bodyMedium: TextStyle(color: Colors.white70, fontWeight: FontWeight.w400), bodyMedium: TextStyle(color: _subTextColor, fontWeight: FontWeight.w400),
), ),
), ),
home: LoginScreen(), home: const HomeScreen(),
); );
} }
} }
@@ -81,19 +87,37 @@ class _HomeScreenState extends State<HomeScreen> {
}); });
}, },
type: BottomNavigationBarType.fixed, type: BottomNavigationBarType.fixed,
backgroundColor: const Color(0xFF1C1C1E), backgroundColor: _mainBlueColor,
elevation: 0, elevation: 0,
selectedItemColor: Colors.white, selectedItemColor: Colors.white,
unselectedItemColor: Colors.grey, unselectedItemColor: Colors.white70,
showUnselectedLabels: true, showUnselectedLabels: true,
selectedFontSize: 12, selectedFontSize: 12,
unselectedFontSize: 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 [ items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'HOME'), BottomNavigationBarItem(
BottomNavigationBarItem(icon: Icon(Icons.history), label: 'HISTORY'), icon: Padding(padding: EdgeInsets.only(bottom: 2.0), child: Icon(Icons.home)),
BottomNavigationBarItem(icon: Icon(Icons.settings_input_component), label: 'CONTROL'), label: '',
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.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: '설정',
),
], ],
), ),
), ),

View File

@@ -3,6 +3,21 @@ import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'rental_process_screen.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 { class StationInfo {
final String name; final String name;
final String address; final String address;
@@ -22,8 +37,7 @@ class RentReturnScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final LatLng centerLocation = const LatLng(35.1595, 126.8526); final LatLng centerLocation = LatLng(35.1595, 126.8526);
final List<StationInfo> stations = [ final List<StationInfo> stations = [
StationInfo( StationInfo(
name: 'STATION A - GU.UNIV', name: 'STATION A - GU.UNIV',
@@ -48,91 +62,114 @@ class RentReturnScreen extends StatelessWidget {
]; ];
return Scaffold( return Scaffold(
backgroundColor: const Color(0xFF27292B), backgroundColor: _pageBackgroundColor,
appBar: AppBar( appBar: PreferredSize(
title: const Text( preferredSize: const Size.fromHeight(kToolbarHeight),
'RENT/ RETURN', child: Container(
style: TextStyle( decoration: BoxDecoration(
fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white), color: _pageBackgroundColor,
), boxShadow: [
backgroundColor: const Color(0xFF2C2C2E), BoxShadow(
elevation: 0, color: Colors.black.withOpacity(0.05),
actions: [ blurRadius: 8,
IconButton( offset: const Offset(0, 4),
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,
), ),
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)),
],
),
],
),
), ),
child: AppBar(
Expanded( scrolledUnderElevation: 0,
flex: 45, title: Text(
child: Container( '대여/ 반납',
color: const Color(0xFF27292B), 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), padding: const EdgeInsets.fromLTRB(16, 20, 16, 0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'LIST VIEW', 'LIST VIEW',
style: TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
ListView.builder(
Expanded( shrinkWrap: true,
child: ListView.builder( physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.only(bottom: 20), padding: const EdgeInsets.only(bottom: 20),
itemCount: stations.length, itemCount: stations.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return _buildStationItem(context, stations[index]); return _buildStationItem(context, stations[index]);
}, },
),
), ),
], ],
), ),
), ),
), ],
], ),
), ),
); );
} }
Widget _buildStationItem(BuildContext context, StationInfo station) { Widget _buildStationItem(BuildContext context, StationInfo station) {
bool isOffline = station.status.startsWith('OFFLINE');
return Container( return Container(
margin: const EdgeInsets.only(bottom: 16), margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF30343B), color: _cardBackgroundColor,
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
boxShadow: [_cleanShadow],
), ),
child: Column( child: Column(
children: [ children: [
@@ -145,7 +182,7 @@ class RentReturnScreen extends StatelessWidget {
child: Container( child: Container(
padding: const EdgeInsets.all(6), padding: const EdgeInsets.all(6),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.3), color: _pageBackgroundColor,
borderRadius: BorderRadius.circular(5), borderRadius: BorderRadius.circular(5),
), ),
child: Stack( child: Stack(
@@ -153,8 +190,8 @@ class RentReturnScreen extends StatelessWidget {
children: [ children: [
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF2D2F33), color: _cardBackgroundColor,
borderRadius: BorderRadius.circular(5) borderRadius: BorderRadius.circular(5),
), ),
), ),
Padding( Padding(
@@ -163,7 +200,7 @@ class RentReturnScreen extends StatelessWidget {
'assets/images/storage.png', 'assets/images/storage.png',
fit: BoxFit.contain, fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) => 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), const SizedBox(width: 12),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
station.name, station.name,
style: const TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 13, fontSize: 13,
), ),
@@ -189,30 +225,29 @@ class RentReturnScreen extends StatelessWidget {
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
station.address, station.address,
style: const TextStyle( style: TextStyle(
color: Colors.grey, fontSize: 11, height: 1.2), color: _subTextColor, fontSize: 11, height: 1.2),
maxLines: 2, maxLines: 2,
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
Text( Text(
'STATUS: ${station.status}', 'STATUS: ${station.status}',
style: TextStyle( style: TextStyle(
color: station.status.startsWith('OFFLINE') color: isOffline ? Colors.redAccent : _mainBlueColor,
? Colors.redAccent
: Colors.grey,
fontSize: 11, fontSize: 11,
), ),
), ),
Text( Text(
station.distance, station.distance,
style: const TextStyle(color: Colors.grey, fontSize: 11), style: TextStyle(color: _subTextColor, fontSize: 11),
), ),
], ],
), ),
), ),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: isOffline
? null
: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
@@ -223,29 +258,31 @@ class RentReturnScreen extends StatelessWidget {
); );
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.white, backgroundColor:
foregroundColor: Colors.black, isOffline ? Colors.grey.shade400 : _mainBlueColor,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5), 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), minimumSize: const Size(80, 40),
elevation: 0,
), ),
child: const Text( child: const Text('SELECT',
'SELECT', style: TextStyle(
style: TextStyle(fontWeight: FontWeight.w500, letterSpacing: 0.8, fontSize: 14) fontWeight: FontWeight.w500,
), letterSpacing: 0.8,
fontSize: 14)),
), ),
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Container( Container(
width: double.infinity, width: double.infinity,
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF1E1E1E), color: _accentContainerColor,
borderRadius: BorderRadius.circular(5), borderRadius: BorderRadius.circular(5),
), ),
child: Column( child: Column(
@@ -254,10 +291,10 @@ class RentReturnScreen extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Text( Text(
'LOGS', '기록',
style: TextStyle( style: TextStyle(
color: Colors.white, color: _mainTextColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 12, fontSize: 12,
), ),
@@ -267,7 +304,7 @@ class RentReturnScreen extends StatelessWidget {
child: Text( child: Text(
'VIEW MORE >', 'VIEW MORE >',
style: TextStyle( style: TextStyle(
color: Colors.grey[400], color: _subTextColor,
fontSize: 10, fontSize: 10,
), ),
), ),
@@ -275,13 +312,13 @@ class RentReturnScreen extends StatelessWidget {
], ],
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
const Text( Text(
'Available: Door Fully Closed', '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)', '(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( return Marker(
point: point, point: point,
width: 40, width: 40,
height: 40, height: 40,
child: const Icon( child: Icon(
Icons.location_on, Icons.location_on,
color: Colors.black, color: _mainBlueColor,
size: 40, size: 40,
), ),
); );
@@ -312,31 +349,44 @@ class RentReturnScreen extends StatelessWidget {
builder: (context) { builder: (context) {
return Container( return Container(
height: MediaQuery.of(context).size.height * 0.6, height: MediaQuery.of(context).size.height * 0.6,
decoration: const BoxDecoration( decoration: BoxDecoration(
color: Color(0xFF1E1E1E), color: _cardBackgroundColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(20)), borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
border: Border.all(color: _borderColor, width: 1.5),
), ),
child: Column( child: Column(
children: [ children: [
const SizedBox(height: 12), const SizedBox(height: 12),
Container( Container(
width: 40, height: 4, width: 40,
decoration: BoxDecoration(color: Colors.grey[600], borderRadius: BorderRadius.circular(2)), height: 4,
decoration: BoxDecoration(
color: _subTextColor,
borderRadius: BorderRadius.circular(2)),
), ),
Padding( Padding(
padding: const EdgeInsets.all(20.0), 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( Expanded(
child: ListView( child: ListView(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
children: [ children: [
_buildLogItem("08:58:33", "Door Fully Closed", Icons.door_front_door, Colors.green), _buildLogItem("08:58:33", "Door Fully Closed",
_buildLogItem("08:58:30", "Helmet Returned", Icons.check_circle_outline, Colors.blue), Icons.door_front_door, _mainBlueColor),
_buildLogItem("08:55:12", "User Unlocked Door", Icons.lock_open, Colors.white), _buildLogItem("08:58:30", "Helmet Returned",
_buildLogItem("08:30:00", "UV Sanitization Complete", Icons.cleaning_services, Colors.purpleAccent), Icons.check_circle_outline, _mainBlueColor),
_buildLogItem("08:00:00", "System Boot Up", Icons.power_settings_new, Colors.grey), _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( return Padding(
padding: const EdgeInsets.only(bottom: 20.0), padding: const EdgeInsets.only(bottom: 20.0),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(time, style: const TextStyle(color: Colors.grey, fontSize: 12)), Text(time, style: TextStyle(color: _subTextColor, fontSize: 12)),
const SizedBox(width: 16), const SizedBox(width: 16),
Column( Column(
children: [ children: [
Icon(icon, color: color, size: 20), 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), const SizedBox(width: 16),
Expanded( Expanded(
child: Text(message, style: const TextStyle(color: Colors.white70, fontSize: 14)), child: Text(message,
style: TextStyle(color: _mainTextColor, fontSize: 14)),
), ),
], ],
), ),

View File

@@ -1,4 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
class RentalProcessScreen extends StatelessWidget { class RentalProcessScreen extends StatelessWidget {
final String stationName; final String stationName;
@@ -8,25 +10,34 @@ class RentalProcessScreen extends StatelessWidget {
required this.stationName, required this.stationName,
}); });
static const Color kBackgroundColor = Color(0xFF1E1E1E); static const Color _mainBlueColor = Color(0xFF002FA7);
static const Color kCardColor = Color(0xFF2C2C2E); static const Color _mainTextColor = Color(0xFF1C1C1E);
static const Color kPrimaryGreen = Color(0xFF4CAF50); static const Color _subTextColor = Color(0xFF6A717B);
static const Color kPrimaryOrange = Color(0xFFFF3D00); 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: kBackgroundColor, backgroundColor: _pageBackgroundColor,
appBar: AppBar( appBar: AppBar(
scrolledUnderElevation: 0,
title: Text( title: Text(
stationName, 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, elevation: 0,
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Icons.more_vert, color: Colors.white), icon: const Icon(Icons.more_vert, color: _mainTextColor),
onPressed: () {}, onPressed: () {},
), ),
], ],
@@ -39,52 +50,51 @@ class RentalProcessScreen extends StatelessWidget {
children: [ children: [
_buildStatusCard(context), _buildStatusCard(context),
const SizedBox(height: 24), const SizedBox(height: 24),
_buildProcessSectionTitle('헬멧 대여 순서'),
_buildProcessSectionTitle('HELMET RENTAL PROCESS'),
const SizedBox(height: 12), const SizedBox(height: 12),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: kCardColor, color: _cardBackgroundColor,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Column( child: Column(
children: [ children: [
_buildStepRow(1, 'UNLOCK & OPEN', Icons.lock_open, isCompleted: true), _buildStepRow(1, '잠금 해제 & 문 열림', Icons.lock_open,
isCompleted: true),
_buildDivider(), _buildDivider(),
_buildStepRow(2, 'DOOR STATUS', Icons.sensor_door_outlined), _buildStepRow(2, '문 상태 확인', Icons.sensor_door_outlined),
_buildDivider(), _buildDivider(),
_buildStepRow(3, 'TAKE HELMET', Icons.outbox), _buildStepRow(3, '헬멧 꺼내기', Icons.outbox),
_buildDivider(), _buildDivider(),
_buildStepRow(4, 'ENJOY RIDING!', Icons.sentiment_satisfied_alt, showDivider: false), _buildStepRow(4, '주행 시작 !', Icons.sentiment_satisfied_alt,
showDivider: false),
], ],
), ),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
_buildProcessSectionTitle('헬멧 반납 과정'),
_buildProcessSectionTitle('HELMET RETURN PROCESS'),
const SizedBox(height: 12), const SizedBox(height: 12),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: kCardColor, color: _cardBackgroundColor,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Column( child: Column(
children: [ children: [
_buildStepRow(1, 'UNLOCK & OPEN', Icons.lock_open), _buildStepRow(1, '잠금해제 & 문열림', Icons.lock_open),
_buildDivider(), _buildDivider(),
_buildStepRow(2, 'INSERT HELMET', Icons.move_to_inbox), _buildStepRow(2, '헬멧 넣기', Icons.move_to_inbox),
_buildDivider(), _buildDivider(),
_buildStepRow(3, 'SENSOR SCANNING\n& RETURN COMPLETE', Icons.sync), _buildStepRow(3, '센서 스캔 & 반납 완료', Icons.sync),
_buildDivider(), _buildDivider(),
_buildStepRow(4, 'SANITIZING', Icons.shield_outlined, showDivider: false), _buildStepRow(4, '살균 시작', Icons.shield_outlined,
showDivider: false),
], ],
), ),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
_buildErrorAlertBox(), _buildErrorAlertBox(),
const SizedBox(height: 24), const SizedBox(height: 24),
_buildLogsCard(context), _buildLogsCard(context),
const SizedBox(height: 20), const SizedBox(height: 20),
], ],
@@ -98,16 +108,18 @@ class RentalProcessScreen extends StatelessWidget {
return Container( return Container(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: kCardColor, color: _cardBackgroundColor,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Column( child: Column(
children: [ children: [
Row( Row(
children: const [ children: const [
Icon(Icons.circle, color: kPrimaryOrange, size: 16), Icon(Icons.circle, color: _primaryOrange, size: 16),
SizedBox(width: 6), 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), const SizedBox(height: 20),
@@ -119,13 +131,16 @@ class RentalProcessScreen extends StatelessWidget {
height: 150, height: 150,
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: kBackgroundColor, color: _accentContainerColor,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Image.asset( child: Image.asset(
'assets/images/open.png', 'assets/images/open.png',
fit: BoxFit.contain, 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), const SizedBox(width: 20),
@@ -133,7 +148,8 @@ class RentalProcessScreen extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('NOW', style: TextStyle(color: Colors.grey, fontSize: 12)), const Text('NOW',
style: TextStyle(color: _subTextColor, fontSize: 12)),
const SizedBox(height: 8), const SizedBox(height: 8),
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
@@ -143,17 +159,20 @@ class RentalProcessScreen extends StatelessWidget {
const SnackBar( const SnackBar(
content: Text('문이 열렸습니다! (OPEN 버튼 눌림)'), content: Text('문이 열렸습니다! (OPEN 버튼 눌림)'),
duration: Duration(seconds: 1), duration: Duration(seconds: 1),
backgroundColor: Colors.blue, backgroundColor: _mainBlueColor,
), ),
); );
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.white, backgroundColor: _mainBlueColor,
foregroundColor: Colors.black, foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12), 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), const SizedBox(height: 12),
@@ -161,13 +180,16 @@ class RentalProcessScreen extends StatelessWidget {
width: double.infinity, width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 12), padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: kBackgroundColor, color: _accentContainerColor,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: const Center( child: const Center(
child: Text( child: Text(
'SENSOR ERROR:\nLOCK FAIL', 'ERROR:\nLOCK FAIL',
style: TextStyle(color: kPrimaryOrange, fontWeight: FontWeight.bold, height: 1.2), style: TextStyle(
color: _primaryOrange,
fontWeight: FontWeight.bold,
height: 1.2),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
@@ -185,11 +207,13 @@ class RentalProcessScreen extends StatelessWidget {
Widget _buildProcessSectionTitle(String title) { Widget _buildProcessSectionTitle(String title) {
return Text( return Text(
title, 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( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row( child: Row(
@@ -199,45 +223,64 @@ class RentalProcessScreen extends StatelessWidget {
children: [ children: [
Row( Row(
children: [ 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) ...[ if (isCompleted) ...[
const SizedBox(width: 8), 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), 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(), const Spacer(),
Icon(icon, color: isCompleted ? Colors.white : Colors.grey, size: 48), Icon(icon,
color: isCompleted ? _mainBlueColor : _subTextColor, size: 48),
], ],
), ),
); );
} }
Widget _buildDivider() { 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() { Widget _buildErrorAlertBox() {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: kCardColor.withOpacity(0.5), color: _accentContainerColor,
border: Border.all(color: kPrimaryOrange, width: 2), border: Border.all(color: _primaryOrange, width: 2),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Row( child: Row(
children: const [ children: const [
Icon(Icons.warning_amber_rounded, color: kPrimaryOrange, size: 40), Icon(Icons.warning_amber_rounded, color: _primaryOrange, size: 40),
SizedBox(width: 16), SizedBox(width: 16),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('SENSOR ERROR:', style: TextStyle(color: kPrimaryOrange, fontWeight: FontWeight.bold, fontSize: 16)), Text('SENSOR ERROR:',
Text('LOCK FAILURE', style: TextStyle(color: kPrimaryOrange, fontWeight: FontWeight.bold, fontSize: 16)), 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( return Container(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: kCardColor, color: _cardBackgroundColor,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Column( child: Column(
@@ -257,24 +300,29 @@ class RentalProcessScreen extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Text('LOGS', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), const Text('LOGS',
style: TextStyle(
color: _mainTextColor, fontWeight: FontWeight.bold)),
InkWell( InkWell(
onTap: () => _showLogHistory(context), 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), const SizedBox(height: 12),
Row( Row(
children: const [ children: [
Icon(Icons.check_circle, color: kPrimaryGreen, size: 16), const Icon(Icons.check_circle, color: _mainBlueColor, size: 16),
SizedBox(width: 8), const SizedBox(width: 8),
Text('Available: Door Fully Closed', style: TextStyle(color: Colors.white70)), const Text('Available: Door Fully Closed',
style: TextStyle(color: _mainTextColor)),
], ],
), ),
Padding( Padding(
padding: const EdgeInsets.only(left: 24.0, top: 4), 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) { builder: (context) {
return Container( return Container(
height: MediaQuery.of(context).size.height * 0.6, height: MediaQuery.of(context).size.height * 0.6,
decoration: const BoxDecoration( decoration: BoxDecoration(
color: Color(0xFF1E1E1E), color: _cardBackgroundColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(20)), borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
), ),
child: Column( child: Column(
children: [ children: [
const SizedBox(height: 12), const SizedBox(height: 12),
Container( Container(
width: 40, height: 4, width: 40,
decoration: BoxDecoration(color: Colors.grey[600], borderRadius: BorderRadius.circular(2)), height: 4,
decoration: BoxDecoration(
color: _subTextColor,
borderRadius: BorderRadius.circular(2)),
), ),
const Padding( const Padding(
padding: EdgeInsets.all(20.0), 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( Expanded(
child: ListView( child: ListView(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
children: [ children: [
_buildLogItem("08:58:33", "Door Fully Closed", Icons.door_front_door, Colors.green), _buildLogItem("08:58:33", "Door Fully Closed",
_buildLogItem("08:58:30", "Helmet Returned (Sensor A)", Icons.check_circle_outline, Colors.blue), Icons.door_front_door, _mainBlueColor),
_buildLogItem("08:55:12", "User Unlocked Door", Icons.lock_open, Colors.white), _buildLogItem("08:58:30", "Helmet Returned (Sensor A)",
_buildLogItem("08:30:00", "UV Sanitization Complete", Icons.cleaning_services, Colors.purpleAccent), Icons.check_circle_outline, _mainBlueColor),
_buildLogItem("08:00:00", "System Boot Up", Icons.power_settings_new, Colors.grey), _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( return Padding(
padding: const EdgeInsets.only(bottom: 20.0), padding: const EdgeInsets.only(bottom: 20.0),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(time, style: const TextStyle(color: Colors.grey, fontSize: 12)), Text(time,
style: const TextStyle(color: _subTextColor, fontSize: 12)),
const SizedBox(width: 16), const SizedBox(width: 16),
Column( Column(
children: [ children: [
Icon(icon, color: color, size: 20), 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), const SizedBox(width: 16),
Expanded( Expanded(
child: Text(message, style: const TextStyle(color: Colors.white70, fontSize: 14)), child: Text(message,
style: const TextStyle(color: _mainTextColor, fontSize: 14)),
), ),
], ],
), ),

View File

@@ -1,14 +1,268 @@
import 'package:flutter/material.dart'; 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}); const SettingsScreen({super.key});
@override
State<SettingsScreen> createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> {
bool _isPushEnabled = true;
bool _isRentalAlertEnabled = true;
bool _isStorageStatusAlert = true;
bool _isAutoSyncEnabled = false;
final String _currentAppVersion = "v1.0.0";
@override @override
Widget build(BuildContext context) { 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( child: Text(
'5. 설정 (Settings) 페이지', title,
style: TextStyle(color: Colors.white70, fontSize: 20), 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<bool> 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),
),
), ),
); );
} }

View File

@@ -1,5 +1,10 @@
import 'package:flutter/material.dart'; 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 { class CustomHeader extends StatelessWidget {
const CustomHeader({super.key}); const CustomHeader({super.key});
@@ -8,24 +13,42 @@ class CustomHeader extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
height: 60.0, height: 50.0,
color: Theme.of(context).scaffoldBackgroundColor, decoration: BoxDecoration(
padding: const EdgeInsets.symmetric(horizontal: _uniformGap), color: Colors.white,
child: Row( boxShadow: const [
mainAxisAlignment: MainAxisAlignment.spaceBetween, BoxShadow(
crossAxisAlignment: CrossAxisAlignment.center, color: Color.fromRGBO(0, 0, 0, 0.07),
children: [ offset: Offset(0, 4),
Row(children: [ blurRadius: 6,
Icon(Icons.settings_input_component, color: Colors.grey[400]), spreadRadius: 0,
const SizedBox(width: 12), ),
const Text('SMART HELMET SYSTEMS', ],
style: TextStyle( ),
fontSize: 15, padding: const EdgeInsets.symmetric(horizontal: _uniformGap),
fontWeight: FontWeight.bold, child: Row(
color: Colors.white)) mainAxisAlignment: MainAxisAlignment.spaceBetween,
]), crossAxisAlignment: CrossAxisAlignment.center,
Text('2025/09/26 - 10:44 AM', children: [
style: TextStyle(color: Colors.grey[400], fontSize: 11)) 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),
),
],
),
);
} }
} }