156 lines
4.8 KiB
Dart
156 lines
4.8 KiB
Dart
|
|
import 'dart:io';
|
|||
|
|
import 'package:flutter/material.dart';
|
|||
|
|
import 'package:get/get.dart';
|
|||
|
|
import 'package:gifunavi/model/entry.dart';
|
|||
|
|
import 'package:gifunavi/pages/gps/gps_controller.dart';
|
|||
|
|
import 'package:gifunavi/pages/history/history_controller.dart';
|
|||
|
|
import 'package:flutter_map/flutter_map.dart';
|
|||
|
|
import 'package:latlong2/latlong.dart';
|
|||
|
|
import 'package:gifunavi/pages/gps/gps_controller.dart';
|
|||
|
|
import 'package:gifunavi/pages/history/history_controller.dart';
|
|||
|
|
import 'package:intl/intl.dart';
|
|||
|
|
|
|||
|
|
class EventResultPage extends StatefulWidget {
|
|||
|
|
final Entry entry;
|
|||
|
|
|
|||
|
|
const EventResultPage({super.key, required this.entry});
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
State<EventResultPage> createState() => _EventResultPageState();
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class _EventResultPageState extends State<EventResultPage> {
|
|||
|
|
late GpsController gpsController;
|
|||
|
|
late HistoryController historyController;
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
void initState() {
|
|||
|
|
super.initState();
|
|||
|
|
gpsController = Get.put(GpsController());
|
|||
|
|
historyController = Get.put(HistoryController());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Widget build(BuildContext context) {
|
|||
|
|
return DefaultTabController(
|
|||
|
|
length: 3,
|
|||
|
|
child: Scaffold(
|
|||
|
|
appBar: AppBar(
|
|||
|
|
title: Text('${widget.entry.event.eventName} 結果'),
|
|||
|
|
bottom: const TabBar(
|
|||
|
|
tabs: [
|
|||
|
|
Tab(text: 'ランキング'),
|
|||
|
|
Tab(text: '走行経路'),
|
|||
|
|
Tab(text: 'チェックポイント'),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
body: TabBarView(
|
|||
|
|
children: [
|
|||
|
|
_buildFutureTab(), //_buildRankingTab(),
|
|||
|
|
_buildFutureTab(), //_buildRouteTab(),
|
|||
|
|
_buildFutureTab(), //_buildCheckpointTab(),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Widget _buildFutureTab() {
|
|||
|
|
// ランキングの表示ロジックを実装
|
|||
|
|
return const Center(child: Text('近日公開予定(当面、HPを参照ください。)'));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
Widget _buildRankingTab() {
|
|||
|
|
// ランキングの表示ロジックを実装
|
|||
|
|
return const Center(child: Text('ランキング表示(実装が必要)'));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Widget _buildRouteTab() {
|
|||
|
|
return Obx(() {
|
|||
|
|
if (gpsController.gpsData.isEmpty) {
|
|||
|
|
return const Center(child: CircularProgressIndicator());
|
|||
|
|
}
|
|||
|
|
return FlutterMap(
|
|||
|
|
options: MapOptions(
|
|||
|
|
center: LatLng(gpsController.gpsData[0].lat, gpsController.gpsData[0].lon),
|
|||
|
|
zoom: 13.0,
|
|||
|
|
),
|
|||
|
|
children: [
|
|||
|
|
TileLayer(
|
|||
|
|
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
|||
|
|
subdomains: ['a', 'b', 'c'],
|
|||
|
|
),
|
|||
|
|
PolylineLayer(
|
|||
|
|
polylines: [
|
|||
|
|
Polyline(
|
|||
|
|
points: gpsController.gpsData
|
|||
|
|
.map((data) => LatLng(data.lat, data.lon))
|
|||
|
|
.toList(),
|
|||
|
|
color: Colors.red,
|
|||
|
|
strokeWidth: 3.0,
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
MarkerLayer(
|
|||
|
|
markers: gpsController.gpsData
|
|||
|
|
.map((data) => Marker(
|
|||
|
|
width: 80.0,
|
|||
|
|
height: 80.0,
|
|||
|
|
point: LatLng(data.lat, data.lon),
|
|||
|
|
child: const Icon(Icons.location_on, color: Colors.red),
|
|||
|
|
))
|
|||
|
|
.toList(),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Widget _buildCheckpointTab() {
|
|||
|
|
return Obx(() {
|
|||
|
|
if (historyController.checkpoints.isEmpty) {
|
|||
|
|
return const Center(child: Text('チェックポイント履歴がありません'));
|
|||
|
|
}
|
|||
|
|
return ListView.builder(
|
|||
|
|
itemCount: historyController.checkpoints.length,
|
|||
|
|
itemBuilder: (context, index) {
|
|||
|
|
final checkpoint = historyController.checkpoints[index];
|
|||
|
|
return ListTile(
|
|||
|
|
title: Text('CP ${checkpoint.cp_number ?? 'Unknown'}'),
|
|||
|
|
subtitle: Column(
|
|||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|||
|
|
children: [
|
|||
|
|
Text('通過時刻: ${_formatDateTime(checkpoint.checkintime)}'),
|
|||
|
|
Text('チーム: ${checkpoint.team_name ?? 'Unknown'}'),
|
|||
|
|
Text('イベント: ${checkpoint.event_code ?? 'Unknown'}'),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
leading: checkpoint.image != null
|
|||
|
|
? Image.file(
|
|||
|
|
File(checkpoint.image!),
|
|||
|
|
width: 50,
|
|||
|
|
height: 50,
|
|||
|
|
fit: BoxFit.cover,
|
|||
|
|
errorBuilder: (context, error, stackTrace) {
|
|||
|
|
print('Error loading image: $error');
|
|||
|
|
return const Icon(Icons.error);
|
|||
|
|
},
|
|||
|
|
)
|
|||
|
|
: const Icon(Icons.image_not_supported),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
String _formatDateTime(int? microsecondsSinceEpoch) {
|
|||
|
|
if (microsecondsSinceEpoch == null) return 'Unknown';
|
|||
|
|
final dateTime = DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch);
|
|||
|
|
return DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime);
|
|||
|
|
}
|