import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; import 'dart:ui' as ui; void main() { runApp(const SmartHelmetApp()); } class SmartHelmetApp extends StatelessWidget { const SmartHelmetApp({super.key}); @override Widget build(BuildContext context) { SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( statusBarColor: Colors.transparent, statusBarIconBrightness: Brightness.light, )); return MaterialApp( debugShowCheckedModeBanner: false, theme: ThemeData( brightness: Brightness.dark, scaffoldBackgroundColor: const Color(0xFF27292B), primaryColor: const Color(0xFF30343B), fontFamily: 'Pretendard', textTheme: const TextTheme( bodyLarge: TextStyle(color: Colors.white), bodyMedium: TextStyle(color: Colors.white70), ), appBarTheme: AppBarTheme( backgroundColor: const Color(0xFF27292B), elevation: 0, foregroundColor: Colors.white, ), ), home: const HomeScreen(), ); } } class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State { int _selectedIndex = 0; final Map _overviewToggles = { 'UV LED': true, 'CHARGING': false, 'HELMET': true, 'FAN': false, }; @override Widget build(BuildContext context) { const double horizontalPadding = 29.1; return MediaQuery( data: MediaQuery.of(context).copyWith( textScaler: TextScaler.linear(1.0), ), child: Scaffold( appBar: null, body: SafeArea( bottom: false, child: Column( children: [ _buildCustomHeader(), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: horizontalPadding), child: Column( children: [ const SizedBox(height: 3.43), _buildOverviewHeader(), const SizedBox(height: 3.0), Row( children: [ Expanded(child: _buildImageCard()), const SizedBox(width: 3.0), Expanded(child: _buildInfoCard()), ], ), const SizedBox(height: 3.0), _buildBatteryStatusCard(), const SizedBox(height: 3.0), _buildOverviewCard(), const SizedBox(height: 3.0), _buildEnvironmentSensorsCard(), const SizedBox(height: 3.0), _buildMyLocationCard(), const SizedBox(height: 3.0), _buildActivityCard(), const SizedBox(height: 32.0), ], ), ), ), ], ), ), bottomNavigationBar: Column( mainAxisSize: MainAxisSize.min, children: [ Container( height: 0.38, color: const Color(0xFF5C5D5F), ), BottomNavigationBar( currentIndex: _selectedIndex, onTap: (index) { setState(() { _selectedIndex = index; }); }, type: BottomNavigationBarType.fixed, backgroundColor: const Color(0xFF30343B), elevation: 0, selectedItemColor: Colors.white, unselectedItemColor: Colors.white54, showUnselectedLabels: true, selectedFontSize: 12, unselectedFontSize: 12, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: 'HOME'), BottomNavigationBarItem( icon: Icon(Icons.tune), label: 'CONTROL'), BottomNavigationBarItem( icon: Icon(Icons.location_on), label: 'LOCATION'), BottomNavigationBarItem( icon: Icon(Icons.history), label: 'HISTORY'), ], ), ], ), ), ); } Widget _buildCustomHeader() { return Container( height: 64.0, decoration: const BoxDecoration( color: Color(0xFF30343B), border: Border(bottom: BorderSide(color: Color(0xFF5C5D5F), width: 1.0))), padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row(children: [ const Icon(Icons.settings_input_component_outlined, color: Colors.white), const SizedBox(width: 12), Text('SMART HELMET SYSTEMS', style: TextStyle( fontSize: 13.71, fontWeight: FontWeight.bold, color: Colors.white)) ]), Text('2025/10/02 ・ 03:19 PM', style: TextStyle(color: Colors.white70, fontSize: 10.67)) ]))); } Widget _buildOverviewHeader() { return Container( height: 27.43, padding: const EdgeInsets.symmetric(horizontal: 16.0), decoration: BoxDecoration( color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(5.7)), child: Row(children: [ Text('SYSTEM OVERVIEW', style: TextStyle( color: Colors.white.withOpacity(0.9), fontSize: 9.14, fontWeight: FontWeight.bold)), const Spacer(), SizedBox( width: 24, height: 24, child: IconButton( onPressed: () {}, icon: const Icon(Icons.search, color: Colors.white), iconSize: 17.14, splashRadius: 18, padding: EdgeInsets.zero)), SizedBox( width: 24, height: 24, child: IconButton( onPressed: () {}, icon: const Icon(Icons.notifications_outlined, color: Colors.white), iconSize: 17.14, splashRadius: 18, padding: EdgeInsets.zero)) ])); } Widget _buildImageCard() { return Container( height: 137.0, decoration: BoxDecoration( color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(5.7), ), child: Center( child: Image.asset( 'assets/images/helmet.png', // 이 경로는 실제 프로젝트에 맞게 확인해주세요. width: 100.0, height: 117.0, fit: BoxFit.contain, ), ), ); } Widget _buildInfoCard() { return Container( height: 137.0, decoration: BoxDecoration( color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(5.7)), child: Column(children: [ Expanded( child: Row(children: [ const Expanded( child: Center( child: Text('Name /\nNumber', textAlign: TextAlign.center, style: TextStyle(color: Colors.white70, fontSize: 11)))), const VerticalDivider(color: Colors.white24, thickness: 1), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.person_outline, size: 16, color: Colors.white70), const SizedBox(height: 4), const Text('J.LIM', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 13)), const Text('001', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 13)) ])) ])), const Divider(color: Colors.white24, height: 1), Expanded( child: Row(children: [ const Expanded( child: Center( child: Text('STATUS', style: TextStyle(color: Colors.white70, fontSize: 11)))), const VerticalDivider(color: Colors.white24, thickness: 1), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('UNLOCKED', style: TextStyle( color: Colors.redAccent, fontWeight: FontWeight.bold, fontSize: 11)), const SizedBox(height: 4), Row(mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 7, height: 7, decoration: const BoxDecoration( color: Colors.redAccent, shape: BoxShape.circle)), const SizedBox(width: 4), const Text('ACTIVE', style: TextStyle( color: Colors.redAccent, fontWeight: FontWeight.bold, fontSize: 11)) ]) ])) ])) ])); } Widget _buildBatteryStatusCard() { return Container( height: 132.0, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(5.7)), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('BATTERY STATUS (%)', style: TextStyle(color: Colors.white70, fontSize: 12)), const SizedBox(height: 12), Expanded( child: Row(children: [ SizedBox( width: 70, height: 70, child: Stack(fit: StackFit.expand, children: [ const CircularProgressIndicator( value: 1.0, strokeWidth: 6, backgroundColor: Colors.white24, valueColor: AlwaysStoppedAnimation(Colors.green)), // 초록색으로 수정 Center( child: Text('100', style: Theme.of(context) .textTheme .bodyLarge ?.copyWith( fontSize: 24, fontWeight: FontWeight.bold))) ])), const SizedBox(width: 16), Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('NOW', style: TextStyle(color: Colors.white70, fontSize: 11)), const SizedBox(height: 4), const Text('사용 중', style: TextStyle( color: Colors.green, fontWeight: FontWeight.bold, fontSize: 11)) ]), const Spacer(), Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Solar Panel', style: TextStyle(color: Colors.white70, fontSize: 11)), const SizedBox(height: 4), const Text('전압: 00 전류: 00', style: TextStyle(color: Colors.white, fontSize: 11)) ]) ])) ])); } Widget _buildOverviewCard() { return Container( height: 88.0, padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), decoration: BoxDecoration( color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(5.7)), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('OVERVIEW', style: TextStyle(color: Colors.white70, fontSize: 11)), const SizedBox(height: 2), Expanded( child: Container( decoration: BoxDecoration( color: Theme.of(context).scaffoldBackgroundColor, borderRadius: BorderRadius.circular(5.7)), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildOverviewToggleSwitch('UV LED'), _buildOverviewToggleSwitch('CHARGING'), _buildOverviewToggleSwitch('HELMET'), _buildOverviewToggleSwitch('FAN') ]))) ])); } Widget _buildEnvironmentSensorsCard() { return Container( height: 88.0, padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(5.7)), child: Column(children: [ Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('ENVIRONMENT SENSORS', style: TextStyle(color: Colors.white70, fontSize: 11)), Row(children: [ Text('VIEW HISTORY', style: TextStyle(color: Colors.white70, fontSize: 9)), Icon(Icons.arrow_forward_ios, size: 8, color: Colors.white70) ]) ]), const SizedBox(height: 4), Expanded( child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildSensorInfoRow( Icons.water_drop_outlined, 'HUMID: 60%'), _buildSensorInfoRow(Icons.thermostat, 'TEMP: 24.5℃') ]), const SizedBox(width: 12), const Expanded(child: _LineChartPlaceholder()) ])) ])); } Widget _buildMyLocationCard() { final LatLng exampleLocation = LatLng(37.5665, 126.9780); return Container( height: 174.0, decoration: BoxDecoration( color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(5.7), ), child: ClipRRect( borderRadius: BorderRadius.circular(5.7), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 16, 16, 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('My Location', style: TextStyle(fontSize: 14, color: Colors.white, fontWeight: FontWeight.bold)), SizedBox(height: 4), Text('주소: 금주 남구 효덕로 277', style: TextStyle(fontSize: 12, color: Colors.white70)), ], ), Row( children: [ Text('VIEW MORE', style: TextStyle(color: Colors.white70, fontSize: 12)), Icon(Icons.arrow_forward_ios, size: 10, color: Colors.white70), ], ), ], ), ), const SizedBox(height: 12), Expanded( child: FlutterMap( options: MapOptions( initialCenter: exampleLocation, initialZoom: 15.0, ), children: [ TileLayer( urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', userAgentPackageName: 'com.example.smart_helmet_app', ), MarkerLayer( markers: [ Marker( point: exampleLocation, width: 80, height: 80, child: const Icon(Icons.location_pin, size: 40, color: Colors.red), ), ], ), ], ), ), ], ), ), ); } Widget _buildActivityCard() { return Container( height: 86.0, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(5.7)), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Activity', style: TextStyle(fontSize: 12, color: Colors.white, fontWeight: FontWeight.bold)), const SizedBox(height: 8), Expanded( child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text('08:15 AM - Battery fully Charged', style: TextStyle(fontSize: 10, color: Colors.white70)), Text('9:30 AM - UV LED Actived', style: TextStyle(fontSize: 10, color: Colors.white70)) ])), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text('10:45 AM - Helmet Unlocked', style: TextStyle(fontSize: 10, color: Colors.white70)), Text('11:00 AM - Helmet Off', style: TextStyle(fontSize: 10, color: Colors.white70)) ])) ])) ])); } Widget _buildOverviewToggleSwitch(String title) { return Expanded( child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ Text(title, style: const TextStyle(fontSize: 9, color: Colors.white)), Transform.scale( scale: 0.6, child: Switch.adaptive( value: _overviewToggles[title]!, onChanged: (bool value) { setState(() { _overviewToggles[title] = value; }); }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, activeColor: Colors.white, activeTrackColor: Colors.blueAccent, inactiveThumbColor: Colors.grey.shade400, inactiveTrackColor: Colors.grey.shade700)) ])); } Widget _buildSensorInfoRow(IconData icon, String text) { return Row(children: [ Container( padding: const EdgeInsets.all(4), decoration: BoxDecoration( color: Colors.white.withOpacity(0.1), shape: BoxShape.circle), child: Icon(icon, color: Colors.white, size: 10)), const SizedBox(width: 6), Text(text, style: const TextStyle(fontSize: 9, color: Colors.white)) ]); } } class _LineChartPlaceholder extends StatelessWidget { const _LineChartPlaceholder(); @override Widget build(BuildContext context) { return Column(children: [ Expanded( child: CustomPaint(painter: _LineChartPainter(), size: Size.infinite)), const SizedBox(height: 4), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('24H AGO', style: TextStyle(fontSize: 8, color: Colors.white54)), Text('12H AGO', style: TextStyle(fontSize: 8, color: Colors.white54)), Text('NOW', style: TextStyle(fontSize: 8, color: Colors.white54)) ]) ]); } } class _LineChartPainter extends CustomPainter { @override void paint(ui.Canvas canvas, ui.Size size) { final paint = Paint() ..color = Colors.white.withOpacity(0.8) ..strokeWidth = 1.5 ..style = PaintingStyle.stroke; final path = ui.Path(); path.moveTo(0, size.height * 0.6); path.cubicTo(size.width * 0.1, size.height * 0.8, size.width * 0.2, size.height * 0.4, size.width * 0.3, size.height * 0.6); path.cubicTo(size.width * 0.4, size.height * 0.8, size.width * 0.45, size.height * 0.2, size.width * 0.6, size.height * 0.5); path.cubicTo(size.width * 0.75, size.height * 0.8, size.width * 0.8, size.height * 0.3, size.width, size.height * 0.2); canvas.drawPath(path, paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } }