1. 이미지 및 UI 개선
2. UI 텍스트 한국어 번역 3. 환경설정 화면 상세 구현 (SettingScreen)
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 675 KiB |
BIN
assets/images/top.png
Normal file
BIN
assets/images/top.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 330 KiB |
@@ -44,12 +44,6 @@ class _ControlScreenState extends State<ControlScreen> {
|
|||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.more_vert, color: _mainTextColor),
|
|
||||||
onPressed: () {},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
|
||||||
@@ -108,7 +102,7 @@ class _ControlScreenState extends State<ControlScreen> {
|
|||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
'assets/images/storage.png',
|
'assets/images/top.png',
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -136,7 +130,7 @@ class _ControlScreenState extends State<ControlScreen> {
|
|||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildStatusButton(
|
child: _buildStatusButton(
|
||||||
text: 'ERROR: 문 열림',
|
text: '센서 경고 : 문 열림',
|
||||||
textColor: _warningColor,
|
textColor: _warningColor,
|
||||||
bgColor: _accentContainerColor,
|
bgColor: _accentContainerColor,
|
||||||
isError: true,
|
isError: true,
|
||||||
@@ -191,7 +185,7 @@ class _ControlScreenState extends State<ControlScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'문 잠금 제어',
|
'보관함 문 제어',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _mainTextColor,
|
color: _mainTextColor,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
@@ -256,7 +250,7 @@ class _ControlScreenState extends State<ControlScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'조작 기록 및 경고',
|
'실시간 상태',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _mainTextColor,
|
color: _mainTextColor,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
@@ -283,7 +277,7 @@ class _ControlScreenState extends State<ControlScreen> {
|
|||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: '센서 경고: ',
|
text: '센서 경고 : ',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _warningColor,
|
color: _warningColor,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -291,9 +285,9 @@ class _ControlScreenState extends State<ControlScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: '문 닫힘 불완전',
|
text: '문 열림',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _mainTextColor,
|
color: _warningColor,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
@@ -334,7 +328,7 @@ class _ControlScreenState extends State<ControlScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'조작 기록 및 경고',
|
'시스템 기록',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _mainTextColor,
|
color: _mainTextColor,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
@@ -357,7 +351,7 @@ class _ControlScreenState extends State<ControlScreen> {
|
|||||||
color: _warningColor, size: 14),
|
color: _warningColor, size: 14),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
'센서 경고: 문 열림',
|
'1:50 PM - 센서 경고 : 문 열림',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _mainTextColor,
|
color: _mainTextColor,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
|||||||
@@ -32,16 +32,14 @@ class HistoryScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _HistoryScreenState extends State<HistoryScreen> {
|
class _HistoryScreenState extends State<HistoryScreen> {
|
||||||
// 🟦 최종 확정 테마 컬러 정의 (대비 구조 교체)
|
final Color _mainBlueColor = const Color(0xFF002FA7);
|
||||||
final Color _mainBlueColor = const Color(0xFF002FA7); // 진한 블루 (0xFF002FA7 -> 0xFF0A68FF로 통일)
|
final Color _mainTextColor = const Color(0xFF1C1C1E);
|
||||||
final Color _mainTextColor = const Color(0xFF1C1C1E); // 어두운 텍스트
|
final Color _subTextColor = const Color(0xFF6A717B);
|
||||||
final Color _subTextColor = const Color(0xFF6A717B); // 보조 텍스트
|
final Color _pageBackgroundColor = const Color(0xFFF5F7F9);
|
||||||
final Color _pageBackgroundColor = const Color(0xFFF5F7F9); // 🚩 Scaffold 배경 (연한 그레이)
|
final Color _cardBackgroundColor = Colors.white;
|
||||||
final Color _cardBackgroundColor = Colors.white; // 🚩 카드 배경 (순수 화이트)
|
final Color _accentContainerColor = const Color(0xFFF0F2F5);
|
||||||
final Color _accentContainerColor = const Color(0xFFF0F2F5); // 🚩 내부 상세 박스 배경 (중간 그레이)
|
|
||||||
final Color _borderColor = const Color(0xFF0A68FF).withOpacity(0.8);
|
final Color _borderColor = const Color(0xFF0A68FF).withOpacity(0.8);
|
||||||
|
|
||||||
// 🚩 그림자 스타일 정의
|
|
||||||
static const BoxShadow _cleanShadow = BoxShadow(
|
static const BoxShadow _cleanShadow = BoxShadow(
|
||||||
color: Color.fromRGBO(0, 0, 0, 0.07),
|
color: Color.fromRGBO(0, 0, 0, 0.07),
|
||||||
blurRadius: 8,
|
blurRadius: 8,
|
||||||
@@ -53,45 +51,45 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
|
|
||||||
final List<UsageHistory> _allHistoryList = [
|
final List<UsageHistory> _allHistoryList = [
|
||||||
UsageHistory(
|
UsageHistory(
|
||||||
date: '2025.11.25 (화)', // 🇰🇷 요일 변경
|
date: '2025.11.25 (화)',
|
||||||
startTime: '18:30',
|
startTime: '18:30',
|
||||||
endTime: '19:15',
|
endTime: '19:15',
|
||||||
duration: 45,
|
duration: 45,
|
||||||
startStation: 'STATION A - GU.UNIV',
|
startStation: '스테이션 A - 광주대',
|
||||||
endStation: 'STATION B - CITY HALL',
|
endStation: '스테이션 B - 시청',
|
||||||
status: 'RETURNED',
|
status: 'RETURNED',
|
||||||
isSanitized: true,
|
isSanitized: true,
|
||||||
isDried: true,
|
isDried: true,
|
||||||
),
|
),
|
||||||
UsageHistory(
|
UsageHistory(
|
||||||
date: '2025.11.23 (일)', // 🇰🇷 요일 변경
|
date: '2025.11.23 (일)',
|
||||||
startTime: '14:00',
|
startTime: '14:00',
|
||||||
endTime: '15:30',
|
endTime: '15:30',
|
||||||
duration: 90,
|
duration: 90,
|
||||||
startStation: 'STATION B - CITY HALL',
|
startStation: '스테이션 B - 시청',
|
||||||
endStation: 'STATION B - CITY HALL',
|
endStation: '스테이션 B - 시청',
|
||||||
status: 'RETURNED',
|
status: 'RETURNED',
|
||||||
isSanitized: true,
|
isSanitized: true,
|
||||||
isDried: false,
|
isDried: false,
|
||||||
),
|
),
|
||||||
UsageHistory(
|
UsageHistory(
|
||||||
date: '2025.11.06 (목)', // 🇰🇷 요일 변경
|
date: '2025.11.06 (목)',
|
||||||
startTime: '09:00',
|
startTime: '09:00',
|
||||||
endTime: '09:30',
|
endTime: '09:30',
|
||||||
duration: 30,
|
duration: 30,
|
||||||
startStation: 'STATION A - GU.UNIV',
|
startStation: '스테이션 A - 광주대',
|
||||||
endStation: 'STATION C - PARK',
|
endStation: '스테이션 C - 시민공원',
|
||||||
status: 'RETURNED',
|
status: 'RETURNED',
|
||||||
isSanitized: true,
|
isSanitized: true,
|
||||||
isDried: true,
|
isDried: true,
|
||||||
),
|
),
|
||||||
UsageHistory(
|
UsageHistory(
|
||||||
date: '2025.09.20 (토)', // 🇰🇷 요일 변경
|
date: '2025.09.20 (토)',
|
||||||
startTime: '20:00',
|
startTime: '20:00',
|
||||||
endTime: '21:00',
|
endTime: '21:00',
|
||||||
duration: 60,
|
duration: 60,
|
||||||
startStation: 'STATION C - PARK',
|
startStation: '스테이션 C - 시민공원',
|
||||||
endStation: 'STATION B - CITY HALL',
|
endStation: '스테이션 B - 시청',
|
||||||
status: 'RETURNED',
|
status: 'RETURNED',
|
||||||
isSanitized: true,
|
isSanitized: true,
|
||||||
isDried: true,
|
isDried: true,
|
||||||
@@ -109,14 +107,9 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
String dateStr = history.date.split(' ')[0];
|
String dateStr = history.date.split(' ')[0];
|
||||||
List<String> parts = dateStr.split('.');
|
List<String> parts = dateStr.split('.');
|
||||||
DateTime historyDate = DateTime(
|
DateTime historyDate = DateTime(
|
||||||
int.parse(parts[0]),
|
int.parse(parts[0]), int.parse(parts[1]), int.parse(parts[2]));
|
||||||
int.parse(parts[1]),
|
|
||||||
int.parse(parts[2])
|
|
||||||
);
|
|
||||||
|
|
||||||
int difference = now
|
int difference = now.difference(historyDate).inDays;
|
||||||
.difference(historyDate)
|
|
||||||
.inDays;
|
|
||||||
|
|
||||||
if (_selectedFilterIndex == 1) {
|
if (_selectedFilterIndex == 1) {
|
||||||
return difference <= 7;
|
return difference <= 7;
|
||||||
@@ -134,16 +127,16 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: _pageBackgroundColor,
|
backgroundColor: _pageBackgroundColor,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('나의 기록', style: TextStyle(fontWeight: FontWeight
|
title: Text('나의 기록',
|
||||||
.bold, fontSize: 18, letterSpacing: 0.5, color: _mainTextColor)),
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 18,
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
color: _mainTextColor)),
|
||||||
backgroundColor: _pageBackgroundColor,
|
backgroundColor: _pageBackgroundColor,
|
||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
actions: [
|
|
||||||
IconButton(icon: Icon(Icons.filter_list, color: _mainTextColor),
|
|
||||||
onPressed: () {}),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -151,7 +144,8 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: currentList.isEmpty
|
child: currentList.isEmpty
|
||||||
? Center(
|
? Center(
|
||||||
child: Text("기록이 없습니다.", style: TextStyle(color: _subTextColor)))
|
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,
|
||||||
@@ -159,7 +153,8 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
final history = currentList[index];
|
final history = currentList[index];
|
||||||
|
|
||||||
bool showHeader = true;
|
bool showHeader = true;
|
||||||
if (index > 0 && currentList[index - 1].date == history.date) {
|
if (index > 0 &&
|
||||||
|
currentList[index - 1].date == history.date) {
|
||||||
showHeader = false;
|
showHeader = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,10 +211,7 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
child: Text(
|
child: Text(
|
||||||
date,
|
date,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _mainTextColor,
|
color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -230,8 +222,7 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: _cardBackgroundColor,
|
color: _cardBackgroundColor,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
boxShadow: [_cleanShadow]
|
boxShadow: [_cleanShadow]),
|
||||||
),
|
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () => _showHistoryDetail(context, history),
|
onTap: () => _showHistoryDetail(context, history),
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
@@ -244,20 +235,22 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'${history.startTime} - ${history.endTime}',
|
'${history.startTime} - ${history.endTime}',
|
||||||
style: TextStyle(color: _mainTextColor,
|
style: TextStyle(
|
||||||
|
color: _mainTextColor,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold),
|
fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding:
|
||||||
horizontal: 8, vertical: 4),
|
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: _mainBlueColor,
|
color: _mainBlueColor,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'${history.duration}분', // 🇰🇷 min -> 분
|
'${history.duration}분',
|
||||||
style: const TextStyle(color: Colors.white,
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w600),
|
fontWeight: FontWeight.w600),
|
||||||
),
|
),
|
||||||
@@ -269,9 +262,10 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
children: [
|
children: [
|
||||||
_buildDot(_subTextColor),
|
_buildDot(_subTextColor),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(child: Text(history.startStation,
|
Expanded(
|
||||||
style: TextStyle(color: _mainTextColor, fontSize: 13),
|
child: Text(history.startStation,
|
||||||
overflow: TextOverflow.ellipsis)),
|
style: TextStyle(color: _mainTextColor, fontSize: 13),
|
||||||
|
overflow: TextOverflow.ellipsis)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
@@ -279,40 +273,45 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
height: 10,
|
height: 10,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
left: BorderSide(color: _subTextColor.withOpacity(0.5), width: 1)),
|
left: BorderSide(
|
||||||
|
color: _subTextColor.withOpacity(0.5), width: 1)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
_buildDot(_mainBlueColor),
|
_buildDot(_mainBlueColor),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(child: Text(history.endStation,
|
Expanded(
|
||||||
style: TextStyle(color: _mainTextColor, fontSize: 13),
|
child: Text(history.endStation,
|
||||||
overflow: TextOverflow.ellipsis)),
|
style: TextStyle(color: _mainTextColor, fontSize: 13),
|
||||||
|
overflow: TextOverflow.ellipsis)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Divider(color: _subTextColor.withOpacity(0.5), 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(_mainBlueColor),
|
||||||
_buildDot(_mainBlueColor),
|
if (history.isSanitized) const SizedBox(width: 8),
|
||||||
if (history.isSanitized)
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
Text(
|
||||||
history.isSanitized ? "살균 완료" : "진행 중", // 🇰🇷 Pending -> 진행 중
|
history.isSanitized ? "살균 완료" : "진행 중",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: history.isSanitized ? _mainTextColor : _subTextColor,
|
color: history.isSanitized
|
||||||
|
? _mainTextColor
|
||||||
|
: _subTextColor,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold),
|
fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"상세 보기 >", // 🇰🇷 VIEW DETAILS > -> 상세 보기 >
|
"더보기 >",
|
||||||
style: TextStyle(color: _subTextColor, fontSize: 11),
|
style: TextStyle(color: _subTextColor, fontSize: 11),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -325,7 +324,8 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDot(Color color) {
|
Widget _buildDot(Color color) {
|
||||||
return Container(width: 8,
|
return Container(
|
||||||
|
width: 8,
|
||||||
height: 8,
|
height: 8,
|
||||||
decoration: BoxDecoration(color: color, shape: BoxShape.circle));
|
decoration: BoxDecoration(color: color, shape: BoxShape.circle));
|
||||||
}
|
}
|
||||||
@@ -338,21 +338,20 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return Container(
|
return Container(
|
||||||
height: MediaQuery
|
height: MediaQuery.of(context).size.height * 0.75,
|
||||||
.of(context)
|
|
||||||
.size
|
|
||||||
.height * 0.75,
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: _cardBackgroundColor,
|
color: _cardBackgroundColor,
|
||||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
|
borderRadius:
|
||||||
boxShadow: [_cleanShadow]
|
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: _subTextColor,
|
decoration: BoxDecoration(
|
||||||
|
color: _subTextColor,
|
||||||
borderRadius: BorderRadius.circular(2))),
|
borderRadius: BorderRadius.circular(2))),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
@@ -360,10 +359,11 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("이용 상세 정보", style: TextStyle( // 🇰🇷 HISTORY DETAILS
|
Text("이용 상세 정보",
|
||||||
color: _mainTextColor,
|
style: TextStyle(
|
||||||
fontSize: 20,
|
color: _mainTextColor,
|
||||||
fontWeight: FontWeight.bold)),
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
history.date,
|
history.date,
|
||||||
@@ -372,7 +372,8 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
decoration: BoxDecoration(color: _accentContainerColor,
|
decoration: BoxDecoration(
|
||||||
|
color: _accentContainerColor,
|
||||||
borderRadius: BorderRadius.circular(8)),
|
borderRadius: BorderRadius.circular(8)),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
@@ -380,38 +381,45 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("이용 시간", style: TextStyle( // 🇰🇷 이용 시간
|
Text("이용 시간",
|
||||||
color: _subTextColor, fontSize: 12)),
|
style: TextStyle(
|
||||||
|
color: _subTextColor, fontSize: 12)),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text("${history.duration}분", // 🇰🇷 min -> 분
|
Text("${history.duration}분",
|
||||||
style: TextStyle(color: _mainTextColor,
|
style: TextStyle(
|
||||||
|
color: _mainTextColor,
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
fontWeight: FontWeight.bold)),
|
fontWeight: FontWeight.bold)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
width: 1, height: 40, color: _subTextColor.withOpacity(0.5)),
|
width: 1,
|
||||||
|
height: 40,
|
||||||
|
color: _subTextColor.withOpacity(0.5)),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("이용 금액", style: TextStyle( // 🇰🇷 이용 금액
|
Text("이용 금액",
|
||||||
color: _subTextColor, fontSize: 12)),
|
style: TextStyle(
|
||||||
|
color: _subTextColor, fontSize: 12)),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text("₩ $cost", style: TextStyle( // 🇰🇷 ₩ 위치 수정
|
Text("₩ $cost",
|
||||||
color: _mainTextColor,
|
style: TextStyle(
|
||||||
fontSize: 24,
|
color: _mainTextColor,
|
||||||
fontWeight: FontWeight.bold)),
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
Text("시간별 기록", style: TextStyle( // 🇰🇷 TIMELINE
|
Text("시간별 기록",
|
||||||
color: _mainTextColor,
|
style: TextStyle(
|
||||||
fontSize: 16,
|
color: _mainTextColor,
|
||||||
fontWeight: FontWeight.bold,
|
fontSize: 16,
|
||||||
letterSpacing: 1.0)),
|
fontWeight: FontWeight.bold,
|
||||||
|
letterSpacing: 1.0)),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
_buildTimelineItem(
|
_buildTimelineItem(
|
||||||
history.startTime, "대여 시작", history.startStation,
|
history.startTime, "대여 시작", history.startStation,
|
||||||
@@ -420,26 +428,27 @@ 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, "살균 시작", "자동 살균 시스템",
|
||||||
isCompleted: true),
|
isCompleted: true),
|
||||||
_buildTimelineItem(
|
_buildTimelineItem(
|
||||||
"15분 후", "살균 완료", "다음 사용자 준비 완료", // 🇰🇷 15 min later, Ready for next user
|
"15분 후", "살균 완료", "다음 사용자 준비 완료",
|
||||||
isLast: true, isCompleted: history.isSanitized),
|
isLast: true, isCompleted: history.isSanitized),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
Text("살균/건조 상태", style: TextStyle( // 🇰🇷 CONDITION
|
Text("살균/건조 상태",
|
||||||
color: _mainTextColor,
|
style: TextStyle(
|
||||||
fontSize: 16,
|
color: _mainTextColor,
|
||||||
fontWeight: FontWeight.bold)),
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: _buildConditionCard(
|
Expanded(
|
||||||
"살균", history.isSanitized,
|
child: _buildConditionCard("살균",
|
||||||
Icons.sentiment_satisfied_alt)),
|
history.isSanitized, Icons.sentiment_satisfied_alt)),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(child: _buildConditionCard(
|
Expanded(
|
||||||
"건조", history.isDried,
|
child: _buildConditionCard("건조", history.isDried,
|
||||||
Icons.sentiment_very_satisfied)),
|
Icons.sentiment_very_satisfied)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -455,7 +464,6 @@ 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;
|
String displayTime = time.contains("min later") ? "15분 후" : time;
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
@@ -463,16 +471,18 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 60,
|
width: 60,
|
||||||
child: Text(
|
child: Text(displayTime,
|
||||||
displayTime, style: TextStyle(color: _mainTextColor, fontSize: 12)),
|
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 ? _mainBlueColor : _subTextColor, 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
|
||||||
? _mainBlueColor.withOpacity(0.5)
|
? _mainBlueColor.withOpacity(0.5)
|
||||||
@@ -484,10 +494,11 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(title, style: TextStyle(
|
Text(title,
|
||||||
color: isCompleted ? _mainTextColor : _subTextColor,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
color: isCompleted ? _mainTextColor : _subTextColor,
|
||||||
fontSize: 14)),
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 14)),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(subtitle,
|
Text(subtitle,
|
||||||
style: TextStyle(color: _subTextColor, fontSize: 12)),
|
style: TextStyle(color: _subTextColor, fontSize: 12)),
|
||||||
@@ -505,10 +516,13 @@ 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: _accentContainerColor, // 내부 상세 박스 배경 (중간 그레이)
|
color: _accentContainerColor,
|
||||||
boxShadow: isDone ? [_cleanShadow] : null,
|
boxShadow: isDone ? [_cleanShadow] : null,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isDone ? _mainBlueColor.withOpacity(0.9) : _subTextColor.withOpacity(0.3), width: 1.0),
|
color: isDone
|
||||||
|
? _mainBlueColor.withOpacity(0.9)
|
||||||
|
: _subTextColor.withOpacity(0.3),
|
||||||
|
width: 1.0),
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -526,12 +540,11 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
isDone ? "완료됨" : "진행 중", // 🇰🇷 Completed/In Progress
|
isDone ? "완료됨" : "진행 중",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _subTextColor,
|
color: _subTextColor,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 14
|
fontSize: 14),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
|||||||
final Color _cardBackgroundColor = Colors.white;
|
final Color _cardBackgroundColor = Colors.white;
|
||||||
final Color _accentContainerColor = const Color(0xFFF0F2F5);
|
final Color _accentContainerColor = const Color(0xFFF0F2F5);
|
||||||
final Color _toggleOffTrackColor = const Color(0xFFE0E0E0);
|
final Color _toggleOffTrackColor = const Color(0xFFE0E0E0);
|
||||||
final Color _toggleOffKnobBorderColor = const Color(0xFFB0B0B0);
|
final Color _toggleOffKnobBorderColor = const Color(0xFFE0E0E0);
|
||||||
final Color _toggleOffTextColor = const Color(0xFF909090);
|
|
||||||
|
int _selectedImageIndex = 0;
|
||||||
|
|
||||||
static const BoxShadow _cleanShadow = BoxShadow(
|
static const BoxShadow _cleanShadow = BoxShadow(
|
||||||
color: Color.fromRGBO(0, 0, 0, 0.07),
|
color: Color.fromRGBO(0, 0, 0, 0.07),
|
||||||
@@ -103,11 +104,7 @@ 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: [
|
||||||
Text('장치 개요',
|
Text('장비 상태 / 사용자 정보', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: _mainTextColor)),
|
||||||
style: TextStyle(
|
|
||||||
color: _mainTextColor,
|
|
||||||
fontSize: 11,
|
|
||||||
fontWeight: FontWeight.bold)),
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Icon(Icons.search, color: _subTextColor, size: 20),
|
Icon(Icons.search, color: _subTextColor, size: 20),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
@@ -138,21 +135,19 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 20.0),
|
padding: const EdgeInsets.only(bottom: 20.0),
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
'assets/images/storage.png',
|
_selectedImageIndex == 0
|
||||||
width: 90,
|
? 'assets/images/storage.png'
|
||||||
|
: 'assets/images/top.png',
|
||||||
|
width: 100,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 12,
|
bottom: 12,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
_buildLedIndicator(Colors.grey.shade300),
|
_buildSelectableIndicator(0),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 8),
|
||||||
_buildLedIndicator(Colors.grey.shade300),
|
_buildSelectableIndicator(1),
|
||||||
const SizedBox(width: 4),
|
|
||||||
_buildLedIndicator(Colors.grey.shade300),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
_buildLedIndicator(_mainTextColor),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -162,13 +157,21 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLedIndicator(Color color) {
|
Widget _buildSelectableIndicator(int index) {
|
||||||
return Container(
|
final bool isSelected = _selectedImageIndex == index;
|
||||||
width: 18,
|
return GestureDetector(
|
||||||
height: 6,
|
onTap: () {
|
||||||
decoration: BoxDecoration(
|
setState(() {
|
||||||
color: color,
|
_selectedImageIndex = index;
|
||||||
borderRadius: BorderRadius.circular(3),
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: 30,
|
||||||
|
height: 6,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isSelected ? _mainTextColor : Colors.grey.shade300,
|
||||||
|
borderRadius: BorderRadius.circular(3),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -179,10 +182,10 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
_buildInfoRow('이름 / \n회원번호', Icon(Icons.person, color: _mainTextColor, size: 20), 'USER', '001',
|
_buildInfoRow('ID', Icon(Icons.person, color: _mainTextColor, size: 20), 'USER', '001',
|
||||||
value1Color: _mainTextColor, value2Color: _mainTextColor),
|
value1Color: _mainTextColor, value2Color: _mainTextColor),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
_buildInfoRow('상태', null, '잠금 해제됨', '● 활성',
|
_buildInfoRow('STATUS', null, '잠금 해제', '● 활성',
|
||||||
value1Color: _mainTextColor, value2Color: _mainTextColor),
|
value1Color: _mainTextColor, value2Color: _mainTextColor),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -234,13 +237,13 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('배터리 상태 (%)', style: TextStyle(color: _mainTextColor, fontSize: 12, fontWeight: FontWeight.bold)),
|
Text('배터리 상태 (%)', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: _mainTextColor)),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 20),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 80,
|
width: 70,
|
||||||
height: 80,
|
height: 70,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
@@ -315,7 +318,8 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('', style: TextStyle(color: _mainTextColor, fontSize: 11, fontWeight: FontWeight.bold)),
|
Text('보관함 원격 제어', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: _mainTextColor)),
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 70,
|
height: 70,
|
||||||
@@ -326,7 +330,7 @@ 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(color: _subTextColor.withOpacity(0.5), indent: 10, endIndent: 10),
|
VerticalDivider(color: _mainBlueColor.withOpacity(0.5), indent: 10, endIndent: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildStyledToggleSwitch(
|
child: _buildStyledToggleSwitch(
|
||||||
'CHARGING',
|
'CHARGING',
|
||||||
@@ -367,7 +371,7 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
|||||||
height: 30,
|
height: 30,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(15),
|
borderRadius: BorderRadius.circular(15),
|
||||||
color: value ? _mainTextColor.withOpacity(0.8) : _toggleOffTrackColor,
|
color: value ? _mainBlueColor : _toggleOffTrackColor,
|
||||||
),
|
),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
@@ -383,7 +387,9 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
border: Border.all(color: value ? _mainTextColor : _toggleOffKnobBorderColor, width: 1.5),
|
border: Border.all(
|
||||||
|
color: value ? _mainBlueColor : _toggleOffKnobBorderColor,
|
||||||
|
width: 1.5),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -425,13 +431,13 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
|||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text('환경 센서', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: _mainTextColor)),
|
Text('환경 모니터링', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: _mainTextColor)),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {},
|
onTap: () {},
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text('기록 보기', style: TextStyle(color: _mainTextColor, fontSize: 10)),
|
Text('더보기', style: TextStyle(color: _mainTextColor, fontSize: 9)),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Icon(Icons.arrow_forward_ios, size: 10, color: _subTextColor),
|
Icon(Icons.arrow_forward_ios, size: 10, color: _subTextColor),
|
||||||
],
|
],
|
||||||
@@ -501,7 +507,7 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('현재 위치', style: TextStyle(fontSize: 13, color: _mainTextColor, fontWeight: FontWeight.bold)),
|
Text('현위치', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: _mainTextColor)),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text('주소: 남구 효덕로 277', style: TextStyle(fontSize: 11, color: _mainTextColor)),
|
Text('주소: 남구 효덕로 277', style: TextStyle(fontSize: 11, color: _mainTextColor)),
|
||||||
],
|
],
|
||||||
@@ -510,7 +516,7 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
|||||||
onTap: () {},
|
onTap: () {},
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text('더 보기', style: TextStyle(color: _mainTextColor, fontSize: 9)),
|
Text('더보기', style: TextStyle(color: _mainTextColor, fontSize: 9)),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Icon(Icons.arrow_forward_ios, size: 10, color: _subTextColor),
|
Icon(Icons.arrow_forward_ios, size: 10, color: _subTextColor),
|
||||||
],
|
],
|
||||||
@@ -561,11 +567,7 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('Activity',
|
Text('최근 활동', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: _mainTextColor)),
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
color: _mainTextColor,
|
|
||||||
fontWeight: FontWeight.bold)),
|
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -573,9 +575,9 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
_activityText('08:15 AM - Battery fully Charged'),
|
_activityText('08:15 AM - 배터리 충전 완료'),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
_activityText('9:30 AM - UV LED Actived'),
|
_activityText('9:30 AM - UV LED 활성화 됨'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -584,9 +586,9 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
_activityText('10:45 AM - Helmet Unlocked'),
|
_activityText('10:45 AM - 헬멧 잠금 해제'),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
_activityText('11:00 AM - Helmet Off'),
|
_activityText('11:00 AM - 헬멧 착용 해제'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -622,9 +624,9 @@ class _LineChartPlaceholder extends StatelessWidget {
|
|||||||
CustomPaint(painter: _LineChartPainter(color: lineColor), size: Size.infinite)),
|
CustomPaint(painter: _LineChartPainter(color: lineColor), size: Size.infinite)),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
||||||
Text('24H AGO', style: TextStyle(fontSize: 8, color: subLabelColor)),
|
Text('24 시간 전', style: TextStyle(fontSize: 8, color: subLabelColor)),
|
||||||
Text('12H AGO', style: TextStyle(fontSize: 8, color: subLabelColor)),
|
Text('12시간 전', style: TextStyle(fontSize: 8, color: subLabelColor)),
|
||||||
Text('NOW', style: TextStyle(fontSize: 8, color: subLabelColor))
|
Text('현재', style: TextStyle(fontSize: 8, color: subLabelColor))
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,25 +40,25 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
final LatLng centerLocation = 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: '스테이션 A - 광주대학교',
|
||||||
address: '277 Hyodeok-ro',
|
address: '효덕로 277',
|
||||||
status: 'ONLINE',
|
status: '온라인',
|
||||||
distance: '0.5 km away'),
|
distance: '0.5 km'),
|
||||||
StationInfo(
|
StationInfo(
|
||||||
name: 'STATION B - CITY HALL',
|
name: '스테이션 B - 시청',
|
||||||
address: '123 City Hall Ave',
|
address: '시청대로 123',
|
||||||
status: 'ONLINE',
|
status: '온라인',
|
||||||
distance: '1.2 km away'),
|
distance: '1.2 km'),
|
||||||
StationInfo(
|
StationInfo(
|
||||||
name: 'STATION C - PARK',
|
name: '스테이션 C - 공원',
|
||||||
address: '55 Park Lane',
|
address: '공원길 55',
|
||||||
status: 'OFFLINE',
|
status: '오프라인',
|
||||||
distance: '2.8 km away'),
|
distance: '2.8 km'),
|
||||||
StationInfo(
|
StationInfo(
|
||||||
name: 'STATION D - TECH HUB',
|
name: '스테이션 D - 테크 허브',
|
||||||
address: '88 Innovation Blvd',
|
address: '이노베이션대로 88',
|
||||||
status: 'ONLINE',
|
status: '온라인',
|
||||||
distance: '4.1 km away'),
|
distance: '4.1 km'),
|
||||||
];
|
];
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -88,18 +88,13 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.more_vert, color: _mainTextColor),
|
|
||||||
onPressed: () {},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 300.0,
|
height: 300.0,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
@@ -110,8 +105,7 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
TileLayer(
|
TileLayer(
|
||||||
urlTemplate:
|
urlTemplate: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',
|
||||||
'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',
|
|
||||||
userAgentPackageName: 'com.example.helmet_app',
|
userAgentPackageName: 'com.example.helmet_app',
|
||||||
subdomains: const ['a', 'b', 'c', 'd'],
|
subdomains: const ['a', 'b', 'c', 'd'],
|
||||||
retinaMode: true,
|
retinaMode: true,
|
||||||
@@ -127,6 +121,7 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
Container(
|
Container(
|
||||||
color: _pageBackgroundColor,
|
color: _pageBackgroundColor,
|
||||||
padding: const EdgeInsets.fromLTRB(16, 20, 16, 0),
|
padding: const EdgeInsets.fromLTRB(16, 20, 16, 0),
|
||||||
@@ -134,7 +129,7 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'LIST VIEW',
|
'목록 보기',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _mainTextColor,
|
color: _mainTextColor,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
@@ -142,6 +137,7 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
|
|
||||||
ListView.builder(
|
ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
@@ -208,6 +204,7 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -233,7 +230,9 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
'STATUS: ${station.status}',
|
'STATUS: ${station.status}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: isOffline ? Colors.redAccent : _mainBlueColor,
|
color: isOffline
|
||||||
|
? Colors.redAccent
|
||||||
|
: _mainBlueColor,
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -244,10 +243,9 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: isOffline
|
onPressed: isOffline ? null : () {
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
@@ -258,26 +256,25 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor:
|
backgroundColor: isOffline ? Colors.grey.shade400 : _mainBlueColor,
|
||||||
isOffline ? Colors.grey.shade400 : _mainBlueColor,
|
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(5),
|
borderRadius: BorderRadius.circular(5),
|
||||||
),
|
),
|
||||||
padding:
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
|
||||||
minimumSize: const Size(80, 40),
|
minimumSize: const Size(80, 40),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
child: const Text('SELECT',
|
child: const Text(
|
||||||
style: TextStyle(
|
'SELECT',
|
||||||
fontWeight: FontWeight.w500,
|
style: TextStyle(fontWeight: FontWeight.w500, letterSpacing: 0.8, fontSize: 14)
|
||||||
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),
|
||||||
@@ -292,7 +289,7 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'기록',
|
'이용 기록',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _mainTextColor,
|
color: _mainTextColor,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -302,7 +299,7 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
InkWell(
|
InkWell(
|
||||||
onTap: () => _showLogHistory(context, station.name),
|
onTap: () => _showLogHistory(context, station.name),
|
||||||
child: Text(
|
child: Text(
|
||||||
'VIEW MORE >',
|
'더보기 >',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _subTextColor,
|
color: _subTextColor,
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
@@ -313,7 +310,7 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Text(
|
Text(
|
||||||
'Available: Door Fully Closed',
|
'이용 가능: 문 닫힘',
|
||||||
style: TextStyle(color: _subTextColor, fontSize: 11),
|
style: TextStyle(color: _subTextColor, fontSize: 11),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
@@ -358,35 +355,23 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Container(
|
Container(
|
||||||
width: 40,
|
width: 40, height: 4,
|
||||||
height: 4,
|
decoration: BoxDecoration(color: _subTextColor, borderRadius: BorderRadius.circular(2)),
|
||||||
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",
|
child: Text("LOGS: $stationName", style: TextStyle(color: _mainTextColor, fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
style: TextStyle(
|
|
||||||
color: _mainTextColor,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold)),
|
|
||||||
),
|
),
|
||||||
Divider(color: _subTextColor.withOpacity(0.5), 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",
|
_buildLogItem("08:58:33", "문 닫힘", Icons.door_front_door, _mainBlueColor),
|
||||||
Icons.door_front_door, _mainBlueColor),
|
_buildLogItem("08:58:30", "헬멧 반납 확인", Icons.check_circle_outline, _mainBlueColor),
|
||||||
_buildLogItem("08:58:30", "Helmet Returned",
|
_buildLogItem("08:55:12", "사용자 문 잠금 해제", Icons.lock_open, _mainTextColor),
|
||||||
Icons.check_circle_outline, _mainBlueColor),
|
_buildLogItem("08:30:00", "UV 살균 완료", Icons.cleaning_services, _mainBlueColor),
|
||||||
_buildLogItem("08:55:12", "User Unlocked Door",
|
_buildLogItem("08:00:00", "시스템 가동 중", Icons.power_settings_new, _subTextColor),
|
||||||
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),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -397,8 +382,7 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLogItem(
|
Widget _buildLogItem(String time, String message, IconData icon, Color color) {
|
||||||
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(
|
||||||
@@ -409,14 +393,12 @@ class RentReturnScreen extends StatelessWidget {
|
|||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
Icon(icon, color: color, size: 20),
|
Icon(icon, color: color, size: 20),
|
||||||
Container(
|
Container(width: 2, height: 20, color: _subTextColor.withOpacity(0.5)),
|
||||||
width: 2, height: 20, color: _subTextColor.withOpacity(0.5)),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(message,
|
child: Text(message, style: TextStyle(color: _mainTextColor, fontSize: 14)),
|
||||||
style: TextStyle(color: _mainTextColor, fontSize: 14)),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -35,12 +35,6 @@ class RentalProcessScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
backgroundColor: _cardBackgroundColor,
|
backgroundColor: _cardBackgroundColor,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.more_vert, color: _mainTextColor),
|
|
||||||
onPressed: () {},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -50,7 +44,7 @@ class RentalProcessScreen extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
_buildStatusCard(context),
|
_buildStatusCard(context),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
_buildProcessSectionTitle('헬멧 대여 순서'),
|
_buildProcessSectionTitle('헬멧 대여'),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@@ -66,13 +60,13 @@ class RentalProcessScreen extends StatelessWidget {
|
|||||||
_buildDivider(),
|
_buildDivider(),
|
||||||
_buildStepRow(3, '헬멧 꺼내기', Icons.outbox),
|
_buildStepRow(3, '헬멧 꺼내기', Icons.outbox),
|
||||||
_buildDivider(),
|
_buildDivider(),
|
||||||
_buildStepRow(4, '주행 시작 !', Icons.sentiment_satisfied_alt,
|
_buildStepRow(4, '주행 시작', Icons.sentiment_satisfied_alt,
|
||||||
showDivider: false),
|
showDivider: false),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
_buildProcessSectionTitle('헬멧 반납 과정'),
|
_buildProcessSectionTitle('헬멧 반납'),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@@ -135,7 +129,7 @@ class RentalProcessScreen extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
'assets/images/open.png',
|
'assets/images/top.png',
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
errorBuilder: (context, error, stackTrace) => const Icon(
|
errorBuilder: (context, error, stackTrace) => const Icon(
|
||||||
Icons.inventory_2,
|
Icons.inventory_2,
|
||||||
@@ -149,10 +143,11 @@ class RentalProcessScreen extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text('NOW',
|
const Text('NOW',
|
||||||
style: TextStyle(color: _subTextColor, fontSize: 12)),
|
style: TextStyle(color: Colors.black, fontSize: 12)),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
|
height: 68,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
@@ -175,7 +170,7 @@ class RentalProcessScreen extends StatelessWidget {
|
|||||||
fontWeight: FontWeight.bold, fontSize: 16)),
|
fontWeight: FontWeight.bold, fontSize: 16)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 10),
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
@@ -185,7 +180,7 @@ class RentalProcessScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'ERROR:\nLOCK FAIL',
|
'센서 경고 : 잠금 실패',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _primaryOrange,
|
color: _primaryOrange,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -271,12 +266,7 @@ class RentalProcessScreen extends StatelessWidget {
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('SENSOR ERROR:',
|
Text('센서 오류 : 잠금 실패',
|
||||||
style: TextStyle(
|
|
||||||
color: _primaryOrange,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 16)),
|
|
||||||
Text('LOCK FAILURE',
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _primaryOrange,
|
color: _primaryOrange,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -300,12 +290,12 @@ class RentalProcessScreen extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
const Text('LOGS',
|
const Text('로그',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _mainTextColor, fontWeight: FontWeight.bold)),
|
color: _mainTextColor, fontWeight: FontWeight.bold)),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () => _showLogHistory(context),
|
onTap: () => _showLogHistory(context),
|
||||||
child: Text('VIEW MORE >',
|
child: Text('더보기 >',
|
||||||
style: TextStyle(color: _subTextColor, fontSize: 12)),
|
style: TextStyle(color: _subTextColor, fontSize: 12)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -315,12 +305,12 @@ class RentalProcessScreen extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Icons.check_circle, color: _mainBlueColor, size: 16),
|
const Icon(Icons.check_circle, color: _mainBlueColor, size: 16),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
const Text('Available: Door Fully Closed',
|
const Text('Available: 문 닫힘',
|
||||||
style: TextStyle(color: _mainTextColor)),
|
style: TextStyle(color: _mainTextColor)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 24.0, top: 4),
|
padding: const EdgeInsets.only(left: 210, top: 4),
|
||||||
child: Text('(2025-11-19)/(08:58)',
|
child: Text('(2025-11-19)/(08:58)',
|
||||||
style: TextStyle(color: _subTextColor, fontSize: 12)),
|
style: TextStyle(color: _subTextColor, fontSize: 12)),
|
||||||
),
|
),
|
||||||
@@ -364,15 +354,15 @@ class RentalProcessScreen extends StatelessWidget {
|
|||||||
child: ListView(
|
child: ListView(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
children: [
|
children: [
|
||||||
_buildLogItem("08:58:33", "Door Fully Closed",
|
_buildLogItem("08:58:33", "문 열림",
|
||||||
Icons.door_front_door, _mainBlueColor),
|
Icons.door_front_door, _mainBlueColor),
|
||||||
_buildLogItem("08:58:30", "Helmet Returned (Sensor A)",
|
_buildLogItem("08:58:30", "헬멧 반납 확인(센서 A)",
|
||||||
Icons.check_circle_outline, _mainBlueColor),
|
Icons.check_circle_outline, _mainBlueColor),
|
||||||
_buildLogItem("08:55:12", "User Unlocked Door",
|
_buildLogItem("08:55:12", "사용자 문 잠금 해제",
|
||||||
Icons.lock_open, _mainTextColor),
|
Icons.lock_open, _mainTextColor),
|
||||||
_buildLogItem("08:30:00", "UV Sanitization Complete",
|
_buildLogItem("08:30:00", "UV 살균 완료",
|
||||||
Icons.cleaning_services, _mainBlueColor),
|
Icons.cleaning_services, _mainBlueColor),
|
||||||
_buildLogItem("08:00:00", "System Boot Up",
|
_buildLogItem("08:00:00", "시스템 가동 중",
|
||||||
Icons.power_settings_new, _subTextColor),
|
Icons.power_settings_new, _subTextColor),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ final Color _subTextColor = const Color(0xFF6A717B);
|
|||||||
final Color _pageBackgroundColor = const Color(0xFFF5F7F9);
|
final Color _pageBackgroundColor = const Color(0xFFF5F7F9);
|
||||||
final Color _cardBackgroundColor = Colors.white;
|
final Color _cardBackgroundColor = Colors.white;
|
||||||
final Color _accentContainerColor = const Color(0xFFF0F2F5);
|
final Color _accentContainerColor = const Color(0xFFF0F2F5);
|
||||||
final Color _warningColor = Colors.redAccent;
|
final Color _warningColor = const Color(0xFFFF3B30);
|
||||||
|
|
||||||
const BoxShadow _cleanShadow = BoxShadow(
|
const BoxShadow _cleanShadow = BoxShadow(
|
||||||
color: Color.fromRGBO(0, 0, 0, 0.07),
|
color: Color.fromRGBO(0, 0, 0, 0.07),
|
||||||
@@ -15,7 +15,6 @@ const BoxShadow _cleanShadow = BoxShadow(
|
|||||||
spreadRadius: 0,
|
spreadRadius: 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
class SettingsScreen extends StatefulWidget {
|
class SettingsScreen extends StatefulWidget {
|
||||||
const SettingsScreen({super.key});
|
const SettingsScreen({super.key});
|
||||||
|
|
||||||
@@ -24,14 +23,495 @@ class SettingsScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SettingsScreenState extends State<SettingsScreen> {
|
class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
|
|
||||||
bool _isPushEnabled = true;
|
bool _isPushEnabled = true;
|
||||||
bool _isRentalAlertEnabled = true;
|
bool _isRentalAlertEnabled = true;
|
||||||
bool _isStorageStatusAlert = true;
|
bool _isStorageStatusAlert = true;
|
||||||
bool _isAutoSyncEnabled = false;
|
bool _isEnvSensorAlert = true;
|
||||||
|
|
||||||
final String _currentAppVersion = "v1.0.0";
|
bool _isBiometricEnabled = false;
|
||||||
|
bool _isAutoLogoutEnabled = true;
|
||||||
|
bool _isLoginNotificationEnabled = true;
|
||||||
|
bool _isLocationEnabled = true;
|
||||||
|
|
||||||
|
final String _currentAppVersion = "v1.0.2";
|
||||||
|
|
||||||
|
void _showCommonModal(BuildContext context, String title, Widget content, {String rightButtonLabel = '닫기'}) {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
builder: (context) {
|
||||||
|
return Container(
|
||||||
|
height: MediaQuery.of(context).size.height * 0.85,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _pageBackgroundColor,
|
||||||
|
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(top: 12, bottom: 20),
|
||||||
|
width: 40,
|
||||||
|
height: 4,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
borderRadius: BorderRadius.circular(2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: _mainTextColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: Text(rightButtonLabel,
|
||||||
|
style: TextStyle(color: _mainBlueColor, fontWeight: FontWeight.bold)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
Expanded(
|
||||||
|
child: content,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showEditProfileSheet(BuildContext context) {
|
||||||
|
_showCommonModal(
|
||||||
|
context,
|
||||||
|
'내 정보 수정',
|
||||||
|
ListView(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
children: [
|
||||||
|
_buildInputGroup('닉네임', '현재 닉네임'),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildInputGroup('이메일', 'user@example.com', isReadOnly: true),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildInputGroup('전화번호', '010-1234-5678'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
rightButtonLabel: '완료',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showPasswordChangeSheet(BuildContext context) {
|
||||||
|
_showCommonModal(
|
||||||
|
context,
|
||||||
|
'비밀번호 변경',
|
||||||
|
ListView(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
children: [
|
||||||
|
_buildInputGroup('현재 비밀번호', '사용 중인 비밀번호 입력', isObscure: true),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildInputGroup('새 비밀번호', '영문, 숫자, 특수문자 포함 8자 이상', isObscure: true),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildInputGroup('새 비밀번호 확인', '비밀번호 재입력', isObscure: true),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 50,
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: _mainBlueColor,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
|
),
|
||||||
|
child: const Text('변경하기', style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showDeviceListSheet(BuildContext context) {
|
||||||
|
_showCommonModal(
|
||||||
|
context,
|
||||||
|
'로그인 관리',
|
||||||
|
ListView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
_buildSectionTitle('현재 접속 중인 기기'),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildDeviceItem('Galaxy S24 Ultra', '서울, 대한민국 • 지금 활동 중', true),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
_buildSectionTitle('다른 접속 기기'),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildDeviceItem('iPhone 15 Pro', '부산, 대한민국 • 3시간 전', false),
|
||||||
|
_buildDeviceItem('Chrome (Windows)', '경기도 성남시 • 1일 전', false),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showNotificationStyleSheet(BuildContext context) {
|
||||||
|
_showCommonModal(
|
||||||
|
context,
|
||||||
|
'알림 방식 설정',
|
||||||
|
ListView(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
children: [
|
||||||
|
_buildRadioItem('배너 + 진동', true),
|
||||||
|
_buildRadioItem('배너만 표시', false),
|
||||||
|
_buildRadioItem('진동만 울림', false),
|
||||||
|
_buildRadioItem('무음', false),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
rightButtonLabel: '저장',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showClearCacheSheet(BuildContext context) {
|
||||||
|
_showCommonModal(
|
||||||
|
context,
|
||||||
|
'캐시 데이터 관리',
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(24.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.cleaning_services_outlined, size: 60, color: _subTextColor),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Text(
|
||||||
|
'저장된 캐시 데이터: 12.5 MB',
|
||||||
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: _mainTextColor),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
'캐시 데이터를 삭제하면 앱의 로딩 속도가 느려질 수 있지만,\n저장 공간을 확보할 수 있습니다.\n로그인 정보나 중요한 설정은 삭제되지 않습니다.',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: _subTextColor, height: 1.5),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 40),
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 50,
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
backgroundColor: _warningColor.withOpacity(0.1),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
|
),
|
||||||
|
child: Text('모두 삭제하기', style: TextStyle(color: _warningColor, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showTermsSheet(BuildContext context) {
|
||||||
|
_showCommonModal(
|
||||||
|
context,
|
||||||
|
'이용 약관',
|
||||||
|
SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
child: Text(
|
||||||
|
'제 1 조 (목적)\n이 약관은 스마트 헬멧 서비스(이하 "서비스")의 이용 조건 및 절차, 이용자와 회사의 권리, 의무, 책임 사항을 규정함을 목적으로 합니다.\n\n제 2 조 (용어의 정의)\n1. "이용자"란 앱에 접속하여 본 약관에 따라 서비스를 이용하는 회원을 말합니다.\n2. "헬멧"이란 회사가 대여하는 스마트 IoT 안전모를 말합니다.\n\n(이하 생략... 더미 데이터입니다.)\n\n제 3 조 (약관의 효력)\n본 약관은 서비스를 신청한 때부터 효력이 발생합니다.',
|
||||||
|
style: TextStyle(color: _subTextColor, height: 1.6),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showLicenseSheet(BuildContext context) {
|
||||||
|
_showCommonModal(
|
||||||
|
context,
|
||||||
|
'오픈소스 라이선스',
|
||||||
|
ListView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
_buildLicenseItem('Flutter', 'Google', 'BSD-style'),
|
||||||
|
_buildLicenseItem('Cupertino Icons', 'Google', 'MIT'),
|
||||||
|
_buildLicenseItem('Kakao Maps SDK', 'Kakao Corp.', 'Apache 2.0'),
|
||||||
|
_buildLicenseItem('Firebase Core', 'Google', 'Apache 2.0'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showSupportSheet(BuildContext context) {
|
||||||
|
_showCommonModal(
|
||||||
|
context,
|
||||||
|
'고객센터',
|
||||||
|
ListView(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _accentContainerColor,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const Text('운영 시간: 평일 09:00 ~ 18:00', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text('(점심시간 12:00 ~ 13:00)', style: TextStyle(color: _subTextColor, fontSize: 12)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildInfoLink('전화 상담 연결', Icons.phone, value: '1588-0000'),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildInfoLink('1:1 채팅 상담', Icons.chat_bubble_outline),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildInfoLink('이메일 문의', Icons.email_outlined, value: 'help@smarthelmet.com'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showFAQSheet(BuildContext context) {
|
||||||
|
_showCommonModal(
|
||||||
|
context,
|
||||||
|
'자주 묻는 질문 (FAQ)',
|
||||||
|
ListView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
_buildFAQItem('Q. 헬멧 대여는 어떻게 하나요?', 'A. 메인 화면의 지도에서 가까운 보관함을 찾은 후, QR코드를 스캔하여 대여할 수 있습니다.'),
|
||||||
|
_buildFAQItem('Q. 반납이 안 될 때는 어떻게 하나요?', 'A. 보관함의 통신 상태를 확인해 주세요. 지속적으로 실패할 경우 고객센터로 연락 바랍니다.'),
|
||||||
|
_buildFAQItem('Q. 결제 수단 변경은 어디서 하나요?', 'A. [마이페이지] > [결제 관리] 메뉴에서 카드 정보를 변경하실 수 있습니다.'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showMapInfoSheet(BuildContext context) {
|
||||||
|
_showCommonModal(
|
||||||
|
context,
|
||||||
|
'서비스 지역 안내',
|
||||||
|
Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.map_outlined, size: 80, color: Colors.grey.shade300),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Text('서비스 지역 지도 표시 영역', style: TextStyle(fontSize: 16, color: _subTextColor, fontWeight: FontWeight.bold)),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text('현재 서울, 경기 일부 지역에서\n서비스를 이용하실 수 있습니다.', textAlign: TextAlign.center, style: TextStyle(color: _subTextColor)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showPushPermissionSheet(BuildContext context) {
|
||||||
|
_showCommonModal(
|
||||||
|
context,
|
||||||
|
'알림 설정 안내',
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(24.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text('알림 권한이 꺼져 있나요?', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text('중요한 헬멧 안전 경고 및 반납 알림을 받으려면 기기 설정에서 알림을 허용해야 합니다.', style: TextStyle(color: _subTextColor, height: 1.5)),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
_buildInfoLink('기기 설정으로 이동', Icons.settings, onTap: () => Navigator.pop(context)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showCameraPermissionSheet(BuildContext context) {
|
||||||
|
_showCommonModal(
|
||||||
|
context,
|
||||||
|
'카메라 권한 설정',
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(24.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _accentContainerColor,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Icon(Icons.camera_alt, size: 40, color: _mainBlueColor),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
const Text(
|
||||||
|
'카메라 권한이 필요합니다',
|
||||||
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
'헬멧 대여 및 반납 시 QR코드를 스캔하기 위해\n카메라 접근 권한이 반드시 필요합니다.\n권한을 거부하면 서비스를 이용할 수 없습니다.',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: _subTextColor, height: 1.5),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(color: Colors.grey.shade200),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Text('현재 상태', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
Text('허용됨', style: TextStyle(color: _mainBlueColor, fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 50,
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: _mainTextColor,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
|
),
|
||||||
|
child: const Text('기기 설정에서 변경하기', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDeviceItem(String name, String info, bool isCurrent) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: isCurrent ? _mainBlueColor : Colors.transparent, width: 1.5),
|
||||||
|
boxShadow: [_cleanShadow],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(isCurrent ? Icons.phone_android : Icons.devices_other, color: isCurrent ? _mainBlueColor : _subTextColor),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(name, style: TextStyle(fontWeight: FontWeight.bold, color: _mainTextColor)),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(info, style: TextStyle(fontSize: 12, color: _subTextColor)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (!isCurrent)
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {},
|
||||||
|
child: Text('로그아웃', style: TextStyle(color: _warningColor, fontSize: 12)),
|
||||||
|
),
|
||||||
|
if (isCurrent)
|
||||||
|
Text('현재 기기', style: TextStyle(color: _mainBlueColor, fontSize: 12, fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildRadioItem(String title, bool isSelected) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(title, style: TextStyle(color: _mainTextColor, fontSize: 15)),
|
||||||
|
trailing: isSelected ? Icon(Icons.check_circle, color: _mainBlueColor) : Icon(Icons.circle_outlined, color: Colors.grey.shade300),
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildLicenseItem(String libName, String author, String licenseType) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
|
child: ExpansionTile(
|
||||||
|
title: Text(libName, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15)),
|
||||||
|
subtitle: Text('$author • $licenseType'),
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Text(
|
||||||
|
'Permission is hereby granted, free of charge, to any person obtaining a copy of this software...',
|
||||||
|
style: TextStyle(color: _subTextColor, fontSize: 12),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFAQItem(String question, String answer) {
|
||||||
|
return Card(
|
||||||
|
elevation: 0,
|
||||||
|
color: Colors.white,
|
||||||
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12), side: BorderSide(color: _accentContainerColor)),
|
||||||
|
child: ExpansionTile(
|
||||||
|
title: Text(question, style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: _mainTextColor)),
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
color: _accentContainerColor.withOpacity(0.5),
|
||||||
|
child: Text(answer, style: TextStyle(color: _subTextColor, fontSize: 13, height: 1.5)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildInputGroup(String label, String placeholder, {bool isReadOnly = false, bool isObscure = false}) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(label, style: TextStyle(color: _subTextColor, fontSize: 13, fontWeight: FontWeight.w600)),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
readOnly: isReadOnly,
|
||||||
|
obscureText: isObscure,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: placeholder,
|
||||||
|
hintStyle: TextStyle(color: _subTextColor.withOpacity(0.5)),
|
||||||
|
filled: true,
|
||||||
|
fillColor: isReadOnly ? _accentContainerColor : Colors.white,
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
borderSide: BorderSide.none,
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -41,19 +521,15 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
title: Text(
|
title: Text(
|
||||||
'설정',
|
'설정',
|
||||||
style: TextStyle(
|
style: TextStyle(fontWeight: FontWeight.w700, fontSize: 16, color: _mainTextColor),
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
fontSize: 16,
|
|
||||||
color: _mainTextColor,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
backgroundColor: _pageBackgroundColor,
|
backgroundColor: _pageBackgroundColor,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.more_vert, color: _mainTextColor),
|
icon: Icon(Icons.close, color: _mainTextColor),
|
||||||
onPressed: () {},
|
onPressed: () => Navigator.pop(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -63,21 +539,157 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
_buildSectionTitle('계정 및 보안'),
|
_buildSectionTitle('계정 및 보안'),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 8),
|
||||||
_buildAccountSecurityCard(),
|
_buildCardWrapper(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildInfoLink('내 정보 수정', Icons.person_outline, onTap: () => _showEditProfileSheet(context)),
|
||||||
|
_buildDivider(16),
|
||||||
|
_buildInfoLink('비밀번호 변경', Icons.lock_outline, onTap: () => _showPasswordChangeSheet(context)),
|
||||||
|
_buildDivider(16),
|
||||||
|
_buildInfoLink('로그인 관리 / 기기 목록', Icons.devices, onTap: () => _showDeviceListSheet(context)),
|
||||||
|
_buildDivider(0),
|
||||||
|
_buildToggleItem(
|
||||||
|
'생체 인증 사용',
|
||||||
|
'앱 잠금 해제 및 인증',
|
||||||
|
_isBiometricEnabled,
|
||||||
|
(val) => setState(() => _isBiometricEnabled = val),
|
||||||
|
showDivider: true,
|
||||||
|
),
|
||||||
|
_buildToggleItem(
|
||||||
|
'이상 감지 시 자동 로그아웃',
|
||||||
|
'보안 위험 감지 시 즉시 로그아웃',
|
||||||
|
_isAutoLogoutEnabled,
|
||||||
|
(val) => setState(() => _isAutoLogoutEnabled = val),
|
||||||
|
showDivider: true,
|
||||||
|
),
|
||||||
|
_buildToggleItem(
|
||||||
|
'새 기기 로그인 알림',
|
||||||
|
'이메일로 알림 발송',
|
||||||
|
_isLoginNotificationEnabled,
|
||||||
|
(val) => setState(() => _isLoginNotificationEnabled = val),
|
||||||
|
showDivider: false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
_buildSectionTitle('알림 설정'),
|
_buildSectionTitle('알림 설정'),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 8),
|
||||||
_buildNotificationCard(),
|
_buildCardWrapper(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildToggleItem(
|
||||||
|
'알림 전체 수신',
|
||||||
|
'모든 푸시 알림을 켜고 끕니다.',
|
||||||
|
_isPushEnabled,
|
||||||
|
(val) => setState(() => _isPushEnabled = val),
|
||||||
|
showDivider: true,
|
||||||
|
),
|
||||||
|
if (_isPushEnabled) ...[
|
||||||
|
_buildToggleItem(
|
||||||
|
'대여/반납 알림',
|
||||||
|
'시작, 종료, 반납 실패',
|
||||||
|
_isRentalAlertEnabled,
|
||||||
|
(val) => setState(() => _isRentalAlertEnabled = val),
|
||||||
|
showDivider: true,
|
||||||
|
),
|
||||||
|
_buildToggleItem(
|
||||||
|
'보관함 상태 알림',
|
||||||
|
'살균/건조 완료, 시스템 오류',
|
||||||
|
_isStorageStatusAlert,
|
||||||
|
(val) => setState(() => _isStorageStatusAlert = val),
|
||||||
|
showDivider: true,
|
||||||
|
),
|
||||||
|
_buildToggleItem(
|
||||||
|
'환경 센서 경고',
|
||||||
|
'비정상 온도, 배터리 부족, 통신 장애',
|
||||||
|
_isEnvSensorAlert,
|
||||||
|
(val) => setState(() => _isEnvSensorAlert = val),
|
||||||
|
showDivider: true,
|
||||||
|
),
|
||||||
|
_buildDivider(16),
|
||||||
|
_buildInfoLink('알림 방식', Icons.notifications_active_outlined, value: '배너 + 진동', onTap: () => _showNotificationStyleSheet(context)),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
_buildSectionTitle('데이터 및 캐시'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildCardWrapper(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildInfoLink('캐시 데이터 삭제', Icons.cleaning_services_outlined, value: '12.5 MB', onTap: () => _showClearCacheSheet(context)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
_buildSectionTitle('위치 및 권한'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildCardWrapper(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildToggleItem(
|
||||||
|
'위치 서비스 사용',
|
||||||
|
'내 주변 보관함 찾기',
|
||||||
|
_isLocationEnabled,
|
||||||
|
(val) => setState(() => _isLocationEnabled = val),
|
||||||
|
showDivider: true,
|
||||||
|
),
|
||||||
|
_buildInfoLink('카메라 권한', Icons.camera_alt_outlined, value: '허용됨', onTap: () => _showCameraPermissionSheet(context)),
|
||||||
|
_buildDivider(16),
|
||||||
|
_buildInfoLink('푸시 권한 설정 안내', Icons.settings_applications_outlined, onTap: () => _showPushPermissionSheet(context)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
_buildSectionTitle('앱 정보'),
|
_buildSectionTitle('앱 정보'),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 8),
|
||||||
_buildAppInfoCard(),
|
_buildCardWrapper(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildInfoLink('버전 정보', null, value: _currentAppVersion, showArrow: false),
|
||||||
|
_buildDivider(16),
|
||||||
|
_buildInfoLink('이용 약관 및 개인정보 처리방침', Icons.article_outlined, onTap: () => _showTermsSheet(context)),
|
||||||
|
_buildDivider(16),
|
||||||
|
_buildInfoLink('오픈소스 라이선스', Icons.code, onTap: () => _showLicenseSheet(context)),
|
||||||
|
_buildDivider(16),
|
||||||
|
_buildInfoLink('고객센터 / 1:1 문의', Icons.headset_mic_outlined, onTap: () => _showSupportSheet(context)),
|
||||||
|
_buildDivider(16),
|
||||||
|
_buildInfoLink('FAQ', Icons.help_outline, onTap: () => _showFAQSheet(context)),
|
||||||
|
_buildDivider(16),
|
||||||
|
_buildInfoLink('서비스 지역 지도 보기', Icons.map_outlined, onTap: () => _showMapInfoSheet(context)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
const SizedBox(height: 40),
|
const SizedBox(height: 40),
|
||||||
_buildWithdrawalButton(),
|
Center(
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: () {},
|
||||||
|
child: Text(
|
||||||
|
'회원 탈퇴 신청',
|
||||||
|
style: TextStyle(
|
||||||
|
color: _warningColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
'Smart Helmet App © 2025',
|
||||||
|
style: TextStyle(color: _subTextColor.withOpacity(0.5), fontSize: 12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 40),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -86,14 +698,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
|
|
||||||
Widget _buildSectionTitle(String title) {
|
Widget _buildSectionTitle(String title) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 8.0),
|
padding: const EdgeInsets.only(left: 4.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
title,
|
title,
|
||||||
style: TextStyle(
|
style: TextStyle(color: _subTextColor, fontSize: 13, fontWeight: FontWeight.w600),
|
||||||
color: _mainTextColor,
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -102,79 +710,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: _cardBackgroundColor,
|
color: _cardBackgroundColor,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(10),
|
||||||
boxShadow: [_cleanShadow],
|
boxShadow: [_cleanShadow],
|
||||||
),
|
),
|
||||||
child: child,
|
child: ClipRRect(
|
||||||
);
|
borderRadius: BorderRadius.circular(10),
|
||||||
}
|
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() {
|
Widget _buildToggleItem(
|
||||||
return _buildCardWrapper(
|
String title, String subtitle, bool value, ValueChanged<bool> onChanged,
|
||||||
child: Column(
|
{required bool showDivider}) {
|
||||||
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(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
@@ -186,46 +734,37 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(title, style: TextStyle(color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.w500)),
|
||||||
title,
|
const SizedBox(height: 2),
|
||||||
style: TextStyle(
|
Text(subtitle, style: TextStyle(color: _subTextColor, fontSize: 11)),
|
||||||
color: _mainTextColor,
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
subtitle,
|
|
||||||
style: TextStyle(
|
|
||||||
color: _subTextColor,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Switch(
|
Transform.scale(
|
||||||
value: value,
|
scale: 0.8,
|
||||||
onChanged: onChanged,
|
child: Switch(
|
||||||
activeColor: _mainBlueColor,
|
value: value,
|
||||||
inactiveThumbColor: _accentContainerColor,
|
onChanged: onChanged,
|
||||||
inactiveTrackColor: Colors.grey.shade400,
|
activeColor: _cardBackgroundColor,
|
||||||
|
activeTrackColor: _mainBlueColor,
|
||||||
|
inactiveThumbColor: Colors.white,
|
||||||
|
inactiveTrackColor: _accentContainerColor,
|
||||||
|
trackOutlineColor: MaterialStateProperty.resolveWith((states) => Colors.transparent),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (showDivider)
|
if (showDivider) _buildDivider(16),
|
||||||
_buildDivider(0),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildInfoLink(String title, IconData? icon, {String? value}) {
|
Widget _buildInfoLink(String title, IconData? icon, {String? value, VoidCallback? onTap, bool showArrow = true}) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () { /* Navigation logic */ },
|
onTap: onTap ?? () {},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 14.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
if (icon != null) ...[
|
if (icon != null) ...[
|
||||||
@@ -233,18 +772,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
],
|
],
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(title, style: TextStyle(color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.w500)),
|
||||||
title,
|
|
||||||
style: TextStyle(color: _mainTextColor, fontSize: 14),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (value != null)
|
if (value != null)
|
||||||
Text(
|
Text(value, style: TextStyle(color: _subTextColor, fontSize: 13)),
|
||||||
value,
|
if (showArrow) ...[
|
||||||
style: TextStyle(color: _subTextColor, fontSize: 14),
|
const SizedBox(width: 8),
|
||||||
)
|
Icon(Icons.arrow_forward_ios, color: _subTextColor.withOpacity(0.5), size: 14),
|
||||||
else
|
],
|
||||||
Icon(Icons.arrow_forward_ios, color: _subTextColor, size: 16),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -252,18 +787,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDivider(double indent) {
|
Widget _buildDivider(double indent) {
|
||||||
return Divider(color: _subTextColor.withOpacity(0.4), height: 1, thickness: 0.5, indent: indent, endIndent: 16);
|
return Divider(
|
||||||
}
|
color: _accentContainerColor,
|
||||||
|
height: 1,
|
||||||
Widget _buildWithdrawalButton() {
|
thickness: 1,
|
||||||
return Center(
|
indent: indent,
|
||||||
child: TextButton(
|
endIndent: 0,
|
||||||
onPressed: () {},
|
|
||||||
child: Text(
|
|
||||||
'회원 탈퇴 신청',
|
|
||||||
style: TextStyle(color: _warningColor, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,12 +4,10 @@ final Color _mainBlueColor = const Color(0xFF0033CC);
|
|||||||
final Color _mainTextColor = const Color(0xFF1C1C1E);
|
final Color _mainTextColor = const Color(0xFF1C1C1E);
|
||||||
final Color _subTextColor = const Color(0xFF6A717B);
|
final Color _subTextColor = const Color(0xFF6A717B);
|
||||||
final Color _pageBackgroundColor = const Color(0xFFF5F7F9);
|
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});
|
||||||
|
|
||||||
static const double _uniformGap = 16.0;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
@@ -21,28 +19,35 @@ class CustomHeader extends StatelessWidget {
|
|||||||
color: Color.fromRGBO(0, 0, 0, 0.07),
|
color: Color.fromRGBO(0, 0, 0, 0.07),
|
||||||
offset: Offset(0, 4),
|
offset: Offset(0, 4),
|
||||||
blurRadius: 6,
|
blurRadius: 6,
|
||||||
spreadRadius: 0,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: _uniformGap),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
children: [
|
||||||
Row(children: [
|
Row(
|
||||||
Icon(Icons.settings_input_component, color: Colors.black),
|
children: [
|
||||||
const SizedBox(width: 10),
|
Container(
|
||||||
Text(
|
width: 18,
|
||||||
'스마트 헬멧',
|
height: 18,
|
||||||
style: TextStyle(
|
decoration: BoxDecoration(
|
||||||
fontSize: 18,
|
color: _mainBlueColor,
|
||||||
fontWeight: FontWeight.w500,
|
shape: BoxShape.circle,
|
||||||
color: _mainTextColor,
|
),
|
||||||
letterSpacing: 0.5,
|
|
||||||
),
|
),
|
||||||
)
|
const SizedBox(width: 10),
|
||||||
]),
|
Text(
|
||||||
|
'스마트 헬멧 보관함',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: _mainTextColor,
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
'2025/09/26 - 10:44 AM',
|
'2025/09/26 - 10:44 AM',
|
||||||
style: TextStyle(color: _subTextColor, fontSize: 11),
|
style: TextStyle(color: _subTextColor, fontSize: 11),
|
||||||
@@ -52,3 +57,47 @@ class CustomHeader extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(const MaterialApp(home: ScrollableHeaderPage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScrollableHeaderPage extends StatelessWidget {
|
||||||
|
const ScrollableHeaderPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: _pageBackgroundColor,
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const CustomHeader(),
|
||||||
|
for (int i = 0; i < 20; i++)
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||||
|
height: 100,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
color: Color.fromRGBO(0, 0, 0, 0.05),
|
||||||
|
blurRadius: 4,
|
||||||
|
offset: Offset(0, 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'컨텐츠 카드 ${i + 1}',
|
||||||
|
style: TextStyle(fontSize: 16, color: _mainTextColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user