1. 하단 네비게이션 바 수정
2. 대여소 지도 및 리스트 화면 제작 (RentReturnScreen) 3. 대여/반납 상세 진행 화면 구현 (RentalProcessScreen)
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AlertsReportScreen extends StatelessWidget {
|
||||
const AlertsReportScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Center(
|
||||
child: Text(
|
||||
'4. 알림/신고 (Alerts/Report) 페이지',
|
||||
style: TextStyle(color: Colors.white70, fontSize: 20),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ class HomeScreenContent extends StatefulWidget {
|
||||
class _HomeScreenContentState extends State<HomeScreenContent> {
|
||||
static const double _uniformGap = 16.0;
|
||||
|
||||
// 포인트 컬러 (배터리, 상태 표시 등에는 유지)
|
||||
final Color _pointColor = Colors.redAccent;
|
||||
|
||||
final Map<String, bool> _controlToggles = {
|
||||
@@ -117,8 +116,8 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20.0),
|
||||
child: Image.asset(
|
||||
'assets/images/helmet.png',
|
||||
width: 100,
|
||||
'assets/images/open.png',
|
||||
width: 120,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
@@ -381,7 +380,6 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
// ⭐ [수정됨] ON(화이트), OFF(다크그레이) -> 블랙 앤 화이트 테마 적용
|
||||
color: value ? Colors.white : Colors.grey.shade700,
|
||||
),
|
||||
child: Stack(
|
||||
@@ -397,7 +395,6 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
||||
height: 26,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
// ⭐ [수정됨] ON(블랙), OFF(화이트) -> 배경과 대비되는 색상
|
||||
color: value ? const Color(0xFF27292B) : Colors.white,
|
||||
),
|
||||
),
|
||||
@@ -411,7 +408,6 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
// ⭐ [수정됨] ON 텍스트 블랙
|
||||
color: value
|
||||
? const Color(0xFF27292B)
|
||||
: Colors.transparent)))),
|
||||
@@ -564,6 +560,7 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
||||
TileLayer(
|
||||
urlTemplate:
|
||||
'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
|
||||
userAgentPackageName: 'com.example.app',
|
||||
subdomains: const ['a', 'b', 'c', 'd'],
|
||||
retinaMode: true,
|
||||
),
|
||||
@@ -636,10 +633,8 @@ class _HomeScreenContentState extends State<HomeScreenContent> {
|
||||
style: const TextStyle(fontSize: 11, color: Colors.white70));
|
||||
}
|
||||
}
|
||||
// --- _HomeScreenContentState 끝 ---
|
||||
|
||||
|
||||
// 👇 HOME 화면에 사용되는 4가지 커스텀 클래스 정의
|
||||
|
||||
class _LineChartPlaceholder extends StatelessWidget {
|
||||
const _LineChartPlaceholder();
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:smarthelmet_app/home_screen_content.dart';
|
||||
import 'package:smarthelmet_app/control_screen.dart';
|
||||
import 'package:smarthelmet_app/history_screen.dart';
|
||||
import 'package:smarthelmet_app/alerts_report_screen.dart';
|
||||
import 'package:smarthelmet_app/rent_return_screen.dart';
|
||||
import 'package:smarthelmet_app/settings_screen.dart';
|
||||
|
||||
void main() {
|
||||
@@ -51,7 +51,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
const HomeScreenContent(),
|
||||
const HistoryScreen(),
|
||||
const ControlScreen(),
|
||||
const AlertsReportScreen(),
|
||||
const RentReturnScreen(),
|
||||
const SettingsScreen(),
|
||||
];
|
||||
|
||||
@@ -91,11 +91,11 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'HOME'),
|
||||
BottomNavigationBarItem(icon: Icon(Icons.history), label: 'HISTORY'),
|
||||
BottomNavigationBarItem(icon: Icon(Icons.settings_input_component), label: 'CONTROL'),
|
||||
BottomNavigationBarItem(icon: Icon(Icons.notifications_active), label: 'ALERTS'),
|
||||
BottomNavigationBarItem(icon: Icon(Icons.assignment_return_outlined), label: 'RENT/RETURN'),
|
||||
BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'SETTINGS'),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
356
lib/rent_return_screen.dart
Normal file
356
lib/rent_return_screen.dart
Normal file
@@ -0,0 +1,356 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'rental_process_screen.dart';
|
||||
|
||||
class StationInfo {
|
||||
final String name;
|
||||
final String address;
|
||||
final String status;
|
||||
final String distance;
|
||||
|
||||
StationInfo({
|
||||
required this.name,
|
||||
required this.address,
|
||||
required this.status,
|
||||
required this.distance,
|
||||
});
|
||||
}
|
||||
|
||||
class RentReturnScreen extends StatelessWidget {
|
||||
const RentReturnScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final LatLng centerLocation = const LatLng(35.1595, 126.8526);
|
||||
|
||||
|
||||
final List<StationInfo> stations = [
|
||||
StationInfo(
|
||||
name: 'STATION A - GU.UNIV',
|
||||
address: '277 Hyodeok-ro',
|
||||
status: 'ONLINE',
|
||||
distance: '0.5 km away'),
|
||||
StationInfo(
|
||||
name: 'STATION B - CITY HALL',
|
||||
address: '123 City Hall Ave',
|
||||
status: 'ONLINE',
|
||||
distance: '1.2 km away'),
|
||||
StationInfo(
|
||||
name: 'STATION C - PARK',
|
||||
address: '55 Park Lane',
|
||||
status: 'OFFLINE',
|
||||
distance: '2.8 km away'),
|
||||
StationInfo(
|
||||
name: 'STATION D - TECH HUB',
|
||||
address: '88 Innovation Blvd',
|
||||
status: 'ONLINE',
|
||||
distance: '4.1 km away'),
|
||||
];
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFF1E1E1E),
|
||||
appBar: AppBar(
|
||||
title: const Text(
|
||||
'RENT/ RETURN',
|
||||
style: TextStyle(
|
||||
fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
),
|
||||
backgroundColor: const Color(0xFF2C2C2E),
|
||||
elevation: 0,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.more_vert, color: Colors.white),
|
||||
onPressed: () {},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 50,
|
||||
child: FlutterMap(
|
||||
options: MapOptions(
|
||||
initialCenter: centerLocation,
|
||||
initialZoom: 15.0,
|
||||
),
|
||||
children: [
|
||||
TileLayer(
|
||||
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
userAgentPackageName: 'com.example.helmet_app',
|
||||
),
|
||||
MarkerLayer(
|
||||
markers: [
|
||||
_buildBlackMarker(centerLocation),
|
||||
_buildBlackMarker(const LatLng(35.162, 126.855)),
|
||||
_buildBlackMarker(const LatLng(35.157, 126.850)),
|
||||
_buildBlackMarker(const LatLng(35.160, 126.858)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
flex: 45,
|
||||
child: Container(
|
||||
color: const Color(0xFF1E1E1E),
|
||||
padding: const EdgeInsets.fromLTRB(16, 20, 16, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'LIST VIEW',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
itemCount: stations.length,
|
||||
itemBuilder: (context, index) {
|
||||
return _buildStationItem(context, stations[index]);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStationItem(BuildContext context, StationInfo station) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF2C2C2E),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[800],
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Image.asset(
|
||||
'assets/images/storage.png',
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (context, error, stackTrace) => const Icon(Icons.inventory_2, color: Colors.white54),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
station.name,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 13,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
station.address,
|
||||
style: const TextStyle(
|
||||
color: Colors.grey, fontSize: 11, height: 1.2),
|
||||
maxLines: 2,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'STATUS: ${station.status}',
|
||||
style: TextStyle(
|
||||
color: station.status.startsWith('OFFLINE')
|
||||
? Colors.redAccent
|
||||
: Colors.grey,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
station.distance,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 11),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => RentalProcessScreen(
|
||||
stationName: station.name,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
minimumSize: const Size(80, 40),
|
||||
),
|
||||
child: const Text(
|
||||
'SELECT',
|
||||
style: TextStyle(fontWeight: FontWeight.w500, letterSpacing: 0.8, fontSize: 14)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 12),
|
||||
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF1E1E1E),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
'LOGS',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () => _showLogHistory(context, station.name),
|
||||
child: Text(
|
||||
'VIEW MORE >',
|
||||
style: TextStyle(
|
||||
color: Colors.grey[400],
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
const Text(
|
||||
'Available: Door Fully Closed',
|
||||
style: TextStyle(color: Colors.grey, fontSize: 11),
|
||||
),
|
||||
const Text(
|
||||
'(2025-11-19)/(08:58)',
|
||||
style: TextStyle(color: Colors.grey, fontSize: 11),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Marker _buildBlackMarker(LatLng point) {
|
||||
return Marker(
|
||||
point: point,
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: const Icon(
|
||||
Icons.location_on,
|
||||
color: Colors.black,
|
||||
size: 40,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showLogHistory(BuildContext context, String stationName) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return Container(
|
||||
height: MediaQuery.of(context).size.height * 0.6,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF1E1E1E),
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
width: 40, height: 4,
|
||||
decoration: BoxDecoration(color: Colors.grey[600], borderRadius: BorderRadius.circular(2)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Text("LOGS: $stationName", style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
const Divider(color: Colors.white24, height: 1),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
children: [
|
||||
_buildLogItem("08:58:33", "Door Fully Closed", Icons.door_front_door, Colors.green),
|
||||
_buildLogItem("08:58:30", "Helmet Returned", Icons.check_circle_outline, Colors.blue),
|
||||
_buildLogItem("08:55:12", "User Unlocked Door", Icons.lock_open, Colors.white),
|
||||
_buildLogItem("08:30:00", "UV Sanitization Complete", Icons.cleaning_services, Colors.purpleAccent),
|
||||
_buildLogItem("08:00:00", "System Boot Up", Icons.power_settings_new, Colors.grey),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLogItem(String time, String message, IconData icon, Color color) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(time, style: const TextStyle(color: Colors.grey, fontSize: 12)),
|
||||
const SizedBox(width: 16),
|
||||
Column(
|
||||
children: [
|
||||
Icon(icon, color: color, size: 20),
|
||||
Container(width: 2, height: 20, color: Colors.white12),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(message, style: const TextStyle(color: Colors.white70, fontSize: 14)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
349
lib/rental_process_screen.dart
Normal file
349
lib/rental_process_screen.dart
Normal file
@@ -0,0 +1,349 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class RentalProcessScreen extends StatelessWidget {
|
||||
final String stationName;
|
||||
|
||||
const RentalProcessScreen({
|
||||
super.key,
|
||||
required this.stationName,
|
||||
});
|
||||
|
||||
static const Color kBackgroundColor = Color(0xFF1E1E1E);
|
||||
static const Color kCardColor = Color(0xFF2C2C2E);
|
||||
static const Color kPrimaryGreen = Color(0xFF4CAF50);
|
||||
static const Color kPrimaryOrange = Color(0xFFFF3D00);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: kBackgroundColor,
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
stationName,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: Colors.white),
|
||||
),
|
||||
backgroundColor: kCardColor,
|
||||
elevation: 0,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.more_vert, color: Colors.white),
|
||||
onPressed: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildStatusCard(context),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
_buildProcessSectionTitle('HELMET RENTAL PROCESS'),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: kCardColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildStepRow(1, 'UNLOCK & OPEN', Icons.lock_open, isCompleted: true),
|
||||
_buildDivider(),
|
||||
_buildStepRow(2, 'DOOR STATUS', Icons.sensor_door_outlined),
|
||||
_buildDivider(),
|
||||
_buildStepRow(3, 'TAKE HELMET', Icons.outbox),
|
||||
_buildDivider(),
|
||||
_buildStepRow(4, 'ENJOY RIDING!', Icons.sentiment_satisfied_alt, showDivider: false),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
_buildProcessSectionTitle('HELMET RETURN PROCESS'),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: kCardColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildStepRow(1, 'UNLOCK & OPEN', Icons.lock_open),
|
||||
_buildDivider(),
|
||||
_buildStepRow(2, 'INSERT HELMET', Icons.move_to_inbox),
|
||||
_buildDivider(),
|
||||
_buildStepRow(3, 'SENSOR SCANNING\n& RETURN COMPLETE', Icons.sync),
|
||||
_buildDivider(),
|
||||
_buildStepRow(4, 'SANITIZING', Icons.shield_outlined, showDivider: false),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
_buildErrorAlertBox(),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
_buildLogsCard(context),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusCard(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: kCardColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: const [
|
||||
Icon(Icons.circle, color: kPrimaryOrange, size: 16),
|
||||
SizedBox(width: 6),
|
||||
Text('IN USE', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 150,
|
||||
height: 150,
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: kBackgroundColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Image.asset(
|
||||
'assets/images/open.png',
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (context, error, stackTrace) => const Icon(Icons.inventory_2, size: 50, color: Colors.white54),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('NOW', style: TextStyle(color: Colors.grey, fontSize: 12)),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('문이 열렸습니다! (OPEN 버튼 눌림)'),
|
||||
duration: Duration(seconds: 1),
|
||||
backgroundColor: Colors.blue,
|
||||
),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
child: const Text('OPEN', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: kBackgroundColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Center(
|
||||
child: Text(
|
||||
'SENSOR ERROR:\nLOCK FAIL',
|
||||
style: TextStyle(color: kPrimaryOrange, fontWeight: FontWeight.bold, height: 1.2),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProcessSectionTitle(String title) {
|
||||
return Text(
|
||||
title,
|
||||
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStepRow(int step, String title, IconData icon, {bool isCompleted = false, bool showDivider = true}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text('STEP $step', style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
||||
if (isCompleted) ...[
|
||||
const SizedBox(width: 8),
|
||||
const Icon(Icons.check_circle, color: kPrimaryGreen, size: 16),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(title, style: const TextStyle(color: Colors.white, fontSize: 18, letterSpacing: 0.6)),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
Icon(icon, color: isCompleted ? Colors.white : Colors.grey, size: 48),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDivider() {
|
||||
return const Divider(color: Colors.white, height: 1, thickness: 1, indent: 20, endIndent: 20);
|
||||
}
|
||||
|
||||
Widget _buildErrorAlertBox() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: kCardColor.withOpacity(0.5),
|
||||
border: Border.all(color: kPrimaryOrange, width: 2),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
children: const [
|
||||
Icon(Icons.warning_amber_rounded, color: kPrimaryOrange, size: 40),
|
||||
SizedBox(width: 16),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('SENSOR ERROR:', style: TextStyle(color: kPrimaryOrange, fontWeight: FontWeight.bold, fontSize: 16)),
|
||||
Text('LOCK FAILURE', style: TextStyle(color: kPrimaryOrange, fontWeight: FontWeight.bold, fontSize: 16)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLogsCard(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: kCardColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text('LOGS', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
||||
InkWell(
|
||||
onTap: () => _showLogHistory(context),
|
||||
child: Text('VIEW MORE >', style: TextStyle(color: Colors.grey[400], fontSize: 12)),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: const [
|
||||
Icon(Icons.check_circle, color: kPrimaryGreen, size: 16),
|
||||
SizedBox(width: 8),
|
||||
Text('Available: Door Fully Closed', style: TextStyle(color: Colors.white70)),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24.0, top: 4),
|
||||
child: Text('(2025-11-19)/(08:58)', style: TextStyle(color: Colors.grey[600], fontSize: 12)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showLogHistory(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return Container(
|
||||
height: MediaQuery.of(context).size.height * 0.6,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF1E1E1E),
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
width: 40, height: 4,
|
||||
decoration: BoxDecoration(color: Colors.grey[600], borderRadius: BorderRadius.circular(2)),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(20.0),
|
||||
child: Text("DEVICE LOGS", style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
const Divider(color: Colors.white24, height: 1),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
children: [
|
||||
_buildLogItem("08:58:33", "Door Fully Closed", Icons.door_front_door, Colors.green),
|
||||
_buildLogItem("08:58:30", "Helmet Returned (Sensor A)", Icons.check_circle_outline, Colors.blue),
|
||||
_buildLogItem("08:55:12", "User Unlocked Door", Icons.lock_open, Colors.white),
|
||||
_buildLogItem("08:30:00", "UV Sanitization Complete", Icons.cleaning_services, Colors.purpleAccent),
|
||||
_buildLogItem("08:00:00", "System Boot Up", Icons.power_settings_new, Colors.grey),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLogItem(String time, String message, IconData icon, Color color) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(time, style: const TextStyle(color: Colors.grey, fontSize: 12)),
|
||||
const SizedBox(width: 16),
|
||||
Column(
|
||||
children: [
|
||||
Icon(icon, color: color, size: 20),
|
||||
Container(width: 2, height: 20, color: Colors.white12),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(message, style: const TextStyle(color: Colors.white70, fontSize: 14)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user