From f66ec03305ab57b9289606542bcb21ff1dc3dac1 Mon Sep 17 00:00:00 2001 From: KIMSEONGMIN <128333586+Brian0KIM@users.noreply.github.com> Date: Fri, 29 Nov 2024 19:10:44 +0900 Subject: [PATCH] Bus Arrival Page --- front/lib/bus_arrival_page.dart | 169 ++++++++++++++++++++++++++++++++ front/lib/bus_info.dart | 27 +++-- front/lib/bus_screen.dart | 9 ++ 3 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 front/lib/bus_arrival_page.dart diff --git a/front/lib/bus_arrival_page.dart b/front/lib/bus_arrival_page.dart new file mode 100644 index 0000000..8213448 --- /dev/null +++ b/front/lib/bus_arrival_page.dart @@ -0,0 +1,169 @@ +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import '../bus_info.dart'; + +class BusArrivalPage extends StatefulWidget { + final String routeNumber; + + const BusArrivalPage({ + super.key, + required this.routeNumber, + }); + + @override + State createState() => _BusArrivalPageState(); +} + +class _BusArrivalPageState extends State { + List busData = []; + bool isLoading = false; + bool isAscending = true; // true: 정문방향(오름차순), false: 사색방향(내림차순) + + @override + void initState() { + super.initState(); + fetchBusData(); + } + + Future fetchBusData() async { + setState(() { + isLoading = true; + }); + + try { + final routeId = busRouteMap[widget.routeNumber]; + final response = await http.get( + Uri.parse('http://localhost:8081/bus/$routeId/eta'), + ); + + if (response.statusCode == 200) { + final data = json.decode(response.body); + if (data['ok']) { + setState(() { + busData = List.from(data['data']) + ..sort((a, b) => isAscending + ? int.parse(a['stationSeq']).compareTo(int.parse(b['stationSeq'])) + : int.parse(b['stationSeq']).compareTo(int.parse(a['stationSeq']))); + }); + } + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('오류 발생: $e')), + ); + } + } finally { + setState(() { + isLoading = false; + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.pop(context), + ), + title: Text('${widget.routeNumber}번 버스'), + ), + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: SegmentedButton( + segments: const [ + ButtonSegment( + value: true, + label: Text("정문 방향"), + ), + ButtonSegment( + value: false, + label: Text("사색 방향"), + ), + ], + selected: {isAscending}, + onSelectionChanged: (Set newSelection) { + setState(() { + isAscending = newSelection.first; + busData.sort((a, b) => isAscending + ? int.parse(a['stationSeq']).compareTo(int.parse(b['stationSeq'])) + : int.parse(b['stationSeq']).compareTo(int.parse(a['stationSeq']))); + }); + }, + style: ButtonStyle( + side: WidgetStateProperty.all( + const BorderSide(color: Colors.blue), + ), + ), + ), + ), + Expanded( + child: isLoading + ? const Center(child: CircularProgressIndicator()) + : busData.isEmpty + ? const Center( + child: Text('운행 중인 버스가 없습니다.'), + ) + : ListView.builder( + itemCount: busData.length, + itemBuilder: (context, index) { + final bus = busData[index]; + final totalStations = int.parse(stationRouteOrder[widget.routeNumber] ?? "0"); + final currentStation = int.parse(bus['stationSeq']); + final remainingStations = totalStations - currentStation; + + return Card( + margin: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0), + side: const BorderSide(color: Colors.blue), + ), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.directions_bus, + color: Colors.blue, + size: 24, + ), + const SizedBox(width: 8), + Text( + "${bus['stationName']}", + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 8), + Text( + '${bus['plateNo']}\n' + '기점에서 ${bus['stationSeq']}째 정류장\n' + '${"종점(사색의광장)"}까지: $remainingStations 전\n' + '잔여: ${bus['remainSeatCnt']} 석', + style: const TextStyle(fontSize: 14), + ), + ], + ), + ), + ); + }, + ), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/front/lib/bus_info.dart b/front/lib/bus_info.dart index 1885403..d573259 100644 --- a/front/lib/bus_info.dart +++ b/front/lib/bus_info.dart @@ -1,12 +1,13 @@ const Map busRouteMap={ - "200000103": "9", - "234000016": "1112", - "200000115": "5100", - "200000112": "7000", - "234001243": "M5107", - "234000884": "1560A", - "228000433": "1560B" + "9": "200000103", + "1112": "234000016", + "5100": "200000115", + "7000": "200000112", + "M5107": "234001243", + "1560A": "234000884", + "1560B": "228000433" }; + const Map stationMap = { "228001174": "사색의광장(정문행)", "228000704": "생명과학대.산업대학(정문행)", @@ -18,7 +19,15 @@ const Map stationMap = { "228000708": "사색의광장(사색행)", "228000706": "경희대차고지(1)", "228000707": "경희대차고지(2)" - //"203000037": "경희대정문(사색행)" +}; +const Map stationRouteOrder = { + "9": "90",//200000103 + "5100": "59",//200000115 + "7000": "79",//200000112 + "M5107": "63",//234001243 + "1560A": "102",//234000884 + "1112": "71",//234000016 + "1560B": "102",//228000433 +}; -}; \ No newline at end of file diff --git a/front/lib/bus_screen.dart b/front/lib/bus_screen.dart index af6c91f..4d282d7 100644 --- a/front/lib/bus_screen.dart +++ b/front/lib/bus_screen.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'bus_timetable_page.dart'; +import 'bus_arrival_page.dart'; class BusScreen extends StatelessWidget { const BusScreen({super.key}); @@ -181,6 +182,14 @@ class BusScreen extends StatelessWidget { ), onPressed: () { // 버스 도착 정보 조회 기능 구현 + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => BusArrivalPage( + routeNumber: routeNumber, + ), + ), + ); }, child: const Text('버스 도착 정보 조회'), ),