import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; //25.12.03 지은 추가 import 'package:smarthelmet_app/services/locker_api.dart'; class RentalProcessScreen extends StatefulWidget { final String stationName; const RentalProcessScreen({ super.key, required this.stationName, }); @override State createState() => _RentalProcessScreenState(); } class _RentalProcessScreenState extends State { static const Color _mainBlueColor = Color(0xFF002FA7); static const Color _mainTextColor = Color(0xFF1C1C1E); static const Color _subTextColor = Color(0xFF6A717B); static const Color _pageBackgroundColor = Color(0xFFF5F7F9); static const Color _cardBackgroundColor = Colors.white; static const Color _accentContainerColor = Color(0xFFF0F2F5); static const Color _borderColor = Color(0xFF007AFF); static const Color _primaryGreen = Color(0xFF4CAF50); static const Color _primaryOrange = Color(0xFFFF3D00); // 25.12.03 지은 추가 시작 final LockerApi _api = LockerApi(); bool _isLoading = false; Future _runLockerAction(String name, Future Function() action) async { if (_isLoading) return; setState(() => _isLoading = true); // 실제 명령 전송 final success = await action(); setState(() => _isLoading = false); if (!mounted) return; } // 25.12.03 지은 추가 끝 final PageController _pageController = PageController(); int _currentPage = 0; Timer? _timer; final List _imageList = [ 'assets/images/top.png', 'assets/images/white_1.png', 'assets/images/white_2.png', ]; @override void initState() { super.initState(); _startAutoScroll(); } @override void dispose() { _timer?.cancel(); _pageController.dispose(); super.dispose(); } void _startAutoScroll() { _timer = Timer.periodic(const Duration(seconds: 3), (Timer timer) { if (_currentPage < _imageList.length - 1) { _currentPage++; } else { _currentPage = 0; } if (_pageController.hasClients) { _pageController.animateToPage( _currentPage, duration: const Duration(milliseconds: 350), curve: Curves.easeIn, ); } }); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: _pageBackgroundColor, appBar: AppBar( scrolledUnderElevation: 0, title: Text( widget.stationName, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, color: _mainTextColor), ), backgroundColor: _cardBackgroundColor, elevation: 0, ), body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildStatusCard(context), const SizedBox(height: 24), _buildProcessSectionTitle('안전모 대여'), const SizedBox(height: 12), Container( decoration: BoxDecoration( color: _cardBackgroundColor, borderRadius: BorderRadius.circular(12), ), child: Column( children: [ _buildStepRow(1, '잠금 해제 & 문 열림', Icons.lock_open, isCompleted: true), _buildDivider(), _buildStepRow(2, '문 상태 확인', Icons.sensor_door_outlined), _buildDivider(), _buildStepRow(3, '안전모 꺼내기', Icons.outbox), _buildDivider(), _buildStepRow(4, '주행 시작', Icons.sentiment_satisfied_alt, showDivider: false), ], ), ), const SizedBox(height: 24), _buildProcessSectionTitle('안전모 반납'), const SizedBox(height: 12), Container( decoration: BoxDecoration( color: _cardBackgroundColor, borderRadius: BorderRadius.circular(12), ), child: Column( children: [ _buildStepRow(1, '잠금해제 & 문열림', Icons.lock_open), _buildDivider(), _buildStepRow(2, '안전모 넣기', Icons.move_to_inbox), _buildDivider(), _buildStepRow(3, '센서 스캔 & 반납 완료', Icons.sync), _buildDivider(), _buildStepRow(4, '살균 시작', 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: _cardBackgroundColor, borderRadius: BorderRadius.circular(12), ), child: Column( children: [ Row( children: const [ Icon(Icons.circle, color: _primaryOrange, size: 16), SizedBox(width: 6), Text('사용 중', style: TextStyle( color: _mainTextColor, fontWeight: FontWeight.bold)), ], ), const SizedBox(height: 20), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 150, height: 150, decoration: BoxDecoration( color: _accentContainerColor, borderRadius: BorderRadius.circular(12), ), child: Stack( alignment: Alignment.bottomCenter, children: [ PageView.builder( controller: _pageController, itemCount: _imageList.length, onPageChanged: (int index) { setState(() { _currentPage = index; }); }, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.fromLTRB(10, 10, 10, 25), child: Image.asset( _imageList[index], fit: BoxFit.contain, errorBuilder: (context, error, stackTrace) => const Icon(Icons.inventory_2, size: 50, color: _subTextColor), ), ); }, ), Positioned( bottom: 10, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(_imageList.length, (index) { return GestureDetector( onTap: () { _pageController.animateToPage( index, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ); }, child: Container( width: 30, height: 6, margin: const EdgeInsets.symmetric(horizontal: 2), decoration: BoxDecoration( borderRadius: BorderRadius.circular(3), color: _currentPage == index ? _mainTextColor : Colors.grey.shade300, ), ), ); }), ), ), ], ), ), const SizedBox(width: 20), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('NOW', style: TextStyle(color: Colors.black, fontSize: 12)), const SizedBox(height: 8), SizedBox( width: double.infinity, height: 68, child: ElevatedButton( onPressed: () { // 25.12.03 지은 추가 _runLockerAction("잠금 해제", () => _api.unlock()); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('문이 열렸습니다! (OPEN 버튼 눌림)'), duration: Duration(seconds: 1), backgroundColor: _mainBlueColor, ), ); }, style: ElevatedButton.styleFrom( backgroundColor: _mainBlueColor, foregroundColor: Colors.white, 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: 10), Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: _accentContainerColor, borderRadius: BorderRadius.circular(8), ), child: const Center( child: Text( '센서 경고 : 잠금 실패', style: TextStyle( color: _primaryOrange, fontWeight: FontWeight.bold, height: 1.2), textAlign: TextAlign.center, ), ), ), ], ), ), ], ), ], ), ); } Widget _buildProcessSectionTitle(String title) { return Text( title, style: const TextStyle( color: _mainTextColor, 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: 18), child: Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text('STEP $step', style: const TextStyle( color: _mainBlueColor, fontSize: 12, fontWeight: FontWeight.w700)), if (isCompleted) ...[ const SizedBox(width: 8), const Icon(Icons.check_circle, color: _primaryGreen, size: 16), ], ], ), const SizedBox(height: 6), Text(title, style: const TextStyle( color: _mainTextColor, fontSize: 16, fontWeight: FontWeight.w600, height: 1.2)), ], ), const Spacer(), Icon(icon, color: isCompleted ? _mainBlueColor : _subTextColor.withOpacity(0.5), size: 32), ], ), ); } Widget _buildDivider() { return Divider( color: _subTextColor.withOpacity(0.1), height: 1, thickness: 1, indent: 20, endIndent: 20); } Widget _buildErrorAlertBox() { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: _accentContainerColor, border: Border.all(color: _primaryOrange, width: 2), borderRadius: BorderRadius.circular(12), ), child: Row( children: const [ Icon(Icons.warning_amber_rounded, color: _primaryOrange, size: 40), SizedBox(width: 16), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('센서 오류 : 잠금 실패', style: TextStyle( color: _primaryOrange, fontWeight: FontWeight.bold, fontSize: 16)), ], ), ], ), ); } Widget _buildLogsCard(BuildContext context) { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: _cardBackgroundColor, borderRadius: BorderRadius.circular(12), ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('로그', style: TextStyle( color: _mainTextColor, fontWeight: FontWeight.bold)), InkWell( onTap: () => _showLogHistory(context), child: const Text('더보기 >', style: TextStyle(color: _subTextColor, fontSize: 12)), ), ], ), const SizedBox(height: 12), Row( children: const [ Icon(Icons.check_circle, color: _mainBlueColor, size: 16), SizedBox(width: 8), Text('Available: 문 닫힘', style: TextStyle(color: _mainTextColor)), ], ), const Padding( padding: EdgeInsets.only(left: 210, top: 4), child: Text('(2025-11-19)/(08:58)', style: TextStyle(color: _subTextColor, 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: _cardBackgroundColor, borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), child: Column( children: [ const SizedBox(height: 12), Container( width: 40, height: 4, decoration: BoxDecoration( color: _subTextColor, borderRadius: BorderRadius.circular(2)), ), const Padding( padding: EdgeInsets.all(20.0), child: Text("DEVICE LOGS", style: TextStyle( color: _mainTextColor, fontSize: 18, fontWeight: FontWeight.bold)), ), Divider(color: _subTextColor.withOpacity(0.5), height: 1), Expanded( child: ListView( padding: const EdgeInsets.all(20), children: [ _buildLogItem("08:58:33", "문 열림", Icons.door_front_door, _mainBlueColor), _buildLogItem("08:58:30", "안전모 반납 확인(센서 A)", Icons.check_circle_outline, _mainBlueColor), _buildLogItem("08:55:12", "사용자 문 잠금 해제", Icons.lock_open, _mainTextColor), _buildLogItem("08:30:00", "UV 살균 완료", Icons.cleaning_services, _mainBlueColor), _buildLogItem("08:00:00", "시스템 가동 중", Icons.power_settings_new, _subTextColor), ], ), ), ], ), ); }, ); } 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: _subTextColor, fontSize: 12)), const SizedBox(width: 16), Column( children: [ Icon(icon, color: color, size: 20), Container( width: 2, height: 20, color: _subTextColor.withOpacity(0.5)), ], ), const SizedBox(width: 16), Expanded( child: Text(message, style: const TextStyle(color: _mainTextColor, fontSize: 14)), ), ], ), ); } }