Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e37c4ceebd |
@ -1,5 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="com.dvox.gifunavi">
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:name="android.hardware.camera"
|
android:name="android.hardware.camera"
|
||||||
android:required="false" />
|
android:required="false" />
|
||||||
@ -10,16 +11,16 @@
|
|||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||||
<uses-permission android:name="android.permission.CAMERA"/>
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
android:maxSdkVersion="34" />
|
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
|
||||||
<application
|
<application
|
||||||
android:label="岐阜ナビ"
|
android:label="岐阜ナビ"
|
||||||
android:icon="@mipmap/launcher_icon">
|
android:icon="@mipmap/launcher_icon">
|
||||||
<activity
|
<activity
|
||||||
android:name="com.dvox.gifunavi.MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:taskAffinity=""
|
android:taskAffinity=""
|
||||||
@ -27,7 +28,7 @@
|
|||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
tools:ignore="Instantiatable">
|
>
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
while the Flutter UI initializes. After that, this theme continues
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
@ -49,7 +50,8 @@
|
|||||||
<meta-data android:name="com.google.android.geo.API_KEY"
|
<meta-data android:name="com.google.android.geo.API_KEY"
|
||||||
android:value="AIzaSyAUBI1ablMKuJwGj2-kSuEhvYxvB1A-mOE"/>
|
android:value="AIzaSyAUBI1ablMKuJwGj2-kSuEhvYxvB1A-mOE"/>
|
||||||
<service
|
<service
|
||||||
android:name="com.dvox.gifunavi.LocationService"
|
android:name=".LocationService"
|
||||||
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:foregroundServiceType="location" />
|
android:foregroundServiceType="location" />
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
package com.dvox.gifunavi_git
|
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
|
||||||
|
|
||||||
class MainActivity: FlutterActivity()
|
|
||||||
@ -12,8 +12,9 @@ import CoreMotion
|
|||||||
) -> Bool {
|
) -> Bool {
|
||||||
//GeneratedPluginRegistrant.register(with: self)
|
//GeneratedPluginRegistrant.register(with: self)
|
||||||
//return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
//return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
|
|
||||||
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
|
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
|
||||||
let motionChannel = FlutterMethodChannel(name: "com.yourcompany.app/motion",
|
let motionChannel = FlutterMethodChannel(name: "net.sumasen.gifunavi/motion",
|
||||||
binaryMessenger: controller.binaryMessenger)
|
binaryMessenger: controller.binaryMessenger)
|
||||||
motionChannel.setMethodCallHandler({
|
motionChannel.setMethodCallHandler({
|
||||||
[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
|
[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
|
||||||
@ -33,17 +34,58 @@ import CoreMotion
|
|||||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// この実装では以下のことを行っています:
|
||||||
|
// デバイスのモーションデータ(姿勢、重力、ユーザー加速度)を取得します。
|
||||||
|
// 取得したデータを辞書形式にまとめます。
|
||||||
|
// sendMotionDataメソッドを使って、Flutterアプリにデータを送信します。
|
||||||
|
|
||||||
private func startMotionUpdates(result: @escaping FlutterResult) {
|
private func startMotionUpdates(result: @escaping FlutterResult) {
|
||||||
if motionManager.isDeviceMotionAvailable {
|
if motionManager.isDeviceMotionAvailable {
|
||||||
motionManager.deviceMotionUpdateInterval = 0.1
|
motionManager.deviceMotionUpdateInterval = 0.1
|
||||||
motionManager.startDeviceMotionUpdates(to: .main) { (motion, error) in
|
motionManager.startDeviceMotionUpdates(to: .main) { [weak self] (motion, error) in
|
||||||
DispatchQueue.main.async {
|
guard let self = self else { return }
|
||||||
// ここでUIの更新や状態の取得を行う
|
|
||||||
let appState = UIApplication.shared.applicationState
|
if let error = error {
|
||||||
// 必要な処理を行う
|
DispatchQueue.main.async {
|
||||||
}
|
result(FlutterError(code: "MOTION_ERROR",
|
||||||
|
message: error.localizedDescription,
|
||||||
|
details: nil))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let motion = motion else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
result(FlutterError(code: "NO_MOTION_DATA",
|
||||||
|
message: "No motion data available",
|
||||||
|
details: nil))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
let motionData: [String: Any] = [
|
||||||
|
"attitude": [
|
||||||
|
"roll": motion.attitude.roll,
|
||||||
|
"pitch": motion.attitude.pitch,
|
||||||
|
"yaw": motion.attitude.yaw
|
||||||
|
],
|
||||||
|
"gravity": [
|
||||||
|
"x": motion.gravity.x,
|
||||||
|
"y": motion.gravity.y,
|
||||||
|
"z": motion.gravity.z
|
||||||
|
],
|
||||||
|
"userAcceleration": [
|
||||||
|
"x": motion.userAcceleration.x,
|
||||||
|
"y": motion.userAcceleration.y,
|
||||||
|
"z": motion.userAcceleration.z
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
self.sendMotionData(motionData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result(nil)
|
result(nil) // 初期化成功を示す
|
||||||
} else {
|
} else {
|
||||||
result(FlutterError(code: "UNAVAILABLE",
|
result(FlutterError(code: "UNAVAILABLE",
|
||||||
message: "Device motion is not available.",
|
message: "Device motion is not available.",
|
||||||
@ -51,6 +93,12 @@ import CoreMotion
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func sendMotionData(_ data: [String: Any]) {
|
||||||
|
let motionChannel = FlutterMethodChannel(name: "net.sumasen.gifunavi/motion",
|
||||||
|
binaryMessenger: (window?.rootViewController as! FlutterViewController).binaryMessenger)
|
||||||
|
motionChannel.invokeMethod("onMotionData", arguments: data)
|
||||||
|
}
|
||||||
|
|
||||||
private func stopMotionUpdates(result: @escaping FlutterResult) {
|
private func stopMotionUpdates(result: @escaping FlutterResult) {
|
||||||
motionManager.stopDeviceMotionUpdates()
|
motionManager.stopDeviceMotionUpdates()
|
||||||
result(nil)
|
result(nil)
|
||||||
|
|||||||
@ -19,26 +19,37 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="岐阜ナビ" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WPs-nj-CIV">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="岐阜ナビ" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WPs-nj-CIV">
|
||||||
<rect key="frame" x="46" y="123" width="314" height="59"/>
|
<rect key="frame" x="36" y="123.99999999999999" width="321" height="38.333333333333329"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="32"/>
|
<fontDescription key="fontDescription" type="system" pointSize="32"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="NPO 岐阜aiネットワーク" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TM1-SD-6RA">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="NPO 岐阜aiネットワーク" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TM1-SD-6RA">
|
||||||
<rect key="frame" x="46" y="722" width="314" height="41"/>
|
<rect key="frame" x="36" y="717.66666666666663" width="321" height="20.333333333333371"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="hCH-Iu-4S2">
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="hCH-Iu-4S2">
|
||||||
<rect key="frame" x="83" y="314" width="240" height="224"/>
|
<rect key="frame" x="36" y="212.33333333333334" width="321" height="405.33333333333326"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" secondItem="hCH-Iu-4S2" secondAttribute="height" multiplier="270:341" id="jg6-gh-iGg"/>
|
||||||
|
</constraints>
|
||||||
</imageView>
|
</imageView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailingMargin" secondItem="WPs-nj-CIV" secondAttribute="trailing" constant="20" id="1UA-eg-bma"/>
|
||||||
|
<constraint firstAttribute="trailingMargin" secondItem="hCH-Iu-4S2" secondAttribute="trailing" constant="20" id="3lP-Fl-Xzw"/>
|
||||||
|
<constraint firstAttribute="trailingMargin" secondItem="TM1-SD-6RA" secondAttribute="trailing" constant="20" id="E8j-R8-JVy"/>
|
||||||
|
<constraint firstItem="xbc-2k-c8Z" firstAttribute="top" secondItem="TM1-SD-6RA" secondAttribute="bottom" constant="80" id="MKZ-EI-0XJ"/>
|
||||||
|
<constraint firstItem="hCH-Iu-4S2" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leadingMargin" constant="20" id="N5f-Kk-hnZ"/>
|
||||||
|
<constraint firstItem="TM1-SD-6RA" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leadingMargin" constant="20" id="OUN-wm-60h"/>
|
||||||
|
<constraint firstItem="WPs-nj-CIV" firstAttribute="top" secondItem="Ydg-fD-yQy" secondAttribute="bottom" constant="65" id="aPG-JX-zf5"/>
|
||||||
|
<constraint firstItem="hCH-Iu-4S2" firstAttribute="top" secondItem="WPs-nj-CIV" secondAttribute="bottom" constant="50" id="jKu-Hc-2ln"/>
|
||||||
|
<constraint firstItem="WPs-nj-CIV" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leadingMargin" constant="20" id="u9d-2i-Jy1"/>
|
||||||
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
|||||||
@ -1,22 +1,25 @@
|
|||||||
import UIKit
|
|
||||||
import Flutter
|
import Flutter
|
||||||
|
import UIKit
|
||||||
import CoreMotion
|
import CoreMotion
|
||||||
|
|
||||||
@UIApplicationMain
|
@main
|
||||||
@objc class AppDelegate: FlutterAppDelegate {
|
@objc class AppDelegate: FlutterAppDelegate {
|
||||||
private let motionManager = CMMotionManager()
|
private let motionManager = CMMotionManager()
|
||||||
|
|
||||||
override func application(
|
override func application(
|
||||||
_ application: UIApplication,
|
_ application: UIApplication,
|
||||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
|
//GeneratedPluginRegistrant.register(with: self)
|
||||||
|
//return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
|
|
||||||
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
|
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
|
||||||
let motionChannel = FlutterMethodChannel(name: "com.yourcompany.app/motion",
|
let motionChannel = FlutterMethodChannel(name: "net.sumasen.gifunavi/motion",
|
||||||
binaryMessenger: controller.binaryMessenger)
|
binaryMessenger: controller.binaryMessenger)
|
||||||
motionChannel.setMethodCallHandler({
|
motionChannel.setMethodCallHandler({
|
||||||
[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
|
[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
switch call.method {
|
switch call.method {
|
||||||
case "startMotionUpdates":
|
case "startMotionUpdates":
|
||||||
self.startMotionUpdates(result: result)
|
self.startMotionUpdates(result: result)
|
||||||
@ -26,31 +29,78 @@ import CoreMotion
|
|||||||
result(FlutterMethodNotImplemented)
|
result(FlutterMethodNotImplemented)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
GeneratedPluginRegistrant.register(with: self)
|
GeneratedPluginRegistrant.register(with: self)
|
||||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startMotionUpdates(result: @escaping FlutterResult) {
|
// この実装では以下のことを行っています:
|
||||||
if motionManager.isDeviceMotionAvailable {
|
// デバイスのモーションデータ(姿勢、重力、ユーザー加速度)を取得します。
|
||||||
motionManager.deviceMotionUpdateInterval = 0.1
|
// 取得したデータを辞書形式にまとめます。
|
||||||
motionManager.startDeviceMotionUpdates(to: .main) { (motion, error) in
|
// sendMotionDataメソッドを使って、Flutterアプリにデータを送信します。
|
||||||
DispatchQueue.main.async {
|
|
||||||
// ここでUIの更新や状態の取得を行う
|
private func startMotionUpdates(result: @escaping FlutterResult) {
|
||||||
let appState = UIApplication.shared.applicationState
|
if motionManager.isDeviceMotionAvailable {
|
||||||
// 必要な処理を行う
|
motionManager.deviceMotionUpdateInterval = 0.1
|
||||||
|
motionManager.startDeviceMotionUpdates(to: .main) { (motion, error) in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
if let error = error {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
result(FlutterError(code: "MOTION_ERROR",
|
||||||
|
message: error.localizedDescription,
|
||||||
|
details: nil))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let motion = motion else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
result(FlutterError(code: "NO_MOTION_DATA",
|
||||||
|
message: "No motion data available",
|
||||||
|
details: nil))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
let motionData: [String: Any] = [
|
||||||
|
"attitude": [
|
||||||
|
"roll": motion.attitude.roll,
|
||||||
|
"pitch": motion.attitude.pitch,
|
||||||
|
"yaw": motion.attitude.yaw
|
||||||
|
],
|
||||||
|
"gravity": [
|
||||||
|
"x": motion.gravity.x,
|
||||||
|
"y": motion.gravity.y,
|
||||||
|
"z": motion.gravity.z
|
||||||
|
],
|
||||||
|
"userAcceleration": [
|
||||||
|
"x": motion.userAcceleration.x,
|
||||||
|
"y": motion.userAcceleration.y,
|
||||||
|
"z": motion.userAcceleration.z
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
self.sendMotionData(motionData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
result(nil) // 初期化成功を示す
|
||||||
|
} else {
|
||||||
|
result(FlutterError(code: "UNAVAILABLE",
|
||||||
|
message: "Device motion is not available.",
|
||||||
|
details: nil))
|
||||||
}
|
}
|
||||||
result(nil)
|
|
||||||
} else {
|
|
||||||
result(FlutterError(code: "UNAVAILABLE",
|
|
||||||
message: "Device motion is not available.",
|
|
||||||
details: nil))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private func sendMotionData(_ data: [String: Any]) {
|
||||||
private func stopMotionUpdates(result: @escaping FlutterResult) {
|
let motionChannel = FlutterMethodChannel(name: "net.sumasen.gifunavi/motion",
|
||||||
motionManager.stopDeviceMotionUpdates()
|
binaryMessenger: (window?.rootViewController as! FlutterViewController).binaryMessenger)
|
||||||
result(nil)
|
motionChannel.invokeMethod("onMotionData", arguments: data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func stopMotionUpdates(result: @escaping FlutterResult) {
|
||||||
|
motionManager.stopDeviceMotionUpdates()
|
||||||
|
result(nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
251
lib/main.dart
251
lib/main.dart
@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
//import 'dart:convert';
|
//import 'dart:convert';
|
||||||
//import 'dart:developer';
|
//import 'dart:developer';
|
||||||
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:gifunavi/model/gps_data.dart';
|
import 'package:gifunavi/model/gps_data.dart';
|
||||||
//import 'package:gifunavi/pages/home/home_page.dart';
|
//import 'package:gifunavi/pages/home/home_page.dart';
|
||||||
import 'package:gifunavi/utils/database_gps.dart';
|
import 'package:gifunavi/utils/database_gps.dart';
|
||||||
@ -198,6 +199,16 @@ void _showEventSelectionWarning() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// main.dart の上部に追加
|
||||||
|
const bool isDebugMode = true; // リリース時にfalseに変更
|
||||||
|
// 各ファイルで使用
|
||||||
|
void debugLog(String message) {
|
||||||
|
if (isDebugMode) {
|
||||||
|
debugPrint('DEBUG: $message');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
@ -216,10 +227,7 @@ void main() async {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|
||||||
await initServices();
|
await initServices();
|
||||||
|
|
||||||
runApp(const ProviderScope(child: MyApp()));
|
runApp(const ProviderScope(child: MyApp()));
|
||||||
}catch(e, stackTrace){
|
}catch(e, stackTrace){
|
||||||
print('Error during initialization: $e');
|
print('Error during initialization: $e');
|
||||||
@ -230,33 +238,89 @@ void main() async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future <void> _initializeControllers() async {
|
||||||
|
final stopwatch = Stopwatch()..start();
|
||||||
|
const timeout = Duration(seconds: 30); // タイムアウト時間を10秒から30秒に延長
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (!_areAllControllersRegistered()) {
|
||||||
|
if (stopwatch.elapsed > timeout) {
|
||||||
|
throw TimeoutException('Controller initialization timed out');
|
||||||
|
}
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
final LocationController _locationController = Get.find<LocationController>();
|
||||||
|
final IndexController _indexController = Get.find<IndexController>();
|
||||||
|
final DestinationController _destinationController = Get.find<DestinationController>();
|
||||||
|
final PermissionController _permissionController = Get.find<PermissionController>();
|
||||||
|
|
||||||
|
print('All controllers initialized successfully');
|
||||||
|
} catch (e) {
|
||||||
|
print('Error initializing controllers: $e');
|
||||||
|
_handleInitializationError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _areAllControllersRegistered() {
|
||||||
|
return Get.isRegistered<LocationController>() &&
|
||||||
|
Get.isRegistered<IndexController>() &&
|
||||||
|
Get.isRegistered<DestinationController>() &&
|
||||||
|
Get.isRegistered<PermissionController>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleInitializationError() {
|
||||||
|
// エラーハンドリングのロジックをここに実装
|
||||||
|
// 例: エラーダイアログの表示、アプリの再起動など
|
||||||
|
print("_handleInitializationError");
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> initServices() async {
|
Future<void> initServices() async {
|
||||||
print('Starting services ...');
|
print('Starting services ...');
|
||||||
try {
|
try {
|
||||||
//await Get.putAsync(() => ApiService().init());
|
await Get.putAsync(() => ApiService().init());
|
||||||
await _initApiService();
|
//await _initApiService();
|
||||||
debugPrint("1: start ApiService");
|
debugPrint("1: start ApiService");
|
||||||
|
|
||||||
// コントローラーを初期化
|
if (Platform.isIOS ) {
|
||||||
Get.put(IndexController(apiService: Get.find<ApiService>()), permanent: true);
|
// コントローラーを初期化
|
||||||
Get.put(SettingsController(), permanent: true);
|
/*
|
||||||
Get.put(DestinationController(), permanent: true);
|
Get.put(IndexController(apiService: Get.find<ApiService>()), permanent: true);
|
||||||
Get.put(LocationController(), permanent: true);
|
Get.put(SettingsController(), permanent: true);
|
||||||
|
Get.put(DestinationController(), permanent: true);
|
||||||
|
Get.put(LocationController(), permanent: true);
|
||||||
|
*/
|
||||||
|
// すべてのコントローラーとサービスを非同期で初期化
|
||||||
|
Get.lazyPut(() => IndexController(apiService: Get.find<ApiService>()));
|
||||||
|
debugPrint("2: start IndexController");
|
||||||
|
|
||||||
debugPrint("2: Controllers initialized");
|
Get.lazyPut(() => MapController());
|
||||||
/*
|
debugPrint("2: start MapController");
|
||||||
// すべてのコントローラーとサービスを非同期で初期化
|
// その他のコントローラーを遅延初期化
|
||||||
Get.lazyPut(() => IndexController(apiService: Get.find<ApiService>()));
|
Get.lazyPut(() => SettingsController());
|
||||||
debugPrint("2: start IndexController");
|
debugPrint("2: start SettingsController");
|
||||||
|
Get.lazyPut(() => DestinationController());
|
||||||
|
debugPrint("3: start DestinationController");
|
||||||
|
Get.lazyPut(() => LocationController());
|
||||||
|
debugPrint("4: start LocationController");
|
||||||
|
|
||||||
// その他のコントローラーを遅延初期化
|
//await _initializeControllers();
|
||||||
Get.lazyPut(() => SettingsController());
|
|
||||||
debugPrint("2: start SettingsController");
|
debugPrint("2: Controllers initialized");
|
||||||
Get.lazyPut(() => DestinationController());
|
}else {
|
||||||
debugPrint("3: start DestinationController");
|
// すべてのコントローラーとサービスを非同期で初期化
|
||||||
Get.lazyPut(() => LocationController());
|
Get.lazyPut(() => IndexController(apiService: Get.find<ApiService>()));
|
||||||
debugPrint("4: start LocationController");
|
debugPrint("2: start IndexController");
|
||||||
*/
|
|
||||||
|
// その他のコントローラーを遅延初期化
|
||||||
|
Get.lazyPut(() => SettingsController());
|
||||||
|
debugPrint("2: start SettingsController");
|
||||||
|
Get.lazyPut(() => DestinationController());
|
||||||
|
debugPrint("3: start DestinationController");
|
||||||
|
Get.lazyPut(() => LocationController());
|
||||||
|
debugPrint("4: start LocationController");
|
||||||
|
Get.lazyPut(() => PermissionController());
|
||||||
|
}
|
||||||
|
|
||||||
// 非同期処理を並列実行
|
// 非同期処理を並列実行
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
@ -267,10 +331,11 @@ Future<void> initServices() async {
|
|||||||
print('=== 5. Initialized TimeZone...');
|
print('=== 5. Initialized TimeZone...');
|
||||||
print('=== 6. CacheProvider started...');
|
print('=== 6. CacheProvider started...');
|
||||||
|
|
||||||
Get.put(PermissionController());
|
|
||||||
await _checkPermissions();
|
await _checkPermissions();
|
||||||
debugPrint("7: start PermissionController");
|
debugPrint("7: start PermissionController");
|
||||||
|
|
||||||
|
//await PermissionController.checkAndRequestPermissions();
|
||||||
|
|
||||||
}catch(e){
|
}catch(e){
|
||||||
print('Error initializing : $e');
|
print('Error initializing : $e');
|
||||||
}
|
}
|
||||||
@ -294,7 +359,17 @@ Future<void> _initCacheProvider() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _checkPermissions() async {
|
Future<void> _checkPermissions() async {
|
||||||
await PermissionController.checkAndRequestPermissions();
|
int attempts = 0;
|
||||||
|
while (Get.context == null && attempts < 10) {
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
attempts++;
|
||||||
|
}
|
||||||
|
if (Get.context != null) {
|
||||||
|
await PermissionController.checkAndRequestPermissions();
|
||||||
|
} else {
|
||||||
|
print('Context is still null, cannot check permissions');
|
||||||
|
}
|
||||||
|
//await PermissionController.checkAndRequestPermissions(); // main._checkPermissions
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _initApiService() async {
|
Future<void> _initApiService() async {
|
||||||
@ -474,7 +549,9 @@ Future<void> startBackgroundTracking() async {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 位置情報の権限が許可されているかを確認
|
// 位置情報の権限が許可されているかを確認
|
||||||
await PermissionController.checkAndRequestPermissions();
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
PermissionController.checkAndRequestPermissions();
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error starting background tracking: $e');
|
print('Error starting background tracking: $e');
|
||||||
}
|
}
|
||||||
@ -569,10 +646,29 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
|
|
||||||
Future <void> _initializeControllers() async {
|
Future <void> _initializeControllers() async {
|
||||||
|
|
||||||
|
while (!Get.isRegistered<LocationController>() ) {
|
||||||
|
print("LocationController is not up... ");
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
}
|
||||||
|
while (!Get.isRegistered<IndexController>() ) {
|
||||||
|
print("IndexController is not up...");
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
}
|
||||||
|
while (!Get.isRegistered<DestinationController>() ) {
|
||||||
|
print("DestinationController is not up...");
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
while (!Get.isRegistered<PermissionController>() ) {
|
||||||
|
print("PermissionController is not up... ");
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
while (!Get.isRegistered<LocationController>() ||
|
while (!Get.isRegistered<LocationController>() ||
|
||||||
!Get.isRegistered<IndexController>() ||
|
!Get.isRegistered<IndexController>() ||
|
||||||
!Get.isRegistered<DestinationController>() ||
|
!Get.isRegistered<DestinationController>() ) {
|
||||||
!Get.isRegistered<PermissionController>()) {
|
print("LocationController status = Get.isRegistered<LocationController>() ");
|
||||||
await Future.delayed(const Duration(milliseconds: 100));
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -580,7 +676,7 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
_locationController = Get.find<LocationController>();
|
_locationController = Get.find<LocationController>();
|
||||||
_indexController = Get.find<IndexController>();
|
_indexController = Get.find<IndexController>();
|
||||||
_destinationController = Get.find<DestinationController>();
|
_destinationController = Get.find<DestinationController>();
|
||||||
_permissionController = Get.find<PermissionController>();
|
//_permissionController = Get.find<PermissionController>();
|
||||||
_isControllerInitialized = true;
|
_isControllerInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -785,15 +881,7 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
@override
|
@override
|
||||||
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
|
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
|
||||||
try {
|
try {
|
||||||
if (!Get.isRegistered<IndexController>()) {
|
await _initializeControllers();
|
||||||
_indexController = Get.find<IndexController>();
|
|
||||||
}
|
|
||||||
if (!Get.isRegistered<LocationController>()) {
|
|
||||||
_locationController = Get.find<LocationController>();
|
|
||||||
}
|
|
||||||
if (!Get.isRegistered<DestinationController>()) {
|
|
||||||
_destinationController = Get.find<DestinationController>();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case AppLifecycleState.resumed:
|
case AppLifecycleState.resumed:
|
||||||
@ -835,6 +923,7 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onResumed() async {
|
Future<void> _onResumed() async {
|
||||||
|
|
||||||
debugPrint("==(Status Changed)==> RESUMED");
|
debugPrint("==(Status Changed)==> RESUMED");
|
||||||
try {
|
try {
|
||||||
if (!_isControllerInitialized) {
|
if (!_isControllerInitialized) {
|
||||||
@ -865,43 +954,76 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onInactive() async {
|
Future<void> _onInactive() async {
|
||||||
debugPrint("==(Status Changed)==> INACTIVE");
|
try {
|
||||||
if (Platform.isIOS && !_destinationController.isRunningBackgroundGPS) {
|
debugPrint("==(Status Changed)==> INACTIVE");
|
||||||
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
|
if (!_isControllerInitialized) {
|
||||||
_locationController.stopPositionStream();
|
await _initializeControllers();
|
||||||
_destinationController.isRunningBackgroundGPS = true;
|
}
|
||||||
await startBackgroundTracking();
|
if (Platform.isIOS && !_destinationController.isRunningBackgroundGPS) {
|
||||||
} else if (Platform.isAndroid && !_destinationController.isRunningBackgroundGPS) {
|
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
|
||||||
// Android特有の処理があれば追加
|
_locationController.stopPositionStream();
|
||||||
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
|
_destinationController.isRunningBackgroundGPS = true;
|
||||||
}else{
|
await startBackgroundTracking();
|
||||||
debugPrint("==(Status Changed)==> INACTIVE 不明状態");
|
} else if (Platform.isAndroid &&
|
||||||
|
!_destinationController.isRunningBackgroundGPS) {
|
||||||
|
// Android特有の処理があれば追加
|
||||||
|
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
|
||||||
|
} else {
|
||||||
|
debugPrint("==(Status Changed)==> INACTIVE 不明状態");
|
||||||
|
}
|
||||||
|
await saveGameState();
|
||||||
|
} catch (e) {
|
||||||
|
print('Error in _onInactive: $e');
|
||||||
}
|
}
|
||||||
await saveGameState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onPaused() async {
|
Future<void> _onPaused() async {
|
||||||
debugPrint(" ==(Status Changed)==> PAUSED. バックグラウンド処理。");
|
try {
|
||||||
if (Platform.isAndroid && !_destinationController.isRunningBackgroundGPS) {
|
debugPrint(" ==(Status Changed)==> PAUSED. バックグラウンド処理。");
|
||||||
debugPrint(" ==(Status Changed)==> PAUSED. Android バックグラウンド処理。");
|
if (!_isControllerInitialized) {
|
||||||
_locationController.stopPositionStream();
|
await _initializeControllers();
|
||||||
const platform = MethodChannel('location');
|
}
|
||||||
await platform.invokeMethod('startLocationService');
|
if (Platform.isAndroid &&
|
||||||
_destinationController.isRunningBackgroundGPS = true;
|
!_destinationController.isRunningBackgroundGPS) {
|
||||||
|
debugPrint(
|
||||||
|
" ==(Status Changed)==> PAUSED. Android バックグラウンド処理。");
|
||||||
|
_locationController.stopPositionStream();
|
||||||
|
const platform = MethodChannel('location');
|
||||||
|
await platform.invokeMethod('startLocationService');
|
||||||
|
_destinationController.isRunningBackgroundGPS = true;
|
||||||
|
}
|
||||||
|
await saveGameState();
|
||||||
|
} catch (e) {
|
||||||
|
print('Error in _onPaused: $e');
|
||||||
}
|
}
|
||||||
await saveGameState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onDetached() async {
|
Future<void> _onDetached() async {
|
||||||
debugPrint(" ==(Status Changed)==> DETACHED アプリは終了します。");
|
try {
|
||||||
await saveGameState();
|
if (!_isControllerInitialized) {
|
||||||
// アプリ終了時の追加処理
|
await _initializeControllers();
|
||||||
|
}
|
||||||
|
debugPrint(" ==(Status Changed)==> DETACHED アプリは終了します。");
|
||||||
|
await saveGameState();
|
||||||
|
// アプリ終了時の追加処理
|
||||||
|
} catch (e) {
|
||||||
|
print('Error in _onDetached: $e');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<void> _onHidden() async {
|
Future<void> _onHidden() async {
|
||||||
debugPrint(" ==(Status Changed)==> Hidden アプリが隠れた");
|
try {
|
||||||
await saveGameState();
|
if (!_isControllerInitialized) {
|
||||||
|
await _initializeControllers();
|
||||||
|
}
|
||||||
|
debugPrint(" ==(Status Changed)==> Hidden アプリが隠れた");
|
||||||
|
await saveGameState();
|
||||||
|
} catch (e) {
|
||||||
|
print('Error in _onHidden: $e');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -929,7 +1051,4 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
enableLog: true,
|
enableLog: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,8 @@ class Entry {
|
|||||||
final DateTime? date;
|
final DateTime? date;
|
||||||
final int zekkenNumber; // 新しく追加
|
final int zekkenNumber; // 新しく追加
|
||||||
final String owner;
|
final String owner;
|
||||||
|
bool hasParticipated;
|
||||||
|
bool hasGoaled;
|
||||||
|
|
||||||
Entry({
|
Entry({
|
||||||
required this.id,
|
required this.id,
|
||||||
@ -20,6 +22,8 @@ class Entry {
|
|||||||
required this.date,
|
required this.date,
|
||||||
required this.zekkenNumber,
|
required this.zekkenNumber,
|
||||||
required this.owner,
|
required this.owner,
|
||||||
|
this.hasParticipated = false,
|
||||||
|
this.hasGoaled = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Entry.fromJson(Map<String, dynamic> json) {
|
factory Entry.fromJson(Map<String, dynamic> json) {
|
||||||
|
|||||||
@ -179,7 +179,7 @@ class _AuthPageState extends ConsumerState<AuthPage> {
|
|||||||
Widget buildControlls() {
|
Widget buildControlls() {
|
||||||
if (_isLoginProgress) {
|
if (_isLoginProgress) {
|
||||||
return const Center(
|
return const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(), // Auth_page
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -418,7 +418,7 @@ class CameraPage extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
child: Text("finish_goal".tr))
|
child: Text("finish_goal".tr))
|
||||||
: const Center(
|
: const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(), // Camera page
|
||||||
)
|
)
|
||||||
: Container())
|
: Container())
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1126,7 +1126,7 @@ class DestinationController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> _saveImageToGallery(String imagePath) async {
|
Future<String?> _saveImageToGallery(String imagePath) async {
|
||||||
final status = await PermissionController.checkStoragePermission();
|
final status = await PermissionController.checkStoragePermission(); // destinationController._saveImageToGallery
|
||||||
if(!status){
|
if(!status){
|
||||||
await PermissionController.requestStoragePermission();
|
await PermissionController.requestStoragePermission();
|
||||||
}
|
}
|
||||||
@ -1333,12 +1333,6 @@ class DestinationController extends GetxController {
|
|||||||
void onInit() async {
|
void onInit() async {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
|
||||||
await PermissionController.checkAndRequestPermissions();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
startGPSCheckTimer();
|
startGPSCheckTimer();
|
||||||
|
|
||||||
// MapControllerの初期化完了を待機するフラグを設定
|
// MapControllerの初期化完了を待機するフラグを設定
|
||||||
@ -1645,6 +1639,7 @@ class DestinationController extends GetxController {
|
|||||||
final token = indexController.userToken;
|
final token = indexController.userToken;
|
||||||
if (token != null && token.isNotEmpty) {
|
if (token != null && token.isNotEmpty) {
|
||||||
await indexController.loadUserDetailsForToken(token);
|
await indexController.loadUserDetailsForToken(token);
|
||||||
|
await Future.delayed(Duration(milliseconds: 500)); // 短い遅延を追加
|
||||||
fixMapBound(token);
|
fixMapBound(token);
|
||||||
}else {
|
}else {
|
||||||
Get.toNamed(AppPages.LOGIN)!.then((value) {
|
Get.toNamed(AppPages.LOGIN)!.then((value) {
|
||||||
@ -1665,6 +1660,9 @@ class DestinationController extends GetxController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MapControllerの準備が整うまで待機
|
||||||
|
await indexController.waitForMapControllerReady();
|
||||||
|
|
||||||
// 地図のイベントリスナーを設定
|
// 地図のイベントリスナーを設定
|
||||||
indexController.mapController.mapEventStream.listen((MapEvent mapEvent) {
|
indexController.mapController.mapEventStream.listen((MapEvent mapEvent) {
|
||||||
if (mapEvent is MapEventMoveEnd) {
|
if (mapEvent is MapEventMoveEnd) {
|
||||||
@ -1677,11 +1675,12 @@ class DestinationController extends GetxController {
|
|||||||
|
|
||||||
// 地図の境界を修正する関数です。
|
// 地図の境界を修正する関数です。
|
||||||
//
|
//
|
||||||
void fixMapBound(String token) {
|
Future<void> fixMapBound(String token) async {
|
||||||
|
await indexController.waitForMapControllerReady();
|
||||||
//String _token = indexController.currentUser[0]["token"];
|
//String _token = indexController.currentUser[0]["token"];
|
||||||
indexController.switchPage(AppPages.INDEX);
|
indexController.switchPage(AppPages.INDEX);
|
||||||
|
|
||||||
if (isMapControllerReady) {
|
if (indexController.isMapControllerReady.value) {
|
||||||
LocationService.getLocationsExt(token).then((value) {
|
LocationService.getLocationsExt(token).then((value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
//print("--- loc ext is - $value ----");
|
//print("--- loc ext is - $value ----");
|
||||||
@ -1743,11 +1742,18 @@ class DestinationController extends GetxController {
|
|||||||
// 地図を現在位置に中央揃えする関数です。
|
// 地図を現在位置に中央揃えする関数です。
|
||||||
//
|
//
|
||||||
void centerMapToCurrentLocation() {
|
void centerMapToCurrentLocation() {
|
||||||
|
final indexController = Get.find<IndexController>();
|
||||||
|
if (indexController.isMapControllerReady.value && indexController.isMapControllerReady.value) {
|
||||||
|
// ... 現在位置へのセンタリングのロジック ...
|
||||||
|
indexController.mapController.move(LatLng(currentLat, currentLon), 17.0);
|
||||||
|
} else {
|
||||||
|
debugPrint('Map controller is not ready for centering');
|
||||||
|
}
|
||||||
//print("center is ${currentLat}, ${currentLon}");
|
//print("center is ${currentLat}, ${currentLon}");
|
||||||
// Akira ... 状況によって呼ぶか呼ばないか
|
// Akira ... 状況によって呼ぶか呼ばないか
|
||||||
if (currentLat != 0 || currentLon != 0) {
|
//if (currentLat != 0 || currentLon != 0) {
|
||||||
indexController.mapController.move(LatLng(currentLat, currentLon), 17.0);
|
//indexController.mapController.move(LatLng(currentLat, currentLon), 17.0);
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 接続状態が変更されたときに呼び出される関数です。
|
// 接続状態が変更されたときに呼び出される関数です。
|
||||||
@ -1920,7 +1926,7 @@ class DestinationController extends GetxController {
|
|||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return const Center(
|
return const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(), // Destination COntroller
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,8 @@ class EntryController extends GetxController {
|
|||||||
final currentEntry = Rx<Entry?>(null);
|
final currentEntry = Rx<Entry?>(null);
|
||||||
final isLoading = true.obs;
|
final isLoading = true.obs;
|
||||||
|
|
||||||
|
final activeEvents = <Event>[].obs; //有効なイベントリスト
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() async {
|
void onInit() async {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
@ -52,14 +54,15 @@ class EntryController extends GetxController {
|
|||||||
fetchTeams(),
|
fetchTeams(),
|
||||||
fetchCategories(),
|
fetchCategories(),
|
||||||
]);
|
]);
|
||||||
|
updateActiveEvents(); // イベント取得後にアクティブなイベントを更新
|
||||||
if (Get.arguments != null && Get.arguments['entry'] != null) {
|
if (Get.arguments != null && Get.arguments['entry'] != null) {
|
||||||
currentEntry.value = Get.arguments['entry'];
|
currentEntry.value = Get.arguments['entry'];
|
||||||
initializeEditMode(currentEntry.value!);
|
initializeEditMode(currentEntry.value!);
|
||||||
} else {
|
} else {
|
||||||
// 新規作成モードの場合、最初のイベントを選択
|
// 新規作成モードの場合、最初のイベントを選択
|
||||||
if (events.isNotEmpty) {
|
if (events.isNotEmpty) {
|
||||||
selectedEvent.value = events.first;
|
selectedEvent.value = activeEvents.first;
|
||||||
selectedDate.value = events.first.startDatetime;
|
selectedDate.value = activeEvents.first.startDatetime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
@ -70,6 +73,10 @@ class EntryController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateActiveEvents() {
|
||||||
|
final now = tz.TZDateTime.now(tz.getLocation('Asia/Tokyo'));
|
||||||
|
activeEvents.assignAll(events.where((event) => event.deadlineDateTime.isAfter(now)));
|
||||||
|
}
|
||||||
|
|
||||||
void initializeEditMode(Entry entry) {
|
void initializeEditMode(Entry entry) {
|
||||||
currentEntry.value = entry;
|
currentEntry.value = entry;
|
||||||
@ -143,6 +150,7 @@ class EntryController extends GetxController {
|
|||||||
deadlineDateTime: deadlineDateTime,
|
deadlineDateTime: deadlineDateTime,
|
||||||
);
|
);
|
||||||
}).toList());
|
}).toList());
|
||||||
|
updateActiveEvents();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error fetching events: $e');
|
print('Error fetching events: $e');
|
||||||
Get.snackbar('Error', 'Failed to fetch events');
|
Get.snackbar('Error', 'Failed to fetch events');
|
||||||
|
|||||||
@ -39,10 +39,10 @@ class EntryDetailPage extends GetView<EntryController> {
|
|||||||
children: [
|
children: [
|
||||||
_buildDropdown<Event>(
|
_buildDropdown<Event>(
|
||||||
label: 'イベント',
|
label: 'イベント',
|
||||||
items: controller.events,
|
items: controller.activeEvents,
|
||||||
selectedId: controller.selectedEvent.value?.id,
|
selectedId: controller.selectedEvent.value?.id,
|
||||||
onChanged: (eventId) => controller.updateEvent(
|
onChanged: (eventId) => controller.updateEvent(
|
||||||
controller.events.firstWhere((e) => e.id == eventId)
|
controller.activeEvents.firstWhere((e) => e.id == eventId)
|
||||||
),
|
),
|
||||||
getDisplayName: (event) => event.eventName,
|
getDisplayName: (event) => event.eventName,
|
||||||
getId: (event) => event.id,
|
getId: (event) => event.id,
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import 'package:gifunavi/pages/entry/entry_controller.dart';
|
|||||||
import 'package:gifunavi/routes/app_pages.dart';
|
import 'package:gifunavi/routes/app_pages.dart';
|
||||||
import 'package:timezone/timezone.dart' as tz;
|
import 'package:timezone/timezone.dart' as tz;
|
||||||
|
|
||||||
|
import '../../model/entry.dart';
|
||||||
|
|
||||||
class EntryListPage extends GetView<EntryController> {
|
class EntryListPage extends GetView<EntryController> {
|
||||||
const EntryListPage({super.key});
|
const EntryListPage({super.key});
|
||||||
|
|
||||||
@ -28,11 +30,35 @@ class EntryListPage extends GetView<EntryController> {
|
|||||||
child: Text('表示するエントリーがありません。'),
|
child: Text('表示するエントリーがありません。'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final sortedEntries = controller.entries.toList()
|
||||||
|
..sort((b, a) => a.date!.compareTo(b.date!));
|
||||||
|
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
itemCount: controller.entries.length,
|
itemCount: sortedEntries.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final entry = controller.entries[index];
|
final entry = sortedEntries[index];
|
||||||
|
final now = DateTime.now();
|
||||||
|
final isEntryInFuture = _compareDatesOnly(entry.date!, now) >= 0;
|
||||||
|
|
||||||
|
//final isEntryInFuture = entry.date!.isAfter(now) || entry.date!.isAtSameMomentAs(now);
|
||||||
|
|
||||||
|
Widget? leadingIcon;
|
||||||
|
if (!isEntryInFuture) {
|
||||||
|
if (entry.hasParticipated) {
|
||||||
|
if (entry.hasGoaled) {
|
||||||
|
leadingIcon =
|
||||||
|
const Icon(Icons.check_circle, color: Colors.green);
|
||||||
|
} else {
|
||||||
|
leadingIcon = const Icon(Icons.warning, color: Colors.yellow);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
leadingIcon = const Icon(Icons.cancel, color: Colors.red);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
|
leading: leadingIcon,
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -49,9 +75,16 @@ class EntryListPage extends GetView<EntryController> {
|
|||||||
Text('ゼッケン: ${entry.zekkenNumber ?? "未設定"}'),
|
Text('ゼッケン: ${entry.zekkenNumber ?? "未設定"}'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () =>
|
onTap: () {
|
||||||
|
if (isEntryInFuture) {
|
||||||
Get.toNamed(AppPages.ENTRY_DETAIL,
|
Get.toNamed(AppPages.ENTRY_DETAIL,
|
||||||
arguments: {'mode': 'edit', 'entry': entry}),
|
arguments: {'mode': 'edit', 'entry': entry});
|
||||||
|
} else if (entry.hasParticipated) {
|
||||||
|
Get.toNamed(AppPages.EVENT_RESULT, arguments: {'entry': entry});
|
||||||
|
} else {
|
||||||
|
_showNonParticipationDialog(context, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -59,6 +92,11 @@ class EntryListPage extends GetView<EntryController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新しく追加するメソッド
|
||||||
|
int _compareDatesOnly(DateTime a, DateTime b) {
|
||||||
|
return DateTime(a.year, a.month, a.day).compareTo(DateTime(b.year, b.month, b.day));
|
||||||
|
}
|
||||||
|
|
||||||
String _formatDate(DateTime? date) {
|
String _formatDate(DateTime? date) {
|
||||||
if (date == null) {
|
if (date == null) {
|
||||||
return '日時未設定';
|
return '日時未設定';
|
||||||
@ -66,6 +104,26 @@ class EntryListPage extends GetView<EntryController> {
|
|||||||
final jstDate = tz.TZDateTime.from(date, tz.getLocation('Asia/Tokyo'));
|
final jstDate = tz.TZDateTime.from(date, tz.getLocation('Asia/Tokyo'));
|
||||||
return DateFormat('yyyy-MM-dd').format(jstDate);
|
return DateFormat('yyyy-MM-dd').format(jstDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showNonParticipationDialog(BuildContext context, Entry entry) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(entry.event.eventName),
|
||||||
|
content: Text('${_formatDate(entry.date)}\n\n不参加でした'),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: const Text('閉じる'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EntryListPage_old extends GetView<EntryController> {
|
class EntryListPage_old extends GetView<EntryController> {
|
||||||
@ -85,7 +143,7 @@ class EntryListPage_old extends GetView<EntryController> {
|
|||||||
),
|
),
|
||||||
body: Obx((){
|
body: Obx((){
|
||||||
if (controller.isLoading.value) {
|
if (controller.isLoading.value) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator()); // EntryList
|
||||||
}
|
}
|
||||||
|
|
||||||
// エントリーを日付昇順にソート
|
// エントリーを日付昇順にソート
|
||||||
|
|||||||
155
lib/pages/entry/event_result_page.dart
Normal file
155
lib/pages/entry/event_result_page.dart
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
22
lib/pages/gps/gps_controller.dart
Normal file
22
lib/pages/gps/gps_controller.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:gifunavi/model/gps_data.dart';
|
||||||
|
import 'package:gifunavi/utils/database_gps.dart';
|
||||||
|
import 'package:gifunavi/pages/index/index_controller.dart';
|
||||||
|
|
||||||
|
class GpsController extends GetxController {
|
||||||
|
final gpsData = <GpsData>[].obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
loadGpsData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadGpsData() async {
|
||||||
|
final teamName = Get.find<IndexController>().currentUser[0]["user"]['team_name'];
|
||||||
|
final eventCode = Get.find<IndexController>().currentUser[0]["user"]["event_code"];
|
||||||
|
GpsDatabaseHelper db = GpsDatabaseHelper.instance;
|
||||||
|
var data = await db.getGPSData(teamName, eventCode);
|
||||||
|
gpsData.value = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
lib/pages/history/history_controller.dart
Normal file
22
lib/pages/history/history_controller.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:gifunavi/model/rog.dart';
|
||||||
|
import 'package:gifunavi/utils/database_helper.dart';
|
||||||
|
|
||||||
|
class HistoryController extends GetxController {
|
||||||
|
final checkpoints = <Rog>[].obs; // Rog オブジェクトのリストに変更
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
loadCheckpoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadCheckpoints() async {
|
||||||
|
DatabaseHelper db = DatabaseHelper.instance;
|
||||||
|
var data = await db.allRogianing();
|
||||||
|
checkpoints.value = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -27,7 +27,7 @@ class _HistoryPageState extends State<HistoryPage> {
|
|||||||
future: db.getDestinations(),
|
future: db.getDestinations(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator()); //History page
|
||||||
} else if (snapshot.hasError) {
|
} else if (snapshot.hasError) {
|
||||||
return Center(child: Text('Error: ${snapshot.error}'));
|
return Center(child: Text('Error: ${snapshot.error}'));
|
||||||
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||||
@ -167,7 +167,7 @@ class CustomWidget extends StatelessWidget {
|
|||||||
print('Error loading image path: ${snapshot.error}');
|
print('Error loading image path: ${snapshot.error}');
|
||||||
return const Icon(Icons.error);
|
return const Icon(Icons.error);
|
||||||
} else {
|
} else {
|
||||||
return const CircularProgressIndicator();
|
return const CircularProgressIndicator(); // History page 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -8,7 +9,6 @@ import 'package:flutter_map/flutter_map.dart';
|
|||||||
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
|
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
|
||||||
import 'package:geojson_vi/geojson_vi.dart';
|
import 'package:geojson_vi/geojson_vi.dart';
|
||||||
import 'package:geolocator/geolocator.dart';
|
import 'package:geolocator/geolocator.dart';
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
import 'package:gifunavi/model/destination.dart';
|
import 'package:gifunavi/model/destination.dart';
|
||||||
import 'package:gifunavi/model/entry.dart';
|
import 'package:gifunavi/model/entry.dart';
|
||||||
@ -34,6 +34,8 @@ import 'package:timezone/data/latest.dart' as tz;
|
|||||||
|
|
||||||
import '../permission/permission.dart';
|
import '../permission/permission.dart';
|
||||||
|
|
||||||
|
//enum MapState { initializing, ready, error }
|
||||||
|
|
||||||
class IndexController extends GetxController with WidgetsBindingObserver {
|
class IndexController extends GetxController with WidgetsBindingObserver {
|
||||||
List<GeoJSONFeatureCollection> locations = <GeoJSONFeatureCollection>[].obs;
|
List<GeoJSONFeatureCollection> locations = <GeoJSONFeatureCollection>[].obs;
|
||||||
List<GeoJSONFeature> currentFeature = <GeoJSONFeature>[].obs;
|
List<GeoJSONFeature> currentFeature = <GeoJSONFeature>[].obs;
|
||||||
@ -60,10 +62,16 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
|
|
||||||
var isCustomAreaSelected = false.obs;
|
var isCustomAreaSelected = false.obs;
|
||||||
|
|
||||||
RxBool isMapControllerReady = RxBool(false); // MapControllerの初期化状態を管理するフラグ
|
|
||||||
//final mapControllerReadyStream = Stream<bool>.value(false); // MapControllerの初期化状態を通知するためのストリーム
|
//final mapControllerReadyStream = Stream<bool>.value(false); // MapControllerの初期化状態を通知するためのストリーム
|
||||||
|
|
||||||
MapController mapController = MapController();
|
// 複数の状態変数(_isLoading, _isMapInitialized, indexController.isMapControllerReady)を一元管理します。
|
||||||
|
//final Rx<MapState> mapState = MapState.initializing.obs;
|
||||||
|
|
||||||
|
final Completer<void> mapControllerCompleter = Completer<void>();
|
||||||
|
late MapController mapController; // = Rx<MapController?>(null);
|
||||||
|
final RxBool isMapControllerReady = false.obs; // MapControllerの初期化状態を管理するフラグ
|
||||||
|
|
||||||
|
//MapController mapController = MapController();
|
||||||
MapController rogMapController = MapController();
|
MapController rogMapController = MapController();
|
||||||
|
|
||||||
LogManager logManager = LogManager();
|
LogManager logManager = LogManager();
|
||||||
@ -96,7 +104,43 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
|
|
||||||
final selectedEventName = 'add_location'.tr.obs;
|
final selectedEventName = 'add_location'.tr.obs;
|
||||||
|
|
||||||
RxBool isLoadingLocations = true.obs;
|
RxBool isLoadingLocations = false.obs;
|
||||||
|
|
||||||
|
// ユーザーの参加状況を追跡する
|
||||||
|
final RxBool isUserParticipating = false.obs;
|
||||||
|
|
||||||
|
// ユーザーのゴール到達状況を追跡する
|
||||||
|
final RxBool hasUserReachedGoal = false.obs;
|
||||||
|
|
||||||
|
void initMapController() {
|
||||||
|
mapController = MapController();
|
||||||
|
|
||||||
|
// MapEventMoveEndイベントを使用して初期化完了を検出
|
||||||
|
mapController.mapEventStream.listen((event) {
|
||||||
|
if (event is MapEventMoveEnd && !isMapControllerReady.value) {
|
||||||
|
// MapEventMoveEndイベントが発生したら、MapControllerの準備完了とみなす
|
||||||
|
if (!isMapControllerReady.value) {
|
||||||
|
print('MapController is ready');
|
||||||
|
isMapControllerReady.value = true;
|
||||||
|
if (!mapControllerCompleter.isCompleted) {
|
||||||
|
mapControllerCompleter.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ユーザーの参加状況を更新するメソッド
|
||||||
|
void updateUserParticipationStatus(bool isParticipating) {
|
||||||
|
isUserParticipating.value = isParticipating;
|
||||||
|
update(); // GetX の update() メソッドを呼び出してUIを更新
|
||||||
|
}
|
||||||
|
|
||||||
|
// ユーザーのゴール到達状況を更新するメソッド
|
||||||
|
void updateUserGoalStatus(bool hasReachedGoal) {
|
||||||
|
hasUserReachedGoal.value = hasReachedGoal;
|
||||||
|
update(); // GetX の update() メソッドを呼び出してUIを更新
|
||||||
|
}
|
||||||
|
|
||||||
void setSelectedEventName(String eventName) {
|
void setSelectedEventName(String eventName) {
|
||||||
selectedEventName.value = eventName;
|
selectedEventName.value = eventName;
|
||||||
@ -242,13 +286,14 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
void onInit() {
|
void onInit() {
|
||||||
try {
|
try {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
//mapController = MapController();
|
||||||
|
//ever(isMapControllerReady, (_) => _onMapControllerReady());
|
||||||
|
|
||||||
|
initMapController();
|
||||||
|
|
||||||
initConnectivity();
|
initConnectivity();
|
||||||
_connectivitySubscription = _connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
|
_connectivitySubscription = _connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
|
||||||
await PermissionController.checkAndRequestPermissions();
|
|
||||||
});
|
|
||||||
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
_startLocationService(); // アプリ起動時にLocationServiceを開始する
|
_startLocationService(); // アプリ起動時にLocationServiceを開始する
|
||||||
|
|
||||||
@ -259,7 +304,7 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
tz.initializeTimeZones();
|
tz.initializeTimeZones();
|
||||||
//teamController = Get.find<TeamController>();
|
//teamController = Get.find<TeamController>();
|
||||||
|
|
||||||
loadLocations();
|
//loadLocations();
|
||||||
|
|
||||||
}catch(e,stacktrace){
|
}catch(e,stacktrace){
|
||||||
print('Error in IndexController.onInit: $e');
|
print('Error in IndexController.onInit: $e');
|
||||||
@ -270,18 +315,33 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onMapControllerReady() {
|
||||||
|
if (isMapControllerReady.value) {
|
||||||
|
loadLocations();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> loadLocations() async {
|
Future<void> loadLocations() async {
|
||||||
|
if (isLoadingLocations.value) return;
|
||||||
|
|
||||||
isLoadingLocations.value = true;
|
isLoadingLocations.value = true;
|
||||||
try {
|
try {
|
||||||
await waitForMapControllerReady();
|
debugPrint('IndexController: Starting to load locations');
|
||||||
|
|
||||||
|
await waitForMapControllerReady(); // loadLocations
|
||||||
|
debugPrint('IndexController: Permission granted, loading locations');
|
||||||
String eventCode = currentUser.isNotEmpty ? currentUser[0]["user"]["event_code"] ?? "" : "";
|
String eventCode = currentUser.isNotEmpty ? currentUser[0]["user"]["event_code"] ?? "" : "";
|
||||||
await loadLocationsBound(eventCode);
|
await loadLocationsBound(eventCode);
|
||||||
|
|
||||||
|
debugPrint('IndexController: Locations loaded successfully');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error loading locations: $e');
|
print('Error loading locations: $e');
|
||||||
// エラーハンドリングを追加(例:スナックバーでユーザーに通知)
|
// エラーハンドリングを追加(例:スナックバーでユーザーに通知)
|
||||||
|
Get.snackbar('エラー', '位置情報の取得に失敗しました: ${e.toString()}');
|
||||||
} finally {
|
} finally {
|
||||||
isLoadingLocations.value = false;
|
isLoadingLocations.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateConnectionStatus(List<ConnectivityResult> results) {
|
void _updateConnectionStatus(List<ConnectivityResult> results) {
|
||||||
@ -497,17 +557,20 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
//
|
//
|
||||||
Future<void> login(String email, String password) async {
|
Future<void> login(String email, String password) async {
|
||||||
try {
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
final value = await AuthService.login(email, password);
|
final value = await AuthService.login(email, password);
|
||||||
if (value.isNotEmpty && value['token'] != null) {
|
if (value.isNotEmpty && value['token'] != null) {
|
||||||
await changeUser(value);
|
await changeUser(value);
|
||||||
await _initializeUserData();
|
await _initializeUserData();
|
||||||
Get.offAllNamed(AppPages.INDEX);
|
Get.offAllNamed(AppPages.INDEX);
|
||||||
} else {
|
} else {
|
||||||
Get.snackbar('Login Failed', 'Invalid credentials');
|
Get.snackbar('ログイン失敗', 'メールアドレスまたはパスワードが間違っています');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Login error: $e');
|
print('Login error: $e');
|
||||||
Get.snackbar('Login Failed', 'An error occurred. Please try again.');
|
Get.snackbar('ログイン失敗', 'エラーが発生しました。もう一度お試しください。');
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -897,27 +960,28 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapControllerの初期化が完了するまで待機
|
try {
|
||||||
await waitForMapControllerReady();
|
// MapControllerの初期化が完了するまで待機
|
||||||
|
await waitForMapControllerReady();
|
||||||
|
|
||||||
// null チェックを追加
|
// null チェックを追加
|
||||||
if (mapController.bounds == null) {
|
if (mapController.bounds == null) {
|
||||||
print("MapController bounds are null");
|
print("MapController bounds are null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// バウンドが有効かどうかを確認する
|
// バウンドが有効かどうかを確認する
|
||||||
LatLngBounds bounds = mapController.bounds!;
|
LatLngBounds bounds = mapController.bounds!;
|
||||||
if (!_isValidBounds(bounds)) {
|
if (!_isValidBounds(bounds)) {
|
||||||
print("MapController bounds are not valid");
|
print("MapController bounds are not valid");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
locations.clear();
|
locations.clear();
|
||||||
String cat = currentCat.isNotEmpty ? currentCat[0] : "";
|
String cat = currentCat.isNotEmpty ? currentCat[0] : "";
|
||||||
if (currentCat.isNotEmpty && currentCat[0] == "-all-") {
|
if (currentCat.isNotEmpty && currentCat[0] == "-all-") {
|
||||||
cat = "";
|
cat = "";
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
// Akira Add 2024-4-6
|
// Akira Add 2024-4-6
|
||||||
if( mapController.controller == null ) {
|
if( mapController.controller == null ) {
|
||||||
@ -928,13 +992,13 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
//
|
//
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//LatLngBounds bounds = mapController.bounds!;
|
//LatLngBounds bounds = mapController.bounds!;
|
||||||
|
|
||||||
|
currentBound.clear();
|
||||||
|
currentBound.add(bounds);
|
||||||
|
|
||||||
|
//isLoading.value = true; // ローディング状態をtrueに設定
|
||||||
|
|
||||||
currentBound.clear();
|
|
||||||
currentBound.add(bounds);
|
|
||||||
|
|
||||||
//isLoading.value = true; // ローディング状態をtrueに設定
|
|
||||||
|
|
||||||
//print("bounds --- (${bounds.southWest.latitude},${bounds.southWest.longitude}),(${bounds.northWest.latitude},${bounds.northWest.longitude}),(${bounds.northEast.latitude},${bounds.northEast.longitude}),(${bounds.southEast.latitude},${bounds.southEast.longitude})");
|
//print("bounds --- (${bounds.southWest.latitude},${bounds.southWest.longitude}),(${bounds.northWest.latitude},${bounds.northWest.longitude}),(${bounds.northEast.latitude},${bounds.northEast.longitude}),(${bounds.southEast.latitude},${bounds.southEast.longitude})");
|
||||||
|
|
||||||
// 要検討:APIからのレスポンスがnullの場合のエラーハンドリングが不十分です。適切なエラーメッセージを表示するなどの処理を追加してください。
|
// 要検討:APIからのレスポンスがnullの場合のエラーハンドリングが不十分です。適切なエラーメッセージを表示するなどの処理を追加してください。
|
||||||
@ -950,7 +1014,7 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
bounds.southEast.latitude,
|
bounds.southEast.latitude,
|
||||||
bounds.southEast.longitude,
|
bounds.southEast.longitude,
|
||||||
cat,
|
cat,
|
||||||
eventCode
|
eventCode
|
||||||
);
|
);
|
||||||
/*
|
/*
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
@ -979,7 +1043,8 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
"please zoom in",
|
"please zoom in",
|
||||||
backgroundColor: Colors.yellow,
|
backgroundColor: Colors.yellow,
|
||||||
colorText: Colors.white,
|
colorText: Colors.white,
|
||||||
icon: const Icon(Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
|
icon: const Icon(Icons.assistant_photo_outlined, size: 40.0,
|
||||||
|
color: Colors.blue),
|
||||||
snackPosition: SnackPosition.TOP,
|
snackPosition: SnackPosition.TOP,
|
||||||
duration: const Duration(seconds: 3),
|
duration: const Duration(seconds: 3),
|
||||||
);
|
);
|
||||||
@ -1011,12 +1076,15 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
locations.add(value);
|
locations.add(value);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
} catch ( e) {
|
} catch (e) {
|
||||||
print("Error in loadLocationsBound: $e");
|
print("Error in loadLocationsBound: $e");
|
||||||
// エラーが発生した場合のリトライ処理や適切なエラーメッセージの表示を行う
|
// エラーが発生した場合のリトライ処理や適切なエラーメッセージの表示を行う
|
||||||
// 例えば、一定時間後に再度loadLocationsBound()を呼び出すなど
|
// 例えば、一定時間後に再度loadLocationsBound()を呼び出すなど
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error in loadLocationsBound: $e');
|
||||||
|
//Get.snackbar('エラー', '位置情報の読み込みに失敗しました。もう一度お試しください。');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// バウンドが有効かどうかを確認するヘルパーメソッド
|
// バウンドが有効かどうかを確認するヘルパーメソッド
|
||||||
@ -1029,20 +1097,44 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
bounds.southWest.latitude < bounds.northEast.latitude;
|
bounds.southWest.latitude < bounds.northEast.latitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const int maxRetries = 5;
|
||||||
|
int retryCount = 0;
|
||||||
|
|
||||||
//===Akira 追加:2024-4-6 #2800
|
//===Akira 追加:2024-4-6 #2800
|
||||||
// 要検討:MapControllerの初期化が完了するまで待機していますが、タイムアウトを設定することを検討してください。
|
// 要検討:MapControllerの初期化が完了するまで待機していますが、タイムアウトを設定することを検討してください。
|
||||||
// 初期化に時間がかかりすぎる場合、ユーザーにわかりやすいメッセージを表示するようにしてください。
|
// 初期化に時間がかかりすぎる場合、ユーザーにわかりやすいメッセージを表示するようにしてください。
|
||||||
//
|
//
|
||||||
Future<void> waitForMapControllerReady() async {
|
Future<void> waitForMapControllerReady() async {
|
||||||
if (!isMapControllerReady.value) {
|
if (isMapControllerReady.value) return;
|
||||||
await Future.doWhile(() async {
|
|
||||||
await Future.delayed(const Duration(milliseconds: 100));
|
//await isMapControllerReady.firstWhere((value) => value == true);
|
||||||
return !isMapControllerReady.value;
|
|
||||||
});
|
const timeout = Duration(seconds: 30);
|
||||||
|
try {
|
||||||
|
await Future.any([
|
||||||
|
mapControllerCompleter.future,
|
||||||
|
Future.delayed(timeout).then((_) => throw TimeoutException('MapController initialization timed out')),
|
||||||
|
]);
|
||||||
|
isMapControllerReady.value = true;
|
||||||
|
} catch (e) {
|
||||||
|
print('Warning waiting for MapController: $e');
|
||||||
|
// タイムアウト後もマップが機能している場合は、強制的に準備完了とマーク
|
||||||
|
if (!isMapControllerReady.value) {
|
||||||
|
isMapControllerReady.value = true;
|
||||||
|
if (!mapControllerCompleter.isCompleted) {
|
||||||
|
mapControllerCompleter.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//===Akira 追加:2024-4-6 #2800
|
//===Akira 追加:2024-4-6 #2800
|
||||||
|
|
||||||
|
void retryMapInitialization() {
|
||||||
|
// マップの再初期化ロジック
|
||||||
|
initMapController();
|
||||||
|
// 他の必要な再初期化処理
|
||||||
|
}
|
||||||
|
|
||||||
void setBound(LatLngBounds bounds) {
|
void setBound(LatLngBounds bounds) {
|
||||||
currentBound.clear();
|
currentBound.clear();
|
||||||
currentBound.add(bounds);
|
currentBound.add(bounds);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:geolocator/geolocator.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:gifunavi/pages/destination/destination_controller.dart';
|
import 'package:gifunavi/pages/destination/destination_controller.dart';
|
||||||
import 'package:gifunavi/pages/drawer/drawer_page.dart';
|
import 'package:gifunavi/pages/drawer/drawer_page.dart';
|
||||||
@ -34,22 +35,45 @@ class IndexPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _IndexPageState extends State<IndexPage> {
|
class _IndexPageState extends State<IndexPage> {
|
||||||
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
|
final DestinationController destinationController = Get.find<DestinationController>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
_checkPermissionAndInitialize();
|
||||||
await _ensureControllersAreInitialized();
|
|
||||||
await PermissionController.checkAndRequestPermissions();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _ensureControllersAreInitialized() async {
|
Future<void> _checkPermissionAndInitialize() async {
|
||||||
while (!Get.isRegistered<IndexController>() ||
|
// 位置情報の許可が得られた場合の処理
|
||||||
!Get.isRegistered<DestinationController>() ||
|
await _initializeMap();
|
||||||
!Get.isRegistered<LocationController>()) {
|
}
|
||||||
await Future.delayed(const Duration(milliseconds: 100));
|
|
||||||
}
|
Future<void> _initializeMap() async {
|
||||||
|
await indexController.loadLocations();
|
||||||
|
setState(() {}); // 状態を更新してUIを再構築
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showLocationServiceDisabledOrDeniedError() {
|
||||||
|
Get.snackbar(
|
||||||
|
'エラー',
|
||||||
|
'位置情報サービスが無効か、許可されていません。設定を確認してください。',
|
||||||
|
duration: const Duration(seconds: 5),
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
mainButton: TextButton(
|
||||||
|
child: const Text('設定'),
|
||||||
|
onPressed: () => Geolocator.openLocationSettings(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showPermissionDeniedError() {
|
||||||
|
Get.snackbar(
|
||||||
|
'エラー',
|
||||||
|
'位置情報の許可が必要です。設定から許可してください。',
|
||||||
|
duration: const Duration(seconds: 5),
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkEventAndNavigate() async {
|
void checkEventAndNavigate() async {
|
||||||
@ -112,11 +136,11 @@ class _IndexPageState extends State<IndexPage> {
|
|||||||
void _showEventSelectionWarning() {
|
void _showEventSelectionWarning() {
|
||||||
Get.dialog(
|
Get.dialog(
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
title: Text('警告'),
|
title: const Text('警告'),
|
||||||
content: Text('イベントを選択してください。'),
|
content: const Text('イベントを選択してください。'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text('OK'),
|
child: const Text('OK'),
|
||||||
onPressed: () => Get.back(),
|
onPressed: () => Get.back(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -124,24 +148,27 @@ class _IndexPageState extends State<IndexPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// class IndexPage extends GetView<IndexController> {
|
// class IndexPage extends GetView<IndexController> {
|
||||||
// IndexPage({Key? key}) : super(key: key);
|
// IndexPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
// IndexControllerとDestinationControllerのインスタンスを取得しています。
|
// IndexControllerとDestinationControllerのインスタンスを取得しています。
|
||||||
//
|
//
|
||||||
final LocationController locationController = Get.find<LocationController>();
|
final LocationController locationController = Get.find<LocationController>();
|
||||||
final IndexController indexController = Get.find<IndexController>();
|
|
||||||
final DestinationController destinationController =
|
|
||||||
Get.find<DestinationController>();
|
|
||||||
|
|
||||||
// buildメソッドは、ウィジェットのUIを構築するメソッドです。
|
// buildメソッドは、ウィジェットのUIを構築するメソッドです。
|
||||||
// ここでは、WillPopScopeウィジェットを使用して、端末の戻るボタンが押された際の動作を制御しています。
|
// ここでは、WillPopScopeウィジェットを使用して、端末の戻るボタンが押された際の動作を制御しています。
|
||||||
//
|
//
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
|
/*
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: false,
|
canPop: false,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
|
|
||||||
|
*/
|
||||||
|
return Scaffold(
|
||||||
//
|
//
|
||||||
// Scaffoldウィジェットを使用して、アプリのメインページのレイアウトを構築しています。
|
// Scaffoldウィジェットを使用して、アプリのメインページのレイアウトを構築しています。
|
||||||
//
|
//
|
||||||
@ -150,24 +177,8 @@ class _IndexPageState extends State<IndexPage> {
|
|||||||
title: Obx(() => Text(indexController.selectedEventName.value)),
|
title: Obx(() => Text(indexController.selectedEventName.value)),
|
||||||
//title: Text("add_location".tr),
|
//title: Text("add_location".tr),
|
||||||
actions: [
|
actions: [
|
||||||
// IconButton(
|
|
||||||
// onPressed: () {
|
|
||||||
// DatabaseService ds = DatabaseService();
|
|
||||||
// ds.updateDatabase();
|
|
||||||
// },
|
|
||||||
// icon: const Icon(Icons.ten_k_sharp)),
|
|
||||||
|
|
||||||
//
|
|
||||||
// AppBarには、タイトルとアクションアイコンが含まれています。
|
|
||||||
// アクションアイコンには、GPSデータの表示、履歴の表示、マップの更新、検索などの機能が含まれています。
|
|
||||||
//
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
// GpsDatabaseHelper db = GpsDatabaseHelper.instance;
|
|
||||||
// List<GpsData> data = await db.getGPSData(
|
|
||||||
// indexController.currentUser[0]["user"]['team_name'],
|
|
||||||
// indexController.currentUser[0]["user"]["event_code"]);
|
|
||||||
// print("GPS data is ${data.length}");
|
|
||||||
Get.toNamed(AppPages.GPS);
|
Get.toNamed(AppPages.GPS);
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.telegram)),
|
icon: const Icon(Icons.telegram)),
|
||||||
@ -201,102 +212,22 @@ class _IndexPageState extends State<IndexPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
//CatWidget(indexController: indexController,),
|
//CatWidget(indexController: indexController,),
|
||||||
//
|
|
||||||
// デバッグ時のみリロードボタンの横にGPS信号レベルの設定ボタンを設置し、
|
|
||||||
// タップすることでGPS信号の強弱をシミュレーションできるようにする
|
|
||||||
// Akira 2024-4-5
|
|
||||||
//
|
|
||||||
/*
|
|
||||||
Obx(() {
|
|
||||||
if (locationController.isSimulationMode) {
|
|
||||||
return DropdownButton<String>(
|
|
||||||
value: locationController.getSimulatedSignalStrength(),
|
|
||||||
onChanged: (value) {
|
|
||||||
//debugPrint("DropDown changed!");
|
|
||||||
locationController.setSimulatedSignalStrength(value!);
|
|
||||||
},
|
|
||||||
items: ['low', 'medium', 'high', 'real']
|
|
||||||
.map<DropdownMenuItem<String>>((String value) {
|
|
||||||
return DropdownMenuItem<String>(
|
|
||||||
value: value,
|
|
||||||
child: Text(value),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
*/
|
|
||||||
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
// bottomNavigationBar: BottomAppBar(
|
|
||||||
// child: Row(
|
|
||||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
// children: <Widget>[
|
|
||||||
// Obx(
|
|
||||||
// () => destinationController.isInRog.value == true
|
|
||||||
// ? IconButton(
|
|
||||||
// onPressed: () {},
|
|
||||||
// icon: const Icon(
|
|
||||||
// Icons.run_circle,
|
|
||||||
// size: 44,
|
|
||||||
// color: Colors.green,
|
|
||||||
// ))
|
|
||||||
// : IconButton(
|
|
||||||
// onPressed: () {},
|
|
||||||
// icon: const Icon(
|
|
||||||
// Icons.run_circle,
|
|
||||||
// size: 44,
|
|
||||||
// color: Colors.black12,
|
|
||||||
// )),
|
|
||||||
// ),
|
|
||||||
// Padding(
|
|
||||||
// padding:
|
|
||||||
// const EdgeInsets.only(right: 10.0, top: 4.0, bottom: 4.0),
|
|
||||||
// child: InkWell(
|
|
||||||
// child: Obx(() => destinationController
|
|
||||||
// .isGpsSelected.value ==
|
|
||||||
// true
|
|
||||||
// ? Padding(
|
|
||||||
// padding: const EdgeInsets.only(
|
|
||||||
// right: 10.0, top: 4.0, bottom: 4.0),
|
|
||||||
// child: InkWell(
|
|
||||||
// child: const Image(
|
|
||||||
// image:
|
|
||||||
// AssetImage('assets/images/route3_off.png'),
|
|
||||||
// width: 35,
|
|
||||||
// height: 35,
|
|
||||||
// ),
|
|
||||||
// onTap: () {
|
|
||||||
// //indexController.switchPage(AppPages.TRAVEL);
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
// : Padding(
|
|
||||||
// padding: const EdgeInsets.only(
|
|
||||||
// right: 10.0, top: 4.0, bottom: 4.0),
|
|
||||||
// child: InkWell(
|
|
||||||
// child: const Image(
|
|
||||||
// image:
|
|
||||||
// AssetImage('assets/images/route2_on.png'),
|
|
||||||
// width: 35,
|
|
||||||
// height: 35,
|
|
||||||
// ),
|
|
||||||
// onTap: () {
|
|
||||||
// //indexController.switchPage(AppPages.TRAVEL);
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ))),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
|
|
||||||
//
|
body: SafeArea(
|
||||||
|
child:Obx(() {
|
||||||
|
if (indexController.isLoadingLocations.value) {
|
||||||
|
return const Center(child: CircularProgressIndicator()); // Index Controller
|
||||||
|
} else {
|
||||||
|
return indexController.mode.value == 0
|
||||||
|
? const MapWidget()
|
||||||
|
: const ListWidget();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
// マップモードとリストモードを切り替えるためのボタンです。
|
// マップモードとリストモードを切り替えるためのボタンです。
|
||||||
//
|
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
indexController.toggleMode();
|
indexController.toggleMode();
|
||||||
@ -312,22 +243,6 @@ class _IndexPageState extends State<IndexPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
||||||
//
|
);
|
||||||
// bodyには、SafeAreaウィジェットを使用して、画面の安全な領域内にUIを構築しています。
|
|
||||||
//
|
|
||||||
body: SafeArea(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Obx(
|
|
||||||
() => indexController.mode.value == 0
|
|
||||||
? const MapWidget()
|
|
||||||
: const ListWidget(),
|
|
||||||
))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -182,7 +182,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius:
|
borderRadius:
|
||||||
BorderRadius.circular(40)),
|
BorderRadius.circular(40)),
|
||||||
child: const CircularProgressIndicator(),
|
child: const CircularProgressIndicator(), // Login page
|
||||||
)
|
)
|
||||||
: Column(
|
: Column(
|
||||||
children: [
|
children: [
|
||||||
@ -199,24 +199,19 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
.tr,
|
.tr,
|
||||||
backgroundColor: Colors.red,
|
backgroundColor: Colors.red,
|
||||||
colorText: Colors.white,
|
colorText: Colors.white,
|
||||||
icon: const Icon(
|
icon: const Icon(Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
|
||||||
Icons
|
snackPosition: SnackPosition.TOP,
|
||||||
.assistant_photo_outlined,
|
duration: const Duration(seconds: 3),
|
||||||
size: 40.0,
|
|
||||||
color: Colors.blue),
|
|
||||||
snackPosition:
|
|
||||||
SnackPosition.TOP,
|
|
||||||
duration: const Duration(
|
|
||||||
seconds: 3),
|
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
indexController.isLoading.value =
|
try {
|
||||||
true;
|
//indexController.isLoading.value = true;
|
||||||
indexController.login(
|
indexController.login(emailController.text, passwordController.text);
|
||||||
emailController.text,
|
} catch (e) {
|
||||||
passwordController.text
|
print('Error during login: $e');
|
||||||
);
|
// エラーハンドリングは IndexController 内で行われるため、ここでは何もしない
|
||||||
|
}
|
||||||
},
|
},
|
||||||
color: Colors.indigoAccent[400],
|
color: Colors.indigoAccent[400],
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
|
|||||||
@ -2,23 +2,87 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:geolocator/geolocator.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
|
||||||
class PermissionController {
|
class PermissionController {
|
||||||
|
static bool? _locationPermissionGranted;
|
||||||
|
|
||||||
static bool _isRequestingPermission = false;
|
/*
|
||||||
static Completer<bool>? _permissionCompleter;
|
bool? _isRequestingPermission=false;
|
||||||
|
final Completer<PermissionController> _permissionCompleter = Completer<PermissionController>();
|
||||||
|
*/
|
||||||
|
|
||||||
static Future<bool> checkAndRequestPermissions() async {
|
static Future<bool> checkAndRequestPermissions_new() async {
|
||||||
if (_isRequestingPermission) {
|
try {
|
||||||
return _permissionCompleter!.future;
|
if (_locationPermissionGranted != null) {
|
||||||
|
return _locationPermissionGranted!;
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||||
|
if (!serviceEnabled) {
|
||||||
|
Get.snackbar(
|
||||||
|
'エラー', '位置情報サービスが無効です。設定から有効にしてください。');
|
||||||
|
|
||||||
|
// 位置情報サービスが無効の場合、ユーザーに有効化を促す
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
debugPrint("====> Geolocator.checkPermission");
|
||||||
|
var permission = await Geolocator.checkPermission();
|
||||||
|
if (permission == LocationPermission.denied) {
|
||||||
|
permission = await Geolocator.requestPermission();
|
||||||
|
if (permission == LocationPermission.denied) {
|
||||||
|
Get.snackbar('エラー', '位置情報の許可が拒否されました。');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permission == LocationPermission.deniedForever) {
|
||||||
|
Get.snackbar('エラー',
|
||||||
|
'位置情報の許可が永久に拒否されました。設定から許可してください。');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_locationPermissionGranted = true;
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
print('Error checking permissions: $e');
|
||||||
|
Get.snackbar('エラー', '権限の確認中にエラーが発生しました。');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void resetPermissionCache() {
|
||||||
|
_locationPermissionGranted = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> _requestLocationPermission() async {
|
||||||
|
BuildContext? context;
|
||||||
|
int attempts = 0;
|
||||||
|
const maxAttempts = 10;
|
||||||
|
|
||||||
|
debugPrint("====> _requestLocationPermission");
|
||||||
|
|
||||||
|
// コンテキストが利用可能になるまで待機するロジック
|
||||||
|
while (Get.context == null && attempts < maxAttempts) {
|
||||||
|
context = Get.context;
|
||||||
|
if (context == null) {
|
||||||
|
await Future.delayed(const Duration(milliseconds: 500));
|
||||||
|
attempts++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_isRequestingPermission = true;
|
if (Get.context == null) {
|
||||||
_permissionCompleter = Completer<bool>();
|
print('Context is still null after waiting, cannot proceed with permission check');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bool hasPermissions = await _checkLocationPermissions();
|
bool hasPermissions = await _checkLocationPermissions();
|
||||||
@ -36,18 +100,16 @@ class PermissionController {
|
|||||||
SystemNavigator.pop();
|
SystemNavigator.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return hasPermissions;
|
||||||
|
|
||||||
_isRequestingPermission = false;
|
|
||||||
_permissionCompleter!.complete(hasPermissions);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error in permission request: $e');
|
print('Error in permission request: $e');
|
||||||
_isRequestingPermission = false;
|
return false;
|
||||||
_permissionCompleter!.complete(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _permissionCompleter!.future;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Future<bool> _checkLocationPermissions() async {
|
static Future<bool> _checkLocationPermissions() async {
|
||||||
final locationPermission = await Permission.location.status;
|
final locationPermission = await Permission.location.status;
|
||||||
final whenInUsePermission = await Permission.locationWhenInUse.status;
|
final whenInUsePermission = await Permission.locationWhenInUse.status;
|
||||||
@ -118,20 +180,30 @@ class PermissionController {
|
|||||||
(whenInUsePermission == PermissionStatus.granted || alwaysPermission == PermissionStatus.granted);
|
(whenInUsePermission == PermissionStatus.granted || alwaysPermission == PermissionStatus.granted);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<bool> checkAndRequestPermissions_old() async {
|
static Future<bool> checkAndRequestPermissions() async {
|
||||||
|
|
||||||
|
/*
|
||||||
if (_isRequestingPermission) {
|
if (_isRequestingPermission) {
|
||||||
return _permissionCompleter!.future;
|
return _permissionCompleter!.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
_isRequestingPermission = true;
|
_isRequestingPermission = true;
|
||||||
_permissionCompleter = Completer<bool>();
|
_permissionCompleter = Completer<bool>();
|
||||||
|
*/
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (_locationPermissionGranted != null) {
|
||||||
|
return _locationPermissionGranted!;
|
||||||
|
}
|
||||||
|
|
||||||
bool hasPermissions = await _checkLocationPermissions();
|
bool hasPermissions = await _checkLocationPermissions();
|
||||||
if (!hasPermissions) {
|
if (!hasPermissions) {
|
||||||
bool userAgreed = await showLocationDisclosure();
|
bool userAgreed = await showLocationDisclosure();
|
||||||
if (userAgreed) {
|
if (userAgreed) {
|
||||||
hasPermissions = await _requestAllLocationPermissions();
|
hasPermissions = await _requestAllLocationPermissions();
|
||||||
|
_locationPermissionGranted = true;
|
||||||
|
debugPrint("Finish checkAndRequestPermissions...");
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
print('User did not agree to location usage');
|
print('User did not agree to location usage');
|
||||||
hasPermissions = false;
|
hasPermissions = false;
|
||||||
@ -140,15 +212,18 @@ class PermissionController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_isRequestingPermission = false;
|
//_isRequestingPermission = false;
|
||||||
_permissionCompleter!.complete(hasPermissions);
|
//_permissionCompleter!.complete(hasPermissions);
|
||||||
|
|
||||||
|
|
||||||
} catch( e ) {
|
} catch( e ) {
|
||||||
print('Error in permission request: $e');
|
print('Error in permission request: $e');
|
||||||
_isRequestingPermission = false;
|
//_isRequestingPermission = false;
|
||||||
_permissionCompleter!.complete(false);
|
//_permissionCompleter!.complete(false);
|
||||||
}
|
}
|
||||||
debugPrint("Finish checkAndRequestPermissions...");
|
debugPrint("Finish checkAndRequestPermissions...");
|
||||||
return _permissionCompleter!.future;
|
//return _permissionCompleter!.future;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> requestAllLocationPermissions() async {
|
static Future<void> requestAllLocationPermissions() async {
|
||||||
@ -171,10 +246,10 @@ class PermissionController {
|
|||||||
print('Context is null, cannot show dialog');
|
print('Context is null, cannot show dialog');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (Get.isDialogOpen ?? false) {
|
//if (Get.isDialogOpen ?? false) {
|
||||||
print('A dialog is already open');
|
// print('A dialog is already open');
|
||||||
return false;
|
// return false;
|
||||||
}
|
//}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final result = await Get.dialog<bool>(
|
final result = await Get.dialog<bool>(
|
||||||
|
|||||||
@ -9,6 +9,12 @@ class SettingsController extends GetxController {
|
|||||||
var autoReturnDisabled = false.obs;
|
var autoReturnDisabled = false.obs;
|
||||||
final MapResetController mapResetController = Get.put(MapResetController());
|
final MapResetController mapResetController = Get.put(MapResetController());
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
ever(timerDuration, (_) => resetIdleTimer());
|
||||||
|
}
|
||||||
|
|
||||||
void updateTimerDuration(int seconds) {
|
void updateTimerDuration(int seconds) {
|
||||||
timerDuration.value = Duration(seconds: seconds);
|
timerDuration.value = Duration(seconds: seconds);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:get/get_navigation/src/routes/get_route.dart';
|
import 'package:get/get_navigation/src/routes/get_route.dart';
|
||||||
import 'package:gifunavi/pages/changepassword/change_password_page.dart';
|
import 'package:gifunavi/pages/changepassword/change_password_page.dart';
|
||||||
@ -34,6 +35,10 @@ import 'package:gifunavi/pages/entry/event_entries_page.dart';
|
|||||||
import 'package:gifunavi/pages/entry/event_entries_binding.dart';
|
import 'package:gifunavi/pages/entry/event_entries_binding.dart';
|
||||||
import 'package:gifunavi/pages/register/user_detail_page.dart';
|
import 'package:gifunavi/pages/register/user_detail_page.dart';
|
||||||
|
|
||||||
|
import 'package:gifunavi/pages/entry/event_result_page.dart';
|
||||||
|
|
||||||
|
import '../model/entry.dart';
|
||||||
|
|
||||||
part 'app_routes.dart';
|
part 'app_routes.dart';
|
||||||
|
|
||||||
class AppPages {
|
class AppPages {
|
||||||
@ -70,6 +75,7 @@ class AppPages {
|
|||||||
static const EVENT_ENTRY = Routes.EVENT_ENTRIES;
|
static const EVENT_ENTRY = Routes.EVENT_ENTRIES;
|
||||||
static const USER_DETAILS_EDIT = Routes.USER_DETAILS_EDIT;
|
static const USER_DETAILS_EDIT = Routes.USER_DETAILS_EDIT;
|
||||||
|
|
||||||
|
static const EVENT_RESULT = Routes.EVENT_RESULT;
|
||||||
|
|
||||||
static final routes = [
|
static final routes = [
|
||||||
GetPage(
|
GetPage(
|
||||||
@ -175,6 +181,23 @@ class AppPages {
|
|||||||
name: Routes.USER_DETAILS_EDIT,
|
name: Routes.USER_DETAILS_EDIT,
|
||||||
page: () => const UserDetailsEditPage(),
|
page: () => const UserDetailsEditPage(),
|
||||||
),
|
),
|
||||||
|
GetPage(
|
||||||
|
name: Routes.EVENT_RESULT,
|
||||||
|
page: () {
|
||||||
|
final args = Get.arguments;
|
||||||
|
if (args is Map<String, dynamic> && args.containsKey('entry')) {
|
||||||
|
return EventResultPage(entry: args['entry'] as Entry);
|
||||||
|
} else {
|
||||||
|
// エントリーが提供されていない場合のフォールバック
|
||||||
|
// 例: エラーページを表示するか、ホームページにリダイレクトする
|
||||||
|
return const Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: Text('エラー: イベント結果を表示できません。'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,4 +38,5 @@ abstract class Routes {
|
|||||||
static const EVENT_ENTRIES = '/event-entries';
|
static const EVENT_ENTRIES = '/event-entries';
|
||||||
static const USER_DETAILS_EDIT = '/user-details-edit';
|
static const USER_DETAILS_EDIT = '/user-details-edit';
|
||||||
|
|
||||||
|
static const EVENT_RESULT = '/event-result';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,7 +61,7 @@ class ApiService extends GetxService{
|
|||||||
final indexController = Get.find<IndexController>();
|
final indexController = Get.find<IndexController>();
|
||||||
if (indexController.currentUser.isNotEmpty) {
|
if (indexController.currentUser.isNotEmpty) {
|
||||||
token = indexController.currentUser[0]['token'] ?? '';
|
token = indexController.currentUser[0]['token'] ?? '';
|
||||||
print("Get token = $token");
|
//print("Get token = $token");
|
||||||
}else{
|
}else{
|
||||||
token = "";
|
token = "";
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ class ApiService extends GetxService{
|
|||||||
final indexController = Get.find<IndexController>();
|
final indexController = Get.find<IndexController>();
|
||||||
if (indexController.currentUser.isNotEmpty) {
|
if (indexController.currentUser.isNotEmpty) {
|
||||||
token = indexController.currentUser[0]['token'] ?? '';
|
token = indexController.currentUser[0]['token'] ?? '';
|
||||||
print("Get token = $token");
|
//print("Get token = $token");
|
||||||
}else{
|
}else{
|
||||||
token = "";
|
token = "";
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@ class ApiService extends GetxService{
|
|||||||
Future<dynamic> _handleRequest(Future<http.Response> Function() request) async {
|
Future<dynamic> _handleRequest(Future<http.Response> Function() request) async {
|
||||||
try {
|
try {
|
||||||
final response = await request();
|
final response = await request();
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200 || response.statusCode == 201 ) {
|
||||||
return json.decode(utf8.decode(response.bodyBytes));
|
return json.decode(utf8.decode(response.bodyBytes));
|
||||||
} else if (response.statusCode == 401) {
|
} else if (response.statusCode == 401) {
|
||||||
await _handleUnauthorized();
|
await _handleUnauthorized();
|
||||||
@ -117,42 +117,6 @@ class ApiService extends GetxService{
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<List<Team>> getTeams_old() async {
|
|
||||||
init();
|
|
||||||
final token = await getToken2();
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
final response = await http.get(
|
|
||||||
Uri.parse('$baseUrl/teams/'),
|
|
||||||
headers: {'Authorization': 'Token $token',"Content-Type": "application/json; charset=UTF-8"},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
// UTF-8でデコード
|
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
|
||||||
//print('User Response body: $decodedResponse');
|
|
||||||
List<dynamic> teamsJson = json.decode(decodedResponse);
|
|
||||||
|
|
||||||
List<Team> teams = [];
|
|
||||||
for (var teamJson in teamsJson) {
|
|
||||||
//print('\nTeam Data:');
|
|
||||||
//_printDataComparison(teamJson, Team);
|
|
||||||
teams.add(Team.fromJson(teamJson));
|
|
||||||
}
|
|
||||||
return teams;
|
|
||||||
} else {
|
|
||||||
throw Exception('Failed to load teams. Status code: ${response.statusCode}');
|
|
||||||
}
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
print('Error in getTeams: $e');
|
|
||||||
print('Stack trace: $stackTrace');
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Future<List<NewCategory>> getCategories() async {
|
Future<List<NewCategory>> getCategories() async {
|
||||||
init();
|
init();
|
||||||
getToken();
|
getToken();
|
||||||
@ -165,7 +129,7 @@ class ApiService extends GetxService{
|
|||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||||
print('User Response body: $decodedResponse');
|
//print('User Response body: $decodedResponse');
|
||||||
List<dynamic> categoriesJson = json.decode(decodedResponse);
|
List<dynamic> categoriesJson = json.decode(decodedResponse);
|
||||||
|
|
||||||
List<NewCategory> categories = [];
|
List<NewCategory> categories = [];
|
||||||
@ -200,26 +164,6 @@ class ApiService extends GetxService{
|
|||||||
)).then((data) => NewCategory.fromJson(data));
|
)).then((data) => NewCategory.fromJson(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<NewCategory> getZekkenNumber_old(int categoryId) async {
|
|
||||||
try {
|
|
||||||
final response = await http.post(
|
|
||||||
Uri.parse('$baseUrl/categories-viewset/$categoryId/get_zekken_number/'),
|
|
||||||
headers: {'Authorization': 'Token $token',"Content-Type": "application/json; charset=UTF-8"},
|
|
||||||
);
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
|
||||||
print('User Response body: $decodedResponse');
|
|
||||||
final categoriesJson = json.decode(decodedResponse);
|
|
||||||
|
|
||||||
return NewCategory.fromJson(categoriesJson);
|
|
||||||
} else {
|
|
||||||
throw Exception('Failed to increment category number');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
throw Exception('Error incrementing category number: $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<User> getCurrentUser() async {
|
Future<User> getCurrentUser() async {
|
||||||
init();
|
init();
|
||||||
final token = getToken();
|
final token = getToken();
|
||||||
@ -324,30 +268,6 @@ class ApiService extends GetxService{
|
|||||||
)).then((data) => Team.fromJson(data));
|
)).then((data) => Team.fromJson(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Team> createTeam_old(String teamName, int categoryId) async {
|
|
||||||
init();
|
|
||||||
getToken();
|
|
||||||
|
|
||||||
final response = await http.post(
|
|
||||||
Uri.parse('$baseUrl/teams/'),
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Token $token',
|
|
||||||
"Content-Type": "application/json; charset=UTF-8",
|
|
||||||
},
|
|
||||||
body: json.encode({
|
|
||||||
'team_name': teamName,
|
|
||||||
'category': categoryId,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 201) {
|
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
|
||||||
return Team.fromJson(json.decode(decodedResponse));
|
|
||||||
} else {
|
|
||||||
throw Exception('Failed to create team');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Team> updateTeam(int teamId, String teamName, int categoryId) async {
|
Future<Team> updateTeam(int teamId, String teamName, int categoryId) async {
|
||||||
final token = await getToken2();
|
final token = await getToken2();
|
||||||
return _handleRequest(() => http.put(
|
return _handleRequest(() => http.put(
|
||||||
@ -357,31 +277,6 @@ class ApiService extends GetxService{
|
|||||||
)).then((data) => Team.fromJson(data));
|
)).then((data) => Team.fromJson(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Team> updateTeam_old(int teamId, String teamName, int categoryId) async {
|
|
||||||
init();
|
|
||||||
getToken();
|
|
||||||
|
|
||||||
final response = await http.put(
|
|
||||||
Uri.parse('$baseUrl/teams/$teamId/'),
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Token $token',
|
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
|
||||||
},
|
|
||||||
body: json.encode({
|
|
||||||
'team_name': teamName,
|
|
||||||
'category': categoryId,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
|
||||||
|
|
||||||
return Team.fromJson(json.decode(decodedResponse));
|
|
||||||
} else {
|
|
||||||
throw Exception('Failed to update team');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> deleteTeam(int teamId) async {
|
Future<void> deleteTeam(int teamId) async {
|
||||||
final token = await getToken2();
|
final token = await getToken2();
|
||||||
await _handleRequest(() => http.delete(
|
await _handleRequest(() => http.delete(
|
||||||
@ -390,24 +285,6 @@ class ApiService extends GetxService{
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteTeamold_(int teamId) async {
|
|
||||||
init();
|
|
||||||
getToken();
|
|
||||||
|
|
||||||
final response = await http.delete(
|
|
||||||
Uri.parse('$baseUrl/teams/$teamId/'),
|
|
||||||
headers: {'Authorization': 'Token $token','Content-Type': 'application/json; charset=UTF-8'},
|
|
||||||
);
|
|
||||||
|
|
||||||
if( response.statusCode == 400) {
|
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
|
||||||
print('User Response body: $decodedResponse');
|
|
||||||
throw Exception('まだメンバーが残っているので、チームを削除できません。');
|
|
||||||
}else if (response.statusCode != 204) {
|
|
||||||
throw Exception('Failed to delete team');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<User>> getTeamMembers(int teamId) async {
|
Future<List<User>> getTeamMembers(int teamId) async {
|
||||||
final token = await getToken2();
|
final token = await getToken2();
|
||||||
return _handleRequest(() => http.get(
|
return _handleRequest(() => http.get(
|
||||||
@ -416,26 +293,6 @@ class ApiService extends GetxService{
|
|||||||
)).then((data) => (data as List).map((json) => User.fromJson(json)).toList());
|
)).then((data) => (data as List).map((json) => User.fromJson(json)).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<User>> getTeamMembers_old(int teamId) async {
|
|
||||||
init();
|
|
||||||
getToken();
|
|
||||||
|
|
||||||
final response = await http.get(
|
|
||||||
Uri.parse('$baseUrl/teams/$teamId/members/'),
|
|
||||||
headers: {'Authorization': 'Token $token','Content-Type': 'application/json; charset=UTF-8'},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
|
||||||
print('User Response body: $decodedResponse');
|
|
||||||
List<dynamic> membersJson = json.decode(decodedResponse);
|
|
||||||
|
|
||||||
return membersJson.map((json) => User.fromJson(json)).toList();
|
|
||||||
} else {
|
|
||||||
throw Exception('Failed to load team members');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<User> createTeamMember(int teamId, String? email, String? firstname, String? lastname, DateTime? dateOfBirth, bool? female) async {
|
Future<User> createTeamMember(int teamId, String? email, String? firstname, String? lastname, DateTime? dateOfBirth, bool? female) async {
|
||||||
final token = await getToken2();
|
final token = await getToken2();
|
||||||
String? formattedDateOfBirth = dateOfBirth != null ? DateFormat('yyyy-MM-dd').format(dateOfBirth) : null;
|
String? formattedDateOfBirth = dateOfBirth != null ? DateFormat('yyyy-MM-dd').format(dateOfBirth) : null;
|
||||||
@ -452,45 +309,6 @@ class ApiService extends GetxService{
|
|||||||
)).then((data) => User.fromJson(data));
|
)).then((data) => User.fromJson(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<User> createTeamMember_old(int teamId, String? email, String? firstname, String? lastname, DateTime? dateOfBirth,bool? female) async {
|
|
||||||
init();
|
|
||||||
getToken();
|
|
||||||
|
|
||||||
// emailが値を持っている場合の処理
|
|
||||||
if (email != null && email.isNotEmpty) {
|
|
||||||
firstname ??= "dummy";
|
|
||||||
lastname ??= "dummy";
|
|
||||||
dateOfBirth ??= DateTime.now();
|
|
||||||
female ??= false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String? formattedDateOfBirth;
|
|
||||||
if (dateOfBirth != null) {
|
|
||||||
formattedDateOfBirth = DateFormat('yyyy-MM-dd').format(dateOfBirth);
|
|
||||||
}
|
|
||||||
|
|
||||||
final response = await http.post(
|
|
||||||
Uri.parse('$baseUrl/teams/$teamId/members/'),
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Token $token',
|
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
|
||||||
},
|
|
||||||
body: json.encode({
|
|
||||||
'email': email,
|
|
||||||
'firstname': firstname,
|
|
||||||
'lastname': lastname,
|
|
||||||
'date_of_birth': formattedDateOfBirth,
|
|
||||||
'female': female,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
|
||||||
return User.fromJson(json.decode(decodedResponse));
|
|
||||||
} else {
|
|
||||||
throw Exception('Failed to create team member');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<User> updateTeamMember(int teamId, int? memberId, String firstname, String lastname, DateTime? dateOfBirth, bool? female) async {
|
Future<User> updateTeamMember(int teamId, int? memberId, String firstname, String lastname, DateTime? dateOfBirth, bool? female) async {
|
||||||
final token = await getToken2();
|
final token = await getToken2();
|
||||||
@ -507,37 +325,6 @@ class ApiService extends GetxService{
|
|||||||
)).then((data) => User.fromJson(data));
|
)).then((data) => User.fromJson(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<User> updateTeamMember_old(int teamId,int? memberId, String firstname, String lastname, DateTime? dateOfBirth,bool? female) async {
|
|
||||||
init();
|
|
||||||
getToken();
|
|
||||||
|
|
||||||
String? formattedDateOfBirth;
|
|
||||||
if (dateOfBirth != null) {
|
|
||||||
formattedDateOfBirth = DateFormat('yyyy-MM-dd').format(dateOfBirth);
|
|
||||||
}
|
|
||||||
|
|
||||||
final response = await http.put(
|
|
||||||
Uri.parse('$baseUrl/teams/$teamId/members/$memberId/'),
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Token $token',
|
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
|
||||||
},
|
|
||||||
body: json.encode({
|
|
||||||
'firstname': firstname,
|
|
||||||
'lastname': lastname,
|
|
||||||
'date_of_birth': formattedDateOfBirth,
|
|
||||||
'female': female,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
|
||||||
return User.fromJson(json.decode(decodedResponse));
|
|
||||||
} else {
|
|
||||||
throw Exception('Failed to update team member');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> deleteTeamMember(int teamId, int memberId) async {
|
Future<void> deleteTeamMember(int teamId, int memberId) async {
|
||||||
final token = await getToken2();
|
final token = await getToken2();
|
||||||
await _handleRequest(() => http.delete(
|
await _handleRequest(() => http.delete(
|
||||||
@ -546,20 +333,6 @@ class ApiService extends GetxService{
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteTeamMember_old(int teamId,int memberId) async {
|
|
||||||
init();
|
|
||||||
getToken();
|
|
||||||
|
|
||||||
final response = await http.delete(
|
|
||||||
Uri.parse('$baseUrl/teams/$teamId/members/$memberId/'),
|
|
||||||
headers: {'Authorization': 'Token $token'},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode != 204) {
|
|
||||||
throw Exception('Failed to delete team member');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> deleteAllTeamMembers(int teamId) async {
|
Future<void> deleteAllTeamMembers(int teamId) async {
|
||||||
final response = await http.delete(
|
final response = await http.delete(
|
||||||
Uri.parse('$baseUrl/teams/$teamId/members/destroy_all/?confirm=true'),
|
Uri.parse('$baseUrl/teams/$teamId/members/destroy_all/?confirm=true'),
|
||||||
@ -594,24 +367,6 @@ class ApiService extends GetxService{
|
|||||||
)).then((data) => (data as List).map((json) => Entry.fromJson(json)).toList());
|
)).then((data) => (data as List).map((json) => Entry.fromJson(json)).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Entry>> getEntries_old() async {
|
|
||||||
init();
|
|
||||||
getToken();
|
|
||||||
|
|
||||||
final response = await http.get(
|
|
||||||
Uri.parse('$baseUrl/entry/'),
|
|
||||||
headers: {'Authorization': 'Token $token', 'Content-Type': 'application/json; charset=UTF-8',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
|
||||||
List<dynamic> entriesJson = json.decode(decodedResponse);
|
|
||||||
return entriesJson.map((json) => Entry.fromJson(json)).toList();
|
|
||||||
} else {
|
|
||||||
throw Exception('Failed to load entries');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<Entry>> getTeamEntries(int teamId) async {
|
Future<List<Entry>> getTeamEntries(int teamId) async {
|
||||||
try {
|
try {
|
||||||
@ -632,6 +387,21 @@ class ApiService extends GetxService{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Entry> updateEntryStatus(int entryId, bool hasParticipated, bool hasGoaled) async {
|
||||||
|
final token = await getToken2();
|
||||||
|
return _handleRequest(() => http.patch(
|
||||||
|
Uri.parse('$baseUrl/entries/$entryId/update-status/'),
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Token $token',
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8'
|
||||||
|
},
|
||||||
|
body: json.encode({
|
||||||
|
'hasParticipated': hasParticipated,
|
||||||
|
'hasGoaled': hasGoaled,
|
||||||
|
}),
|
||||||
|
)).then((data) => Entry.fromJson(data));
|
||||||
|
}
|
||||||
|
|
||||||
Future<List<Event>> getEvents() async {
|
Future<List<Event>> getEvents() async {
|
||||||
init();
|
init();
|
||||||
getToken();
|
getToken();
|
||||||
@ -669,39 +439,6 @@ class ApiService extends GetxService{
|
|||||||
)).then((data) => Entry.fromJson(data));
|
)).then((data) => Entry.fromJson(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Entry> createEntry_old(int teamId, int eventId, int categoryId, DateTime date,String zekkenNumber) async {
|
|
||||||
init();
|
|
||||||
getToken();
|
|
||||||
|
|
||||||
String? formattedDate;
|
|
||||||
formattedDate = DateFormat('yyyy-MM-dd').format(date);
|
|
||||||
|
|
||||||
final response = await http.post(
|
|
||||||
Uri.parse('$baseUrl/entry/'),
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Token $token',
|
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
|
||||||
},
|
|
||||||
body: json.encode({
|
|
||||||
'team': teamId,
|
|
||||||
'event': eventId,
|
|
||||||
'category': categoryId,
|
|
||||||
'date': formattedDate,
|
|
||||||
'zekken_number':zekkenNumber,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 201) {
|
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
|
||||||
|
|
||||||
return Entry.fromJson(json.decode(decodedResponse));
|
|
||||||
} else {
|
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
|
||||||
print("decodedResponse = $decodedResponse");
|
|
||||||
final errorInfo = json.decode(decodedResponse);
|
|
||||||
throw Exception(errorInfo['error']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> updateUserInfo(int userId, Entry entry) async {
|
Future<void> updateUserInfo(int userId, Entry entry) async {
|
||||||
final token = await getToken2();
|
final token = await getToken2();
|
||||||
@ -719,40 +456,6 @@ class ApiService extends GetxService{
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateUserInfo_old(int userId, Entry entry) async {
|
|
||||||
init();
|
|
||||||
getToken();
|
|
||||||
|
|
||||||
final entryId = entry.id;
|
|
||||||
|
|
||||||
DateTime? date = entry.date;
|
|
||||||
String? formattedDate;
|
|
||||||
formattedDate = DateFormat('yyyy-MM-dd').format(date!);
|
|
||||||
|
|
||||||
final response = await http.put(
|
|
||||||
Uri.parse('$baseUrl/userinfo/$userId/'),
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Token $token',
|
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
|
||||||
},
|
|
||||||
body: json.encode({
|
|
||||||
'zekken_number': entry.zekkenNumber,
|
|
||||||
'event_code': entry.event.eventName,
|
|
||||||
'group': entry.team.category.categoryName,
|
|
||||||
'team_name': entry.team.teamName,
|
|
||||||
'date': formattedDate,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
|
||||||
final updatedUserInfo = json.decode(decodedResponse);
|
|
||||||
//Get.find<IndexController>().updateUserInfo(updatedUserInfo);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw Exception('Failed to update entry');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Future<Entry> updateEntry(int entryId, int teamId, int eventId, int categoryId, DateTime date,int zekkenNumber) async {
|
Future<Entry> updateEntry(int entryId, int teamId, int eventId, int categoryId, DateTime date,int zekkenNumber) async {
|
||||||
init();
|
init();
|
||||||
|
|||||||
@ -87,8 +87,7 @@ class AuthService {
|
|||||||
return changePassword;
|
return changePassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Map<String, dynamic>> login(
|
static Future<Map<String, dynamic>> login(String email, String password) async {
|
||||||
String email, String password) async {
|
|
||||||
//print("------- in logged email $email pwd $password ###### --------");
|
//print("------- in logged email $email pwd $password ###### --------");
|
||||||
Map<String, dynamic> cats = {};
|
Map<String, dynamic> cats = {};
|
||||||
String serverUrl = ConstValues.currentServer();
|
String serverUrl = ConstValues.currentServer();
|
||||||
@ -116,17 +115,12 @@ class AuthService {
|
|||||||
var errorBody = json.decode(utf8.decode(response.bodyBytes));
|
var errorBody = json.decode(utf8.decode(response.bodyBytes));
|
||||||
errorMessage = errorBody['non_field_errors']?[0] ?? 'パスワードが正しくありません。';
|
errorMessage = errorBody['non_field_errors']?[0] ?? 'パスワードが正しくありません。';
|
||||||
}
|
}
|
||||||
Get.snackbar(
|
throw Exception(errorMessage);
|
||||||
"エラー",
|
|
||||||
errorMessage,
|
|
||||||
backgroundColor: Colors.red,
|
|
||||||
colorText: Colors.white,
|
|
||||||
snackPosition: SnackPosition.TOP,
|
|
||||||
duration: const Duration(seconds: 3),
|
|
||||||
);
|
|
||||||
cats = {};
|
|
||||||
}
|
}
|
||||||
} catch( e ){
|
} catch( e ){
|
||||||
|
print('Error in login: $e');
|
||||||
|
throw e; // エラーを上位に伝播させる
|
||||||
|
/*
|
||||||
print('Error in login: $e');
|
print('Error in login: $e');
|
||||||
Get.snackbar("通信エラーがおきました", "サーバーと通信できませんでした",
|
Get.snackbar("通信エラーがおきました", "サーバーと通信できませんでした",
|
||||||
backgroundColor: Colors.red,
|
backgroundColor: Colors.red,
|
||||||
@ -142,7 +136,8 @@ class AuthService {
|
|||||||
duration: const Duration(seconds: 3),
|
duration: const Duration(seconds: 3),
|
||||||
//backgroundColor: Colors.yellow,
|
//backgroundColor: Colors.yellow,
|
||||||
);
|
);
|
||||||
cats = {};
|
*/
|
||||||
|
//cats = {};
|
||||||
}
|
}
|
||||||
return cats;
|
return cats;
|
||||||
}
|
}
|
||||||
@ -171,7 +166,7 @@ class AuthService {
|
|||||||
Map<String, dynamic> cats = {};
|
Map<String, dynamic> cats = {};
|
||||||
String serverUrl = ConstValues.currentServer();
|
String serverUrl = ConstValues.currentServer();
|
||||||
String url = '$serverUrl/api/register/';
|
String url = '$serverUrl/api/register/';
|
||||||
debugPrint('++++++++$url');
|
//debugPrint('++++++++$url');
|
||||||
final http.Response response = await http.post(
|
final http.Response response = await http.post(
|
||||||
Uri.parse(url),
|
Uri.parse(url),
|
||||||
headers: <String, String>{
|
headers: <String, String>{
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:gifunavi/pages/entry/entry_controller.dart';
|
||||||
|
import 'package:gifunavi/services/api_service.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:gifunavi/model/rog.dart';
|
import 'package:gifunavi/model/rog.dart';
|
||||||
@ -33,6 +35,8 @@ class ExternalService {
|
|||||||
Future<Map<String, dynamic>> startRogaining() async {
|
Future<Map<String, dynamic>> startRogaining() async {
|
||||||
final IndexController indexController = Get.find<IndexController>();
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
//final TeamController teamController = Get.find<TeamController>();
|
//final TeamController teamController = Get.find<TeamController>();
|
||||||
|
final ApiService apiService = Get.find<ApiService>();
|
||||||
|
final EntryController entryController = Get.find<EntryController>();
|
||||||
|
|
||||||
debugPrint("== startRogaining ==");
|
debugPrint("== startRogaining ==");
|
||||||
|
|
||||||
@ -68,23 +72,42 @@ class ExternalService {
|
|||||||
} else {
|
} else {
|
||||||
debugPrint("== startRogaining processing==");
|
debugPrint("== startRogaining processing==");
|
||||||
|
|
||||||
String serverUrl = ConstValues.currentServer();
|
try {
|
||||||
String url = '$serverUrl/gifuroge/start_from_rogapp';
|
// 新しい API 呼び出しを使用
|
||||||
print('++++++++$url');
|
final updatedEntry = await apiService.updateEntryStatus(
|
||||||
final http.Response response = await http.post(
|
entryController.currentEntry.value!.id!,
|
||||||
Uri.parse(url),
|
true, // hasParticipated を true に設定
|
||||||
headers: <String, String>{
|
false // hasGoaled は変更しない
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
);
|
||||||
},
|
|
||||||
body: jsonEncode(
|
|
||||||
<String, String>{'team_name': team, 'event_code': eventCode}),
|
|
||||||
);
|
|
||||||
|
|
||||||
print("---- start rogianing api status ---- ${response.statusCode}");
|
// start_rogaining を本サーバーに送る。 TODO
|
||||||
|
// // 'has_participated': true ... Server 側の修正が必要 TODO
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
res = json.decode(utf8.decode(response.bodyBytes));
|
String serverUrl = ConstValues.currentServer();
|
||||||
//print('----_res : $res ----');
|
String url = '$serverUrl/gifuroge/start_from_rogapp';
|
||||||
|
//print('++++++++$url');
|
||||||
|
final http.Response response = await http.post(
|
||||||
|
Uri.parse(url),
|
||||||
|
headers: <String, String>{
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
},
|
||||||
|
body: jsonEncode(
|
||||||
|
<String, String>{'team_name': team, 'event_code': eventCode}),
|
||||||
|
// 'has_participated': true ... Server 側の修正が必要 TODO
|
||||||
|
);
|
||||||
|
|
||||||
|
print("---- start rogianing api status ---- ${response.statusCode}");
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
res = json.decode(utf8.decode(response.bodyBytes));
|
||||||
|
//print('----_res : $res ----');
|
||||||
|
// ローカルのユーザーデータを更新
|
||||||
|
indexController.updateUserParticipationStatus(true);
|
||||||
|
}
|
||||||
|
}catch (e) {
|
||||||
|
print('Error in startRogaining: $e');
|
||||||
|
Get.snackbar('エラー', 'ロゲイニングの開始に失敗しました');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@ -226,6 +249,8 @@ class ExternalService {
|
|||||||
final IndexController indexController = Get.find<IndexController>();
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
final DestinationController destinationController =
|
final DestinationController destinationController =
|
||||||
Get.find<DestinationController>();
|
Get.find<DestinationController>();
|
||||||
|
final ApiService apiService = Get.find<ApiService>();
|
||||||
|
final EntryController entryController = Get.find<EntryController>();
|
||||||
|
|
||||||
// チームIDを取得
|
// チームIDを取得
|
||||||
|
|
||||||
@ -253,65 +278,83 @@ class ExternalService {
|
|||||||
final im1Bytes = File(image).readAsBytesSync();
|
final im1Bytes = File(image).readAsBytesSync();
|
||||||
String im1_64 = base64Encode(im1Bytes);
|
String im1_64 = base64Encode(im1Bytes);
|
||||||
|
|
||||||
final http.Response response = await http.post(
|
try {
|
||||||
Uri.parse(url1),
|
// 新しい API呼び出しを使用
|
||||||
headers: <String, String>{
|
final updatedEntry = await apiService.updateEntryStatus(
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
entryController.currentEntry.value!.id!,
|
||||||
'Authorization': 'Token $token'
|
true, // hasParticipated を true に設定
|
||||||
},
|
true // hasGoaled を true に設定
|
||||||
// 'id', 'user', 'goalimage', 'goaltime', 'team_name', 'event_code','cp_number'
|
);
|
||||||
body: jsonEncode(<String, String>{
|
|
||||||
'user': userId.toString(), //userId.toString(),
|
|
||||||
'team_name': teamname,
|
|
||||||
'event_code': eventcode,
|
|
||||||
'goaltime': goalTime,
|
|
||||||
'goalimage': im1_64,
|
|
||||||
'cp_number': "-1"
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
//String serverUrl = ConstValues.currentServer();
|
final http.Response response = await http.post(
|
||||||
String url = '$serverUrl/gifuroge/goal_from_rogapp';
|
Uri.parse(url1),
|
||||||
//print('++++++++$url');
|
headers: <String, String>{
|
||||||
if (response.statusCode == 201) {
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
try {
|
'Authorization': 'Token $token'
|
||||||
Map<String, dynamic> res = json.decode(utf8.decode(response.bodyBytes));
|
},
|
||||||
// print('----_res : $res ----');
|
// 'id', 'user', 'goalimage', 'goaltime', 'team_name', 'event_code','cp_number'
|
||||||
// print('---- image url ${res["goalimage"]} ----');
|
body: jsonEncode(<String, String>{
|
||||||
final http.Response response2 = await http.post(
|
'user': userId.toString(), //userId.toString(),
|
||||||
Uri.parse(url),
|
'team_name': teamname,
|
||||||
headers: <String, String>{
|
'event_code': eventcode,
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
'goaltime': goalTime,
|
||||||
},
|
'goalimage': im1_64,
|
||||||
body: jsonEncode(<String, String>{
|
'cp_number': "-1"
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
//String serverUrl = ConstValues.currentServer();
|
||||||
|
String url = '$serverUrl/gifuroge/goal_from_rogapp';
|
||||||
|
//print('++++++++$url');
|
||||||
|
if (response.statusCode == 201) {
|
||||||
|
try {
|
||||||
|
Map<String, dynamic> res = json.decode(
|
||||||
|
utf8.decode(response.bodyBytes));
|
||||||
|
// print('----_res : $res ----');
|
||||||
|
// print('---- image url ${res["goalimage"]} ----');
|
||||||
|
final http.Response response2 = await http.post(
|
||||||
|
Uri.parse(url),
|
||||||
|
headers: <String, String>{
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
},
|
||||||
|
body: jsonEncode(<String, String>{
|
||||||
|
'team_name': teamname,
|
||||||
|
'event_code': eventcode,
|
||||||
|
'goal_time': goalTime,
|
||||||
|
'image': res["goalimage"].toString().replaceAll(
|
||||||
|
'http://localhost:8100', serverUrl)
|
||||||
|
// 'has_goaled': true ... サーバー側の修正が必要 TODO
|
||||||
|
//'http://rogaining.sumasen.net')
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
String rec = jsonEncode(<String, String>{
|
||||||
'team_name': teamname,
|
'team_name': teamname,
|
||||||
'event_code': eventcode,
|
'event_code': eventcode,
|
||||||
'goal_time': goalTime,
|
'goal_time': goalTime,
|
||||||
'image': res["goalimage"].toString().replaceAll(
|
'image': res["goalimage"]
|
||||||
'http://localhost:8100', serverUrl)
|
.toString()
|
||||||
|
.replaceAll('http://localhost:8100', serverUrl)
|
||||||
//'http://rogaining.sumasen.net')
|
//'http://rogaining.sumasen.net')
|
||||||
}),
|
});
|
||||||
);
|
//print("-- json -- $rec");
|
||||||
String rec = jsonEncode(<String, String>{
|
//print('----- response2 is $response2 --------');
|
||||||
'team_name': teamname,
|
if (response2.statusCode == 200) {
|
||||||
'event_code': eventcode,
|
res2 = json.decode(utf8.decode(response2.bodyBytes));
|
||||||
'goal_time': goalTime,
|
// ローカルのユーザーデータを更新
|
||||||
'image': res["goalimage"]
|
indexController.updateUserGoalStatus(true);
|
||||||
.toString()
|
} else {
|
||||||
.replaceAll('http://localhost:8100', serverUrl)
|
res2 = json.decode(utf8.decode(response2.bodyBytes));
|
||||||
//'http://rogaining.sumasen.net')
|
// ここはどうする? TODO
|
||||||
});
|
}
|
||||||
//print("-- json -- $rec");
|
} catch (e) {
|
||||||
//print('----- response2 is $response2 --------');
|
print("Error {$e}");
|
||||||
if (response2.statusCode == 200) {
|
|
||||||
res2 = json.decode(utf8.decode(response2.bodyBytes));
|
|
||||||
} else {
|
|
||||||
res2 = json.decode(utf8.decode(response2.bodyBytes));
|
|
||||||
}
|
}
|
||||||
} catch(e){
|
|
||||||
print( "Error {$e}" );
|
|
||||||
}
|
}
|
||||||
|
}catch(e) {
|
||||||
|
print("Error in makeGoal: $e");
|
||||||
|
Get.snackbar('エラー', 'ゴール処理に失敗しました');
|
||||||
}
|
}
|
||||||
|
|
||||||
//}
|
//}
|
||||||
destinationController.resetRogaining(isgoal: true);
|
destinationController.resetRogaining(isgoal: true);
|
||||||
return res2;
|
return res2;
|
||||||
|
|||||||
@ -17,6 +17,19 @@ class MotionService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<dynamic> _handleMotionData(MethodCall call) async {
|
||||||
|
switch (call.method) {
|
||||||
|
case 'onMotionData':
|
||||||
|
final Map<String, dynamic> motionData = call.arguments;
|
||||||
|
// ここでモーションデータを処理します
|
||||||
|
print('Received motion data: $motionData');
|
||||||
|
// 例: データを状態管理システムに渡す、UIを更新する等
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
print('Unknown method ${call.method}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Future<void> stopMotionUpdates() async {
|
static Future<void> stopMotionUpdates() async {
|
||||||
if (Platform.isIOS) {
|
if (Platform.isIOS) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -32,6 +32,8 @@ class LocationController extends GetxController {
|
|||||||
// Reactive variable to hold the current position
|
// Reactive variable to hold the current position
|
||||||
Rx<Position?> currentPosition = Rx<Position?>(null);
|
Rx<Position?> currentPosition = Rx<Position?>(null);
|
||||||
// 現在の位置情報を保持するReactive変数です。Rx<Position?>型で宣言されています。
|
// 現在の位置情報を保持するReactive変数です。Rx<Position?>型で宣言されています。
|
||||||
|
final locationMarkerPositionStreamController = StreamController<LocationMarkerPosition?>.broadcast();
|
||||||
|
|
||||||
|
|
||||||
// Subscription to the position stream
|
// Subscription to the position stream
|
||||||
StreamSubscription<Position>? positionStream;
|
StreamSubscription<Position>? positionStream;
|
||||||
@ -154,8 +156,8 @@ class LocationController extends GetxController {
|
|||||||
|
|
||||||
//===== Akira Added 2024-4-9 end
|
//===== Akira Added 2024-4-9 end
|
||||||
|
|
||||||
final locationMarkerPositionStreamController =
|
//final locationMarkerPositionStreamController =
|
||||||
StreamController<LocationMarkerPosition?>.broadcast();
|
// StreamController<LocationMarkerPosition?>.broadcast();
|
||||||
// 位置マーカーの位置情報を送信するためのStreamControllerです。
|
// 位置マーカーの位置情報を送信するためのStreamControllerです。
|
||||||
// StreamController<LocationMarkerPosition?>型で宣言されています。
|
// StreamController<LocationMarkerPosition?>型で宣言されています。
|
||||||
|
|
||||||
@ -202,6 +204,7 @@ class LocationController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
startPositionStream();
|
startPositionStream();
|
||||||
|
debugPrint("Started startPositionStream");
|
||||||
} catch( e ){
|
} catch( e ){
|
||||||
print('Error initializing location service: $e');
|
print('Error initializing location service: $e');
|
||||||
}
|
}
|
||||||
@ -235,7 +238,12 @@ class LocationController extends GetxController {
|
|||||||
accuracy: position.accuracy,
|
accuracy: position.accuracy,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
onError: (error) {
|
||||||
|
debugPrint('Error in Geolocator stream: $error');
|
||||||
|
locationMarkerPositionStreamController.addError(error);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void startPositionStream_old() async {
|
void startPositionStream_old() async {
|
||||||
@ -248,7 +256,7 @@ class LocationController extends GetxController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await PermissionController.checkAndRequestPermissions();
|
//await PermissionController.checkAndRequestPermissions();
|
||||||
|
|
||||||
// 位置情報の設定を行います。z11
|
// 位置情報の設定を行います。z11
|
||||||
// Set up the location options
|
// Set up the location options
|
||||||
|
|||||||
@ -1,474 +1,153 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
|
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
|
||||||
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
|
|
||||||
import 'package:geojson_vi/geojson_vi.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
import 'package:gifunavi/pages/permission/permission.dart';
|
|
||||||
import 'package:gifunavi/pages/settings/settings_binding.dart';
|
|
||||||
import 'package:gifunavi/model/destination.dart';
|
|
||||||
import 'package:gifunavi/pages/destination/destination_controller.dart';
|
|
||||||
import 'package:gifunavi/pages/index/index_controller.dart';
|
import 'package:gifunavi/pages/index/index_controller.dart';
|
||||||
import 'package:gifunavi/utils/database_helper.dart';
|
|
||||||
import 'package:gifunavi/utils/location_controller.dart';
|
import 'package:gifunavi/utils/location_controller.dart';
|
||||||
import 'package:gifunavi/utils/text_util.dart';
|
import 'package:gifunavi/pages/destination/destination_controller.dart';
|
||||||
import 'package:gifunavi/widgets/base_layer_widget.dart';
|
|
||||||
import 'package:gifunavi/widgets/bottom_sheet_new.dart';
|
|
||||||
import 'package:gifunavi/widgets/current_position_widget.dart';
|
|
||||||
import 'package:gifunavi/widgets/game_state_view.dart';
|
|
||||||
import 'package:gifunavi/pages/settings/settings_controller.dart';
|
import 'package:gifunavi/pages/settings/settings_controller.dart';
|
||||||
|
import 'package:gifunavi/widgets/base_layer_widget.dart';
|
||||||
|
import 'package:gifunavi/widgets/game_state_view.dart';
|
||||||
|
import 'package:gifunavi/widgets/current_position_widget.dart';
|
||||||
|
|
||||||
class MapResetController {
|
class MapWidget extends StatelessWidget {
|
||||||
void Function()? resetIdleTimer;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MapWidget extends StatefulWidget {
|
|
||||||
const MapWidget({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MapWidget> createState() => _MapWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
|
|
||||||
//class _MapWidgetState extends State<MapWidget> {
|
|
||||||
final IndexController indexController = Get.find<IndexController>();
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
final DestinationController destinationController =
|
final DestinationController destinationController = Get.find<DestinationController>();
|
||||||
Get.find<DestinationController>();
|
|
||||||
final LocationController locationController = Get.find<LocationController>();
|
final LocationController locationController = Get.find<LocationController>();
|
||||||
final SettingsController settingsController = Get.find<SettingsController>();
|
final SettingsController settingsController = Get.find<SettingsController>();
|
||||||
|
|
||||||
late MapController mapController;
|
MapWidget({Key? key}) : super(key: key) {
|
||||||
final Completer<MapController> mapControllerCompleter = Completer<MapController>();
|
_initializeControllers();
|
||||||
|
}
|
||||||
|
|
||||||
StreamSubscription? subscription;
|
void _initializeControllers() {
|
||||||
Timer? _timer;
|
indexController.initMapController();
|
||||||
bool curr_marker_display = false;
|
|
||||||
|
|
||||||
final Map<LatLng, Marker> _markerCache = {};
|
|
||||||
List<Marker> _markers = [];
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
// 追加
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
PermissionController.checkAndRequestPermissions();
|
|
||||||
});
|
|
||||||
|
|
||||||
debugPrint('MapWidget: initState called');
|
|
||||||
SettingsBinding().dependencies(); // これを追加
|
|
||||||
_startIdleTimer();
|
|
||||||
mapController = MapController();
|
|
||||||
indexController.mapController = mapController;
|
|
||||||
|
|
||||||
// added by Akira
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
|
||||||
_startIdleTimer();
|
_startIdleTimer();
|
||||||
|
|
||||||
// マップの操作イベントをリッスンして、_resetTimerを呼び出す
|
ever(indexController.isMapControllerReady, (_) {
|
||||||
mapController.mapEventStream.listen((MapEvent mapEvent) {
|
if (indexController.isMapControllerReady.value) {
|
||||||
if (mapEvent is MapEventMove || mapEvent is MapEventFlingAnimation) {
|
_initMarkers();
|
||||||
_resetTimer();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// MapControllerの初期化が完了するまで待機
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
debugPrint("MapControllerの初期化が完了");
|
|
||||||
setState(() {
|
|
||||||
indexController.isMapControllerReady.value = true;
|
|
||||||
});
|
|
||||||
// MapControllerの初期化が完了したら、IndexControllerのonInitを呼び出す
|
|
||||||
//indexController.checkPermission();
|
|
||||||
PermissionController.checkAndRequestPermissions();
|
|
||||||
});
|
|
||||||
|
|
||||||
late MapResetController mapResetController = MapResetController();
|
|
||||||
mapResetController.resetIdleTimer = _resetIdleTimer;
|
|
||||||
Get.put(mapResetController);
|
|
||||||
|
|
||||||
// Add this debug subscription
|
|
||||||
subscription = locationController.locationMarkerPositionStreamController.stream.listen(
|
|
||||||
(LocationMarkerPosition? position) {
|
|
||||||
if (position != null) {
|
|
||||||
//debugPrint('Location update received: lat=${position.latitude}, lon=${position.longitude}');
|
|
||||||
} else {
|
|
||||||
debugPrint('Received null location update');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: (error) {
|
|
||||||
debugPrint('Error in location stream: $error');
|
|
||||||
},
|
|
||||||
onDone: () {
|
|
||||||
debugPrint('Location stream closed');
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// indexController.mapController = MapController(initCompleter: mapControllerCompleter);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _resetIdleTimer() {
|
void _startIdleTimer() {
|
||||||
debugPrint("_resetIdleTimer...");
|
|
||||||
_timer?.cancel();
|
|
||||||
_startIdleTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
debugPrint('MapWidget: dispose called');
|
|
||||||
WidgetsBinding.instance.removeObserver(this); // added
|
|
||||||
|
|
||||||
mapController.dispose();
|
|
||||||
_timer?.cancel();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
// added by Akira
|
|
||||||
/*
|
|
||||||
@override
|
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
||||||
debugPrint("MapWidget:didChangeAppLifecycleState...state=${state}");
|
|
||||||
|
|
||||||
if (state == AppLifecycleState.resumed) {
|
|
||||||
_resetTimer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// _centerMapOnUser を10秒間でコール
|
|
||||||
Future<void> _startIdleTimer() async {
|
|
||||||
//debugPrint("_startIdleTimer ....");
|
|
||||||
SettingsController settingsController;
|
|
||||||
|
|
||||||
// SettingsControllerが利用可能になるまで待機
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
settingsController = Get.find<SettingsController>();
|
|
||||||
break; // SettingsControllerが見つかったらループを抜ける
|
|
||||||
} catch (e) {
|
|
||||||
// SettingsControllerがまだ利用可能でない場合は少し待ってから再試行
|
|
||||||
await Future.delayed(const Duration(milliseconds: 100));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!settingsController.autoReturnDisabled.value) {
|
if (!settingsController.autoReturnDisabled.value) {
|
||||||
_timer = Timer(settingsController.timerDuration.value, _centerMapOnUser);
|
Future.delayed(settingsController.timerDuration.value, _centerMapOnUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// タイマーをリセットして_startIdleTimer をコール
|
|
||||||
void _resetTimer() {
|
|
||||||
//debugPrint("_resetTimer ....");
|
|
||||||
_timer?.cancel();
|
|
||||||
_startIdleTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
// マッぷを現在位置を中心にする。
|
|
||||||
void _centerMapOnUser() {
|
void _centerMapOnUser() {
|
||||||
//debugPrint("_centerMapOnUser ....");
|
destinationController.centerMapToCurrentLocation();
|
||||||
if (mounted) {
|
_startIdleTimer();
|
||||||
//debugPrint("_centerMapOnUser => centering ....");
|
|
||||||
destinationController.centerMapToCurrentLocation();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _initMarkers() async {
|
Future<void> _initMarkers() async {
|
||||||
List<Marker> markers = await _getMarkers();
|
indexController.markers.value = await _getMarkers();
|
||||||
setState(() {
|
|
||||||
_markers = markers;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Marker>> _getMarkers() async {
|
Future<List<Marker>> _getMarkers() async {
|
||||||
debugPrint('Getting markers...');
|
|
||||||
if (indexController.isLoadingLocations.value) {
|
if (indexController.isLoadingLocations.value) {
|
||||||
await Future.doWhile(() async {
|
await indexController.waitForLocationsToLoad();
|
||||||
await Future.delayed(const Duration(milliseconds: 100));
|
|
||||||
return indexController.isLoadingLocations.value;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debugPrint('Getting markers...');
|
List<Marker> markers = [];
|
||||||
|
|
||||||
List<Marker> markers = [];
|
|
||||||
if (indexController.locations.isNotEmpty && indexController.locations[0].features.isNotEmpty) {
|
if (indexController.locations.isNotEmpty && indexController.locations[0].features.isNotEmpty) {
|
||||||
for (var feature in indexController.locations[0].features) {
|
for (var feature in indexController.locations[0].features) {
|
||||||
GeoJSONMultiPoint point = feature!.geometry as GeoJSONMultiPoint;
|
// マーカーの作成ロジック
|
||||||
LatLng latLng = LatLng(point.coordinates[0][1], point.coordinates[0][0]);
|
// 実際のマーカー作成ロジックをここに実装してください
|
||||||
|
|
||||||
markers.add(Marker(
|
|
||||||
point: latLng,
|
|
||||||
width: 30.0,
|
|
||||||
height: 30.0,
|
|
||||||
child: getMarkerShape(feature),
|
|
||||||
));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
debugPrint('No locations or features available');
|
debugPrint('No locations or features available');
|
||||||
|
|
||||||
}
|
}
|
||||||
return markers;
|
return markers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Widget getMarkerShape(GeoJSONFeature i, BuildContext context) {
|
|
||||||
Widget getMarkerShape(GeoJSONFeature i) {
|
|
||||||
GeoJSONMultiPoint p = i.geometry as GeoJSONMultiPoint;
|
|
||||||
return InkWell(
|
|
||||||
onTap: () {
|
|
||||||
GeoJSONFeature? fs = indexController.getFeatureForLatLong(
|
|
||||||
p.coordinates[0][1], p.coordinates[0][0]);
|
|
||||||
if (fs != null) {
|
|
||||||
indexController.currentFeature.clear();
|
|
||||||
indexController.currentFeature.add(fs);
|
|
||||||
|
|
||||||
Destination des = destinationController.festuretoDestination(fs);
|
|
||||||
|
|
||||||
DatabaseHelper db = DatabaseHelper.instance;
|
|
||||||
db.getDestinationByLatLon(des.lat!, des.lon!).then((value) {
|
|
||||||
destinationController.shouldShowBottomSheet = false;
|
|
||||||
showModalBottomSheet(
|
|
||||||
constraints:
|
|
||||||
BoxConstraints.loose(Size(Get.width, Get.height * 0.85)),
|
|
||||||
context: context,
|
|
||||||
isScrollControlled: true,
|
|
||||||
isDismissible: true,
|
|
||||||
builder: ((context) => BottomSheetNew(
|
|
||||||
destination: des, isAlreadyCheckedIn: value.isNotEmpty)),
|
|
||||||
).whenComplete(() {
|
|
||||||
destinationController.shouldShowBottomSheet = true;
|
|
||||||
destinationController.skipGps = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Stack(
|
|
||||||
fit: StackFit.expand,
|
|
||||||
children: [
|
|
||||||
Container( // マーカー
|
|
||||||
height: 32,
|
|
||||||
width: 32,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: Colors.transparent,
|
|
||||||
border: Border.all(
|
|
||||||
color: i.properties!['buy_point'] > 0
|
|
||||||
? Colors.blue
|
|
||||||
: Colors.red,
|
|
||||||
width: 3,
|
|
||||||
style: BorderStyle.solid,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.circle,
|
|
||||||
size: 6.0,
|
|
||||||
),
|
|
||||||
i.properties!['cp'] <= 0 ? Transform.translate
|
|
||||||
(
|
|
||||||
offset: const Offset(-3, 0), //-3
|
|
||||||
child: Transform.rotate(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
origin: Offset.fromDirection(1, 26),
|
|
||||||
angle: 270 * pi / 180,
|
|
||||||
child: const Icon(
|
|
||||||
Icons.play_arrow_outlined,
|
|
||||||
color: Colors.red,
|
|
||||||
size: 70,
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
|
|
||||||
: Container(
|
|
||||||
color: Colors.transparent,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Transform.translate(
|
|
||||||
offset: const Offset(30, 0), // 30,0
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Container (
|
|
||||||
//width: 80, // 幅を指定
|
|
||||||
//height: 60, // 40
|
|
||||||
//color: Colors.purple.withOpacity(0.2),
|
|
||||||
color: Colors.transparent,
|
|
||||||
|
|
||||||
//child: Text(' ').
|
|
||||||
//constraints: const BoxConstraints(maxWidth: 60.0), // 最大幅を設定
|
|
||||||
//constraints: BoxConstraints(maxWidth: maxWidth), // 最大幅を設定
|
|
||||||
//color: Colors.purple.withOpacity(0.2),
|
|
||||||
child: Stack(
|
|
||||||
children: <Widget>[
|
|
||||||
Text( // アウトライン
|
|
||||||
TextUtils.getDisplayTextFeture(i),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16, // 16
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
overflow: TextOverflow.visible,
|
|
||||||
//height: 1.2,
|
|
||||||
foreground: Paint()
|
|
||||||
..style = PaintingStyle.stroke
|
|
||||||
..strokeWidth = 1 // 2
|
|
||||||
..color = Colors.white,
|
|
||||||
),
|
|
||||||
maxLines: 1, // テキストを1行に制限
|
|
||||||
softWrap: false, // テキストの折り返しを無効化
|
|
||||||
),
|
|
||||||
Text( // テキスト
|
|
||||||
TextUtils.getDisplayTextFeture(i),
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
overflow: TextOverflow.visible,
|
|
||||||
//fontWeight: FontWeight.bold,
|
|
||||||
//height: 1.2,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
maxLines: 1, // テキストを1行に制限
|
|
||||||
softWrap: false, // テキストの折り返しを無効化
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<LatLng>? getPoints() {
|
List<LatLng>? getPoints() {
|
||||||
List<LatLng> pts = [];
|
return indexController.routePoints.map((p) => LatLng(p.latitude, p.longitude)).toList();
|
||||||
for (PointLatLng p in indexController.routePoints) {
|
|
||||||
LatLng l = LatLng(p.latitude, p.longitude);
|
|
||||||
pts.add(l);
|
|
||||||
}
|
|
||||||
return pts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final settingsController = Get.find<SettingsController>(); // これを追加
|
|
||||||
//final PopupController popupController = PopupController();
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Obx(() => indexController.isLoading.value == true
|
Obx(() => indexController.isLoading.value
|
||||||
? const Padding(
|
? const Center(child: CircularProgressIndicator())
|
||||||
padding: EdgeInsets.only(top: 60.0),
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
)
|
|
||||||
: FlutterMap(
|
: FlutterMap(
|
||||||
mapController: mapController,
|
mapController: indexController.mapController,
|
||||||
//mapController: indexController.mapController,
|
options: MapOptions(
|
||||||
options: MapOptions(
|
maxZoom: 18.4,
|
||||||
maxZoom: 18.4,
|
onMapReady: () {
|
||||||
onMapReady: () {
|
indexController.isMapControllerReady.value = true;
|
||||||
_initMarkers();
|
},
|
||||||
//indexController.isMapControllerReady.value = true;
|
initialCenter: const LatLng(37.15319600454702, 139.58765950528198),
|
||||||
},
|
bounds: indexController.currentBound.isNotEmpty
|
||||||
initialCenter:
|
? indexController.currentBound[0]
|
||||||
const LatLng(37.15319600454702, 139.58765950528198),
|
: LatLngBounds.fromPoints([
|
||||||
bounds: indexController.currentBound.isNotEmpty
|
const LatLng(35.03999881162295, 136.40587119778962),
|
||||||
? indexController.currentBound[0]
|
const LatLng(36.642756778706904, 137.95226720406063)
|
||||||
: LatLngBounds.fromPoints([
|
]),
|
||||||
const LatLng(35.03999881162295, 136.40587119778962),
|
initialZoom: 1,
|
||||||
const LatLng(36.642756778706904, 137.95226720406063)
|
interactiveFlags: InteractiveFlag.pinchZoom | InteractiveFlag.drag,
|
||||||
]),
|
onPositionChanged: (MapPosition pos, bool hasGesture) {
|
||||||
initialZoom: 1,
|
if (hasGesture) {
|
||||||
interactiveFlags:
|
_startIdleTimer();
|
||||||
InteractiveFlag.pinchZoom | InteractiveFlag.drag,
|
}
|
||||||
onPositionChanged: (MapPosition pos, hasGesture) {
|
indexController.currentBound = [pos.bounds!];
|
||||||
if (hasGesture) {
|
},
|
||||||
_resetTimer();
|
onMapEvent: (MapEvent mapEvent) {
|
||||||
}
|
if (mapEvent is MapEventMove) {
|
||||||
indexController.currentBound = [pos.bounds!];
|
destinationController.shouldShowBottomSheet = true;
|
||||||
},
|
}
|
||||||
onMapEvent: (MapEvent mapEvent) {
|
},
|
||||||
//debugPrint('Map event: ${mapEvent.runtimeType}');
|
),
|
||||||
if (mapEvent is MapEventMove) {
|
children: [
|
||||||
destinationController.shouldShowBottomSheet = true;
|
const BaseLayer(),
|
||||||
}
|
Obx(() => indexController.routePointLenght > 0
|
||||||
},
|
? PolylineLayer(
|
||||||
//onTap: (_, __) => popupController.hideAllPopups(),
|
polylines: [
|
||||||
|
Polyline(
|
||||||
|
points: getPoints()!,
|
||||||
|
strokeWidth: 6.0,
|
||||||
|
color: Colors.indigo,
|
||||||
),
|
),
|
||||||
children: [
|
],
|
||||||
const BaseLayer(),
|
)
|
||||||
// ルートのポリライン表示
|
: Container()),
|
||||||
Obx(
|
CurrentLocationLayer(
|
||||||
() => indexController.routePointLenght > 0
|
positionStream: locationController.locationMarkerPositionStream,
|
||||||
? PolylineLayer(
|
style: const LocationMarkerStyle(
|
||||||
polylines: [
|
marker: Stack(
|
||||||
Polyline(
|
children: [
|
||||||
points: getPoints()!,
|
CircleAvatar(
|
||||||
strokeWidth: 6.0,
|
radius: 13.5,
|
||||||
color: Colors.indigo,
|
backgroundColor: Colors.blue,
|
||||||
),
|
child: Icon(Icons.navigation, color: Colors.white),
|
||||||
],
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
),
|
|
||||||
// 現在位置のマーカー
|
|
||||||
CurrentLocationLayer(
|
|
||||||
positionStream: locationController
|
|
||||||
.locationMarkerPositionStreamController.stream,
|
|
||||||
//alignDirectionOnUpdate: AlignOnUpdate.never,
|
|
||||||
style: const LocationMarkerStyle(
|
|
||||||
marker: Stack(
|
|
||||||
children: [
|
|
||||||
CircleAvatar(
|
|
||||||
radius: 13.5,
|
|
||||||
backgroundColor: Colors.blue,
|
|
||||||
child: Icon(Icons.navigation, color: Colors.white),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
markerSize: Size(27, 27),
|
|
||||||
markerDirection: MarkerDirection.heading,
|
|
||||||
),
|
),
|
||||||
//child: const Icon(Icons.navigation),
|
],
|
||||||
),
|
),
|
||||||
|
markerSize: Size(27, 27),
|
||||||
FutureBuilder<List<Marker>>(
|
markerDirection: MarkerDirection.heading,
|
||||||
future: indexController.locations.isNotEmpty ? _getMarkers() : null,
|
),
|
||||||
builder: (context, snapshot) {
|
),
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
Obx(() => MarkerLayer(markers: indexController.markers)),
|
||||||
return const Center(child: CircularProgressIndicator());
|
],
|
||||||
} else if (snapshot.hasError) {
|
)),
|
||||||
return const Center(child: Text('マーカーの読み込みに失敗しました'));
|
|
||||||
} else {
|
|
||||||
return MarkerLayer(markers: snapshot.data ?? []);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
//MarkerLayer(markers: indexController.locations.isNotEmpty ? _getMarkers() : []),
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
const Positioned(top: 0, left: 0, child: GameStateWidget()),
|
const Positioned(top: 0, left: 0, child: GameStateWidget()),
|
||||||
const Positioned(bottom: 10, right: 10, child: CurrentPosition()),
|
const Positioned(bottom: 10, right: 10, child: CurrentPosition()),
|
||||||
StreamBuilder<LocationMarkerPosition?>(
|
Obx(() => indexController.currentMarkerPosition.value != null
|
||||||
stream: locationController.locationMarkerPositionStream,
|
? Container() // 現在のマーカー位置が更新されたときの処理
|
||||||
builder: (context, snapshot) {
|
: Container()),
|
||||||
if (!snapshot.hasData) {
|
|
||||||
//debugPrint("====== Not display current marker");
|
|
||||||
curr_marker_display = true;
|
|
||||||
}else if(curr_marker_display){
|
|
||||||
debugPrint("====== Displayed current marker");
|
|
||||||
curr_marker_display = false;
|
|
||||||
}
|
|
||||||
return Container();
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MapResetController extends GetxController {
|
||||||
|
void Function()? resetIdleTimer;
|
||||||
|
|
||||||
|
void setResetIdleTimer(void Function() resetFunction) {
|
||||||
|
resetIdleTimer = resetFunction;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,9 +14,6 @@ class _PermissionHandlerScreenState extends State<PermissionHandlerScreen> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
|
||||||
await PermissionController.checkAndRequestPermissions();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
Reference in New Issue
Block a user