re factor rog

This commit is contained in:
2024-04-01 09:26:56 +05:30
parent dd36ab8399
commit edbf52825b
54 changed files with 2597 additions and 435 deletions

View File

@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:rogapp/features/home/home_page.dart';
import 'package:rogapp/features/services/auth_repo.dart';
import 'package:rogapp/features/state/user_state.dart';
class LoginPage extends ConsumerStatefulWidget {
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends ConsumerState<LoginPage> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
bool _isLoading = false; // Track loading state
void _login() {
setState(() {
_isLoading = true; // Start loading
});
String email = _emailController.text.trim();
String password = _passwordController.text;
ref.read(authProvider("$email|$password").future).then((user) {
setState(() {
_isLoading = false; // Stop loading
});
if (user != null) {
ref.read(userNotifierProvider.notifier).setUser(user);
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => const HomePage(),
));
} else {
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(content: Text('Login failed')));
}
}).catchError((error) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text('Error: $error')));
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Login')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextFormField(
controller: _emailController,
decoration: const InputDecoration(labelText: 'Email'),
),
TextFormField(
controller: _passwordController,
decoration: const InputDecoration(labelText: 'Password'),
obscureText: true,
),
_isLoading
? CircularProgressIndicator()
: ElevatedButton(
onPressed: _login,
child: const Text('Login'),
),
],
),
),
);
}
}

View File

@ -0,0 +1,397 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:convert';
class FeatureCollection {
final String type;
final List<Feature> features;
FeatureCollection({required this.type, required this.features});
factory FeatureCollection.fromJson(Map<String, dynamic> json) {
return FeatureCollection(
type: json['type'],
features:
List<Feature>.from(json['features'].map((x) => Feature.fromJson(x))),
);
}
}
class Feature {
final int id;
final String type;
final Geometry geometry;
final Properties properties;
Feature(
{required this.id,
required this.type,
required this.geometry,
required this.properties});
factory Feature.fromJson(Map<String, dynamic> json) {
return Feature(
id: json['id'],
type: json['type'],
geometry: Geometry.fromJson(json['geometry']),
properties: Properties.fromJson(json['properties']),
);
}
String toJson() => json.encode(toMap());
Map<String, dynamic> toMap() {
return {
'id': id,
'type': type,
'geometry': geometry.toMap(), // Assuming Geometry has a toMap method
'properties':
properties.toMap(), // Assuming Properties has a toMap method
};
}
Feature.fromMap(Map<String, dynamic> map)
: id = map['id'],
type = map['type'],
geometry = Geometry.fromMap(map['geometry']),
properties = Properties.fromJson(map['properties']);
static empty() {}
}
class Geometry {
final String type;
final List<List<double>> coordinates;
Geometry({required this.type, required this.coordinates});
Geometry copyWith({
String? type,
List<List<double>>? coordinates,
}) {
return Geometry(
type: type ?? this.type,
coordinates: coordinates ?? this.coordinates,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'type': type,
'coordinates': coordinates,
};
}
factory Geometry.fromMap(Map<String, dynamic> map) {
return Geometry(
type: map['type'] as String,
coordinates: List<List<double>>.from(
(map['coordinates'] as List<dynamic>).map<List<double>>(
(coordinateList) {
// Ensure 'coordinateList' is treated as a List of dynamic elements.
// Then, for each element in the list, explicitly cast or convert it to double.
return List<double>.from(coordinateList.map((coordinate) {
// 'coordinate' is dynamic, needs explicit conversion to double.
// The conversion depends on the original data type of 'coordinate';
// if it's already a number, you can use .toDouble(); if it's a String, you might need double.parse.
return double.parse(coordinate.toString());
}));
},
),
),
);
}
String toJson() => json.encode(toMap());
factory Geometry.fromJson(Map<String, dynamic> source) {
final geom = Geometry.fromMap(source);
return geom;
}
get latitude => coordinates[0][1];
get longitude => coordinates[0][0];
}
class Properties {
// Include all properties fields
final int locationId;
final String subLocId;
final double cp;
final String? name;
final String? address;
final String? phone;
final String? email;
final String? webcontents;
final String? videos;
final String? category;
final int? series;
final double? lat;
final double? lon;
final int? listOrder;
final String? photos;
final double? checkinRadious;
final bool? autoCheckin;
bool? selected = false;
bool? checkedin = false;
final double? checkinPoint;
final double? buyPoint;
final bool? hiddenLocation;
String? checkinImage;
String? buypointImage;
bool? forcedCheckin = false;
final String? tags;
Properties(
{required this.locationId,
required this.subLocId,
required this.cp,
this.name,
this.address,
this.phone,
this.email,
this.webcontents,
this.videos,
this.category,
this.series,
this.lat,
this.lon,
this.listOrder,
this.photos,
this.checkinRadious,
this.autoCheckin,
this.selected,
this.checkedin,
this.checkinPoint,
this.buyPoint,
this.hiddenLocation,
this.checkinImage,
this.buypointImage,
this.forcedCheckin,
this.tags});
factory Properties.fromJson(Map<String, dynamic> json) {
Properties prop;
try {
prop = Properties(
locationId: json['location_id'],
subLocId: json['sub_loc_id'],
cp: json['cp'],
name: json['name'] ?? '',
address: json['address'] ?? '',
phone: json['phone'] ?? '',
email: json['email'] ?? '',
webcontents: json['webcontents'] ?? '',
videos: json['videos'] ?? '',
category: json['category'] ?? '',
series: json['series'] ?? 0,
lat: json['lat'] ?? 0.0,
lon: json['lon'] ?? 0.0,
listOrder: json['list_order'] ?? 0,
photos: json['photos'] ?? '',
checkinRadious: json['checkin_radious'] ?? 0.0,
autoCheckin: json['auto_checkin'] == 0 ? false : true,
selected: json['selected'] == 0 ? false : true,
checkedin: json['checkedin'] == 0 ? false : true,
checkinPoint: json['checkin_point'] ?? 0.0,
buyPoint: json['buy_point'] ?? 0.0,
hiddenLocation: json['hidden_location'] == 0 ? false : true,
checkinImage: json['checkin_image'] ?? '',
buypointImage: json['buypoint_image'] ?? '',
forcedCheckin: json['forced_checkin'] == 0 ? false : true,
tags: json['tags'] ?? '');
//print("from json --- $prop");
} catch (e) {
//print("from json --- $e");
return Properties(locationId: 0, subLocId: '', cp: 0.0, name: '');
}
return prop;
}
factory Properties.toJson(Properties prop) {
return Properties(
locationId: prop.locationId,
subLocId: prop.subLocId,
cp: prop.cp,
name: prop.name,
address: prop.address,
phone: prop.phone,
email: prop.email,
webcontents: prop.webcontents,
videos: prop.videos,
category: prop.category,
series: prop.series,
lat: prop.lat,
lon: prop.lon,
listOrder: prop.listOrder,
photos: prop.photos,
checkinRadious: prop.checkinRadious,
autoCheckin: prop.autoCheckin,
selected: prop.selected,
checkedin: prop.checkedin,
checkinPoint: prop.checkinPoint,
buyPoint: prop.buyPoint,
hiddenLocation: prop.hiddenLocation,
checkinImage: prop.checkinImage,
buypointImage: prop.buypointImage,
forcedCheckin: prop.forcedCheckin,
tags: prop.tags);
}
Properties copyWith({
int? locationId,
String? subLocId,
double? cp,
String? name,
String? address,
String? phone,
String? email,
String? webcontents,
String? videos,
String? category,
int? series,
double? lat,
double? lon,
int? listOrder,
String? photos,
double? checkinRadious,
bool? autoCheckin,
bool? selected,
bool? checkedin,
double? checkinPoint,
double? buyPoint,
bool? hiddenLocation,
String? checkinImage,
String? buypointImage,
bool? forcedCheckin,
String? tags,
}) {
return Properties(
locationId: locationId ?? this.locationId,
subLocId: subLocId ?? this.subLocId,
cp: cp ?? this.cp,
name: name ?? this.name,
address: address ?? this.address,
phone: phone ?? this.phone,
email: email ?? this.email,
webcontents: webcontents ?? this.webcontents,
videos: videos ?? this.videos,
category: category ?? this.category,
series: series ?? this.series,
lat: lat ?? this.lat,
lon: lon ?? this.lon,
listOrder: listOrder ?? this.listOrder,
photos: photos ?? this.photos,
checkinRadious: checkinRadious ?? this.checkinRadious,
autoCheckin: autoCheckin ?? this.autoCheckin,
selected: selected ?? this.selected,
checkedin: checkedin ?? this.checkedin,
checkinPoint: checkinPoint ?? this.checkinPoint,
buyPoint: buyPoint ?? this.buyPoint,
hiddenLocation: hiddenLocation ?? this.hiddenLocation,
checkinImage: checkinImage ?? this.checkinImage,
buypointImage: buypointImage ?? this.buypointImage,
forcedCheckin: forcedCheckin ?? this.forcedCheckin,
tags: tags ?? this.tags,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'locationId': locationId,
'subLocId': subLocId,
'cp': cp,
'name': name,
'address': address,
'phone': phone,
'email': email,
'webcontents': webcontents,
'videos': videos,
'category': category,
'series': series,
'lat': lat,
'lon': lon,
'listOrder': listOrder,
'photos': photos,
'checkinRadious': checkinRadious,
'autoCheckin': autoCheckin,
'selected': selected,
'checkedin': checkedin,
'checkinPoint': checkinPoint,
'buyPoint': buyPoint,
'hiddenLocation': hiddenLocation,
'checkinImage': checkinImage,
'buypointImage': buypointImage,
'forcedCheckin': forcedCheckin,
'tags': tags,
};
}
String toJson() => json.encode(toMap());
@override
String toString() {
return 'Properties(locationId: $locationId, subLocId: $subLocId, cp: $cp, name: $name, address: $address, phone: $phone, email: $email, webcontents: $webcontents, videos: $videos, category: $category, series: $series, lat: $lat, lon: $lon, listOrder: $listOrder, photos: $photos, checkinRadious: $checkinRadious, autoCheckin: $autoCheckin, selected: $selected, checkedin: $checkedin, checkinPoint: $checkinPoint, buyPoint: $buyPoint, hiddenLocation: $hiddenLocation, checkinImage: $checkinImage, buypointImage: $buypointImage, forcedCheckin: $forcedCheckin, tags: $tags)';
}
@override
bool operator ==(covariant Properties other) {
if (identical(this, other)) return true;
return other.locationId == locationId &&
other.subLocId == subLocId &&
other.cp == cp &&
other.name == name &&
other.address == address &&
other.phone == phone &&
other.email == email &&
other.webcontents == webcontents &&
other.videos == videos &&
other.category == category &&
other.series == series &&
other.lat == lat &&
other.lon == lon &&
other.listOrder == listOrder &&
other.photos == photos &&
other.checkinRadious == checkinRadious &&
other.autoCheckin == autoCheckin &&
other.selected == selected &&
other.checkedin == checkedin &&
other.checkinPoint == checkinPoint &&
other.buyPoint == buyPoint &&
other.hiddenLocation == hiddenLocation &&
other.checkinImage == checkinImage &&
other.buypointImage == buypointImage &&
other.forcedCheckin == forcedCheckin &&
other.tags == tags;
}
@override
int get hashCode {
return locationId.hashCode ^
subLocId.hashCode ^
cp.hashCode ^
name.hashCode ^
address.hashCode ^
phone.hashCode ^
email.hashCode ^
webcontents.hashCode ^
videos.hashCode ^
category.hashCode ^
series.hashCode ^
lat.hashCode ^
lon.hashCode ^
listOrder.hashCode ^
photos.hashCode ^
checkinRadious.hashCode ^
autoCheckin.hashCode ^
selected.hashCode ^
checkedin.hashCode ^
checkinPoint.hashCode ^
buyPoint.hashCode ^
hiddenLocation.hashCode ^
checkinImage.hashCode ^
buypointImage.hashCode ^
forcedCheckin.hashCode ^
tags.hashCode;
}
}

View File

@ -0,0 +1,82 @@
import 'dart:convert';
class CheckpointVisit {
final String id;
final String checkpointId;
final String teamName;
final String userName;
final String eventCode;
final DateTime visitTime;
CheckpointVisit({
required this.id,
required this.checkpointId,
required this.teamName,
required this.userName,
required this.eventCode,
required this.visitTime,
});
CheckpointVisit copyWith({
String? id,
String? checkpointId,
String? teamName,
String? userName,
String? eventCode,
DateTime? visitTime,
}) {
return CheckpointVisit(
id: id ?? this.id,
checkpointId: checkpointId ?? this.checkpointId,
teamName: teamName ?? this.teamName,
userName: userName ?? this.userName,
eventCode: eventCode ?? this.eventCode,
visitTime: visitTime ?? this.visitTime,
);
}
Map<String, dynamic> toMap() {
return {
'id': id,
'checkpointId': checkpointId,
'teamName': teamName,
'userName': userName,
'eventCode': eventCode,
'visitTime': visitTime.millisecondsSinceEpoch,
};
}
factory CheckpointVisit.fromMap(Map<String, dynamic> map) {
return CheckpointVisit(
id: map['id'],
checkpointId: map['checkpointId'],
teamName: map['teamName'],
userName: map['userName'],
eventCode: map['eventCode'],
visitTime: DateTime.fromMillisecondsSinceEpoch(map['visitTime']),
);
}
String toJson() => json.encode(toMap());
factory CheckpointVisit.fromJson(String source) =>
CheckpointVisit.fromMap(json.decode(source));
@override
String toString() {
return 'CheckpointVisit(id: $id, checkpointId: $checkpointId, teamName: $teamName, userName: $userName, eventCode: $eventCode, visitTime: $visitTime)';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is CheckpointVisit &&
other.id == id &&
other.checkpointId == checkpointId &&
other.teamName == teamName &&
other.userName == userName &&
other.eventCode == eventCode &&
other.visitTime == visitTime;
}
}

View File

@ -0,0 +1,329 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:rogapp/features/data/checkpoint.dart';
import 'package:rogapp/features/data/user_location.dart';
final dataProviderProvider = Provider<DataProvider>((ref) {
return DataProvider();
});
// Convert a Feature object to a Map
Map<String, dynamic> featureToMap(Feature feature) {
var properties = feature.properties;
try {
return {
'id': feature.id,
'type': feature.type,
'name': properties.name ?? '',
'address': properties.address ?? '',
'phone': properties.phone ?? '',
'email': properties.email ?? '',
'webcontents': properties.webcontents ?? '',
'videos': properties.videos ?? '',
'category': properties.category ?? '',
'series': properties.series,
'lat': feature.geometry.latitude,
'lon': feature.geometry.longitude,
'list_order': properties.listOrder,
'photos': properties.photos ?? '',
'checkin_radious': properties.checkinRadious,
'sub_loc_id': properties.subLocId,
'auto_checkin': properties.autoCheckin! ? 1 : 0,
'selected': properties.selected == null
? 0
: properties.selected!
? 1
: 0,
'checkedin': properties.checkedin == null
? 0
: properties.checkedin!
? 1
: 0,
'cp': properties.cp,
'checkin_point': properties.checkinPoint,
'buy_point': properties.buyPoint,
'hidden_location': properties.hiddenLocation,
'checkin_image': properties.checkinImage,
'buypoint_image': properties.buypointImage,
'forced_checkin': properties.forcedCheckin == null
? 0
: properties.forcedCheckin!
? 1
: 0,
'tags': properties.tags ?? '',
'location_id': properties.locationId,
};
} catch (e) {
//print("--- feature to map Error: $e");
return {};
}
}
// Convert a Map to a Feature object
Feature mapToFeature(Map<String, dynamic> map) {
Feature feat;
//print("mapToFeature id --- ${Properties.fromJson(map)}");
//print("mapToFeature --- ${map['type']}");
feat = Feature(
id: map['id'],
type: map['type'],
geometry: Geometry(
type: map['type'] ?? 'Point',
coordinates: [
[map['lon'], map['lat']]
],
),
properties: Properties.fromJson(map),
);
//print("mapToFeature --- $feat");
return feat;
}
class DataProvider {
static Database? _database;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await initDB();
return _database!;
}
initDB() async {
var documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "GameDB.db");
return await openDatabase(path,
version: 1, onOpen: (db) {}, onCreate: _onCreate);
}
_onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE Checkpoints (
location_id INTEGER PRIMARY KEY,
id INTEGER,
type TEXT,
name TEXT,
address TEXT,
phone TEXT,
email TEXT,
webcontents TEXT,
videos TEXT,
category TEXT,
series INTEGER,
lat REAL,
lon REAL,
list_order INTEGER,
photos TEXT,
checkin_radious REAL,
sub_loc_id TEXT,
auto_checkin INTEGER,
selected INTEGER,
checkedin INTEGER,
cp REAL,
checkin_point REAL,
buy_point REAL,
hidden_location INTEGER,
checkin_image TEXT,
buypoint_image TEXT,
forced_checkin INTEGER,
recipt_times INTEGER,
tags TEXT
)
''');
await db.execute('''
CREATE TABLE GPSLocations (
timestamp TEXT PRIMARY KEY,
latitude REAL,
longitude REAL,
event_code TEXT,
team_name TEXT,
user_name TEXT,
attempt_count INTEGER
)
''');
await db.execute('''
CREATE TABLE GameState (
key TEXT PRIMARY KEY,
value TEXT,
event_code TEXT,
team_name TEXT,
user_name TEXT,
attempt_count INTEGER
)
''');
await db.execute('''
CREATE TABLE CheckpointVisits (
id INTEGER PRIMARY KEY AUTOINCREMENT,
checkpointId INTEGER,
photoTaken BOOLEAN,
receiptPhotoTaken BOOLEAN,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
event_code TEXT,
team_name TEXT,
user_name TEXT,
attempt_count INTEGER DEFAULT 1,
FOREIGN KEY (checkpointId) REFERENCES Checkpoints(location_id)
)
''');
}
Future<void> recordCheckpointVisit(
int locationId, String userName, String teamName, String eventCode,
{bool photoTaken = false, bool? receiptPhotoTaken}) async {
final db = await database;
// Fetching buy_point to determine if a receipt is required
var checkpointQueryResult = await db.query(
'Checkpoints',
columns: ['buy_point'],
where: 'location_id = ?',
whereArgs: [locationId],
);
var buyPoint = checkpointQueryResult.isNotEmpty
? checkpointQueryResult.first['buy_point'] as double?
: null;
var requiresReceipt = buyPoint != null && buyPoint > 0.0;
await db.insert(
'CheckpointVisits',
{
'checkpointId': locationId,
'user_name': userName,
'team_name': teamName,
'event_code': eventCode,
'photoTaken': photoTaken ? 1 : 0,
'receiptPhotoTaken':
requiresReceipt ? (receiptPhotoTaken == true ? 1 : 0) : null,
},
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<Map<String, dynamic>> getIncompleteTasks(int locationId,
String userName, String teamName, String eventCode) async {
final db = await database;
var checkpoint = await db.query(
'Checkpoints',
where: 'location_id = ?',
whereArgs: [locationId],
);
var buyPointValue =
checkpoint.isNotEmpty ? checkpoint.first['buy_point'] as double? : null;
var requiresReceipt = buyPointValue != null && buyPointValue > 0.0;
// Fetch visits considering all relevant identifiers
List<Map> visits = await db.query(
'CheckpointVisits',
where:
'checkpointId = ? AND user_name = ? AND team_name = ? AND event_code = ?',
whereArgs: [locationId, userName, teamName, eventCode],
);
if (visits.isNotEmpty) {
var lastVisit = visits.last;
return {
'photoTaken': lastVisit['photoTaken'] == 1,
'receiptPhotoTaken':
requiresReceipt ? lastVisit['receiptPhotoTaken'] == 1 : true,
};
}
// Default return values when no visits found
return {
'photoTaken': false,
'receiptPhotoTaken': requiresReceipt ? false : true,
};
}
Future<void> insertMarker(Feature marker) async {
//print("--- inserting ${featureToMap(marker)} ---");
final db = await database;
try {
await db.insert(
'Checkpoints',
featureToMap(marker),
conflictAlgorithm: ConflictAlgorithm.replace,
);
} catch (e) {
print("--- ${e.toString()} ---");
}
}
Future<List<Feature>> getMarkers() async {
final db = await database;
var res = await db.query('Checkpoints');
List<Feature> list =
res.isNotEmpty ? res.map((c) => mapToFeature(c)).toList() : [];
//print("--- checkpoints ${res} ---");
return list;
}
Future<int> deleteMarker(int id) async {
final db = await database;
return db.delete('Checkpoints', where: 'id = ?', whereArgs: [id]);
}
Future<void> insertGPSLocation(UserLocation location) async {
final db = await database;
await db.insert(
'GPSLocations',
location
.toMap(), // Implement a method in UserLocation to convert the object to a Map
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<List<UserLocation>> getGPSLocations() async {
final db = await database;
var res = await db.query('GPSLocations');
List<UserLocation> locations =
res.isNotEmpty ? res.map((c) => UserLocation.fromMap(c)).toList() : [];
return locations;
}
Future<void> updateGameState(
String key,
String value,
String userName,
String teamName,
String eventCode,
) async {
final db = await database;
await db.insert(
'GameState',
{
'key': key,
'value': value,
'team_name': teamName,
'event_code': eventCode,
'user_name': userName,
},
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<String?> getGameState(
String key,
String userName,
String teamName,
String eventCode,
) async {
final db = await database;
List<Map<String, dynamic>> results = await db.query(
'GameState',
where: 'key = ? AND team_name = ? AND event_code = ? AND user_name = ?',
whereArgs: [key, teamName, eventCode, userName],
);
if (results.isNotEmpty) {
return results.first['value'] as String?;
}
return null;
}
}

View File

@ -0,0 +1,17 @@
enum EventType {
atStart,
starting,
started,
inCheckin,
checkiningIn,
checkedIn,
atGoal,
finishingGoal,
finishedGoal,
}
class GameEvent {
final EventType type;
final DateTime timestamp;
GameEvent({this.type = EventType.atStart, required this.timestamp});
}

View File

@ -0,0 +1,24 @@
class User {
User.User();
//AuthUser.from({required this.id, required this.email, required this.is_rogaining, required this.group, required this.zekken_number, required this.event_code, required this.team_name});
User.fromMap(Map<String, dynamic> map)
: id = int.parse(map["id"].toString()),
email = map["email"].toString(),
is_rogaining = bool.parse(map["is_rogaining"].toString()),
group = map["group"].toString(),
zekken_number = map["zekken_number"].toString(),
event_code = map["event_code"].toString(),
team_name = map["team_name"].toString(),
auth_token = map["token"];
int? id;
String? email;
bool? is_rogaining;
String? group;
String? zekken_number;
String? event_code;
String? team_name;
String? auth_token;
}

View File

@ -0,0 +1,59 @@
import 'dart:convert';
import 'package:geolocator/geolocator.dart';
// ignore_for_file: public_member_api_docs, sort_constructors_first
class UserLocation {
final double latitude;
final double longitude;
final DateTime timestamp;
UserLocation(
{required this.latitude,
required this.longitude,
required this.timestamp});
static UserLocation empty() {
return UserLocation(
latitude: 0.0, longitude: 0.0, timestamp: DateTime.now());
}
factory UserLocation.fromPosition(Position position) {
return UserLocation(
latitude: position.latitude,
longitude: position.longitude,
timestamp: position.timestamp);
}
UserLocation copyWith({
double? latitude,
double? longitude,
DateTime? timestamp,
}) {
return UserLocation(
latitude: latitude ?? this.latitude,
longitude: longitude ?? this.longitude,
timestamp: timestamp ?? this.timestamp,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'latitude': latitude,
'longitude': longitude,
'timestamp': timestamp.millisecondsSinceEpoch,
};
}
factory UserLocation.fromMap(Map<String, dynamic> map) {
return UserLocation(
latitude: map['latitude'] as double,
longitude: map['longitude'] as double,
timestamp: DateTime.fromMillisecondsSinceEpoch(map['timestamp'] as int),
);
}
String toJson() => json.encode(toMap());
factory UserLocation.fromJson(String source) =>
UserLocation.fromMap(json.decode(source) as Map<String, dynamic>);
}

View File

@ -0,0 +1,131 @@
import 'package:dio_cache_interceptor_hive_store/dio_cache_interceptor_hive_store.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_cache/flutter_map_cache.dart';
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:latlong2/latlong.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:rogapp/features/location/location_provider.dart';
import 'package:rogapp/features/state/game_state.dart';
import 'package:rogapp/features/state/game_view_model.dart';
import 'package:path_provider/path_provider.dart';
class HomePage extends ConsumerStatefulWidget {
const HomePage({super.key});
@override
ConsumerState<HomePage> createState() => _HomePageState();
}
class _HomePageState extends ConsumerState<HomePage> {
MapController mapController = MapController();
String? _cachePath;
@override
void initState() {
super.initState();
//ref.read(featureCheckpointCollectionProvider.future);
ref.read(locationNotifierProvider.notifier).startListening();
_initializeCachePath();
}
Future<void> _initializeCachePath() async {
_cachePath = await getTemporaryDirectory().then((dir) => dir.path);
setState(() {});
}
@override
Widget build(BuildContext context) {
final gameState = ref.watch(gameViewModelProvider);
final locationState = ref.watch(locationNotifierProvider);
if (_cachePath == null || gameState.markers.isEmpty) {
return Scaffold(
appBar: AppBar(),
body: Center(child: CircularProgressIndicator()),
);
}
return Scaffold(
appBar: AppBar(
actions: [
Text(
'Current Latitude: ${locationState.currentPosition?.latitude ?? 'No data'}'),
IconButton(
icon: Icon(MdiIcons.logout),
onPressed: () => _logGameState(gameState),
),
Text(gameState.currentLocation?.latitude.toString() ??
'--- No location'),
],
),
body: Column(
children: [
Expanded(child: _buildMap(gameState, _cachePath!)),
],
),
);
}
void _logGameState(GameState gameState) {
print("Checkpoint photo taken: ${gameState.checkpointPhotoTaken}");
print("Current checkpoint: ${gameState.currentCheckpoint}");
print("Has left start area: ${gameState.hasLeftStartArea}");
print("Has visited checkpoint: ${gameState.hasVisitedCheckpoint}");
}
Widget _buildMap(GameState gameState, String cachePath) {
return FlutterMap(
mapController: mapController,
options: MapOptions(
interactionOptions: const InteractionOptions(
enableMultiFingerGestureRace: true,
flags: InteractiveFlag.doubleTapDragZoom |
InteractiveFlag.doubleTapZoom |
InteractiveFlag.drag |
InteractiveFlag.flingAnimation |
InteractiveFlag.pinchZoom |
InteractiveFlag.scrollWheelZoom,
),
maxZoom: 18.4,
onMapReady: () => mapController.move(LatLng(37.153196, 139.587659), 4),
),
children: [
TileLayer(
urlTemplate:
'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
tileProvider: CachedTileProvider(
maxStale: Duration(days: 30),
store: HiveCacheStore(cachePath, hiveBoxName: 'HiveCacheStore'),
),
),
CurrentLocationLayer(
alignDirectionOnUpdate: AlignOnUpdate.never,
style: const LocationMarkerStyle(
marker: DefaultLocationMarker(
child: Icon(
Icons.navigation,
color: Colors.white,
size: 15,
),
),
markerSize: Size(27, 27),
markerDirection: MarkerDirection.heading,
),
),
MarkerLayer(
markers: gameState.markers.map((feature) {
return Marker(
child: const Icon(Icons.location_on),
// Convert each Feature to a Marker
width: 80.0,
height: 80.0,
point:
LatLng(feature.geometry.latitude, feature.geometry.longitude),
);
}).toList(),
),
],
);
}
}

View File

@ -0,0 +1,53 @@
import 'package:rogapp/features/initializer/camera_check.dart';
import 'package:rogapp/features/initializer/icheck.dart';
import 'package:rogapp/features/initializer/location_check.dart';
class AppInitializer {
final List<ICheck> checks;
AppInitializer()
: checks = [
LocationCheck(),
CameraCheck(),
//NetworkCheck(),
];
Future<bool> permissionStatus() async {
return await LocationCheck().locationPermissionStatus() &&
await CameraCheck().cameraPermissionStatus();
}
Future<bool> initializeApp() async {
if (!await runPreFlightChecks()) {
return false;
}
//await initializeTileCaching();
await loadInitialData();
await configureGlobalSettings();
// Add other initialization tasks as needed
return true;
}
Future<bool> runPreFlightChecks() async {
for (var check in checks) {
if (!await check.check()) {
return false;
}
}
return true;
}
Future<void> initializeTileCaching() async {}
Future<void> loadInitialData() async {
// Logic to preload data that your app will need
}
Future<void> configureGlobalSettings() async {
// Logic to set up any global configurations for your app
}
// You can add more methods for other initialization tasks
}

View File

@ -0,0 +1,19 @@
import 'package:permission_handler/permission_handler.dart';
import 'package:rogapp/features/initializer/icheck.dart';
class CameraCheck implements ICameraCheck {
@override
Future<bool> cameraPermissionStatus() async {
var permission = await Permission.camera.status;
return permission.isGranted;
}
@override
Future<bool> check() async {
var permission = await Permission.camera.status;
if (!permission.isGranted) {
permission = await Permission.camera.request();
}
return permission.isGranted;
}
}

View File

@ -0,0 +1,17 @@
abstract class ICheck {
Future<bool> check();
}
abstract class ILocationCheck extends ICheck {
Future<bool> locationPermissionStatus();
// Additional location-specific methods can be defined here
}
abstract class ICameraCheck extends ICheck {
Future<bool> cameraPermissionStatus();
// Additional camera-specific methods can be defined here
}
abstract class INetworkCheck extends ICheck {
// Additional network-specific methods can be defined here
}

View File

@ -0,0 +1,19 @@
import 'package:permission_handler/permission_handler.dart';
import 'package:geolocator/geolocator.dart';
import 'package:rogapp/features/initializer/icheck.dart';
class LocationCheck implements ILocationCheck {
@override
Future<bool> check() async {
var permission = await Permission.locationWhenInUse.status;
if (!permission.isGranted) {
permission = await Permission.locationWhenInUse.request();
}
return permission.isGranted && await Geolocator.isLocationServiceEnabled();
}
@override
Future<bool> locationPermissionStatus() {
return Permission.locationWhenInUse.isGranted;
}
}

View File

@ -0,0 +1,10 @@
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:rogapp/features/initializer/icheck.dart';
class NetworkCheck implements INetworkCheck {
@override
Future<bool> check() async {
var connectivityResult = await Connectivity().checkConnectivity();
return connectivityResult != ConnectivityResult.none;
}
}

View File

@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
// This screen shows when user denied and intented to close the app
class DeniedScreen extends StatefulWidget {
const DeniedScreen({super.key});
@override
State<DeniedScreen> createState() => _DeniedScreenState();
}
class _DeniedScreenState extends State<DeniedScreen> {
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Text("Dnied"),
);
}
}

View File

@ -0,0 +1,172 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:rogapp/features/data/checkpoint.dart';
import 'package:rogapp/features/data/data_provider.dart';
import 'package:rogapp/features/home/home_page.dart';
import 'package:rogapp/features/landing/denied_screen.dart';
import 'package:rogapp/features/initializer/app_initializer.dart';
import 'package:rogapp/features/services/checkpoint_repo.dart';
import 'package:rogapp/features/state/game_state.dart';
import 'package:rogapp/features/state/game_view_model.dart';
// LandingPage serves as the initial screen displayed by the application to perform necessary checks, initialize required values, and set up services essential for the app's functionality.
class LandingPage extends ConsumerStatefulWidget {
const LandingPage({super.key});
@override
ConsumerState<LandingPage> createState() => _LandingPageState();
}
class _LandingPageState extends ConsumerState<LandingPage> {
bool? _isInitialized;
@override
void initState() {
super.initState();
//load only if network available
ref.read(featureCheckpointCollectionProvider.future);
WidgetsBinding.instance.addPostFrameCallback((_) {
_initializeApp();
});
}
@override
Widget build(BuildContext context) {
AsyncValue<FeatureCollection> featureCollection =
ref.watch(featureCheckpointCollectionProvider);
return featureCollection.when(
data: (data) {
if (_isInitialized == null) {
return const MaterialApp(
home: Scaffold(body: Center(child: CircularProgressIndicator())));
}
return _isInitialized! ? const HomePage() : const DeniedScreen();
},
loading: () => const MaterialApp(
home: Scaffold(body: Center(child: CircularProgressIndicator()))),
error: (error, stack) => ErrorScreen(error: error),
);
}
Future<void> _restoreAppState() async {
final dataProvider = ref.read(dataProviderProvider);
// Example values, replace these with actual values from user and event context
String userName =
"current_user"; // Fetch the current user's name or identifier
String teamName = "user_team"; // Fetch the current user's team
String eventCode = "event_code"; // Fetch the current event code
// Load and restore game state
String? gameStateKey = "current_game_state";
String? gameStateValue = await dataProvider.getGameState(
gameStateKey, userName, teamName, eventCode);
if (gameStateValue != null) {
Map<String, dynamic> gameStateData = jsonDecode(gameStateValue);
GameState gameState = GameState.fromMap(gameStateData);
ref.read(gameViewModelProvider.notifier).loadGameState(gameState);
}
// After restoring all necessary states, proceed with the app
setState(() {
_isInitialized = true;
});
}
// Future<void> _restoreAppState() async {
// await _initializeApp();
// // Add additional restoration logic if needed, for example:
// // Restore game state, user progress, etc., from the DataProvider.
// // Example:
// // var gameState = await _dataProvider.getGameState('current_state');
// // Use the gameState to decide what to do next, e.g., navigate to a specific screen.
// if (_isInitialized != true) {
// // If initialization is not done, try to initialize the app.
// _isInitialized = await _initializeApp();
// }
// if (!_isInitialized!) {
// await _showRetryCloseDialog();
// } else {
// setState(() {
// _isInitialized = true;
// });
// }
// }
Future<bool> _initializeApp() async {
AppInitializer initializer = AppInitializer();
// Check if permissions are already granted
bool permissionsAlreadyGranted = await initializer.permissionStatus();
if (!permissionsAlreadyGranted) {
await _showPermissionRequestMessage();
}
// Initialize the app and return the success status.
return await initializer.initializeApp();
}
Future<void> _showPermissionRequestMessage() async {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) => AlertDialog(
title: const Text('Permissions Required'),
content: const Text(
'This app requires certain permissions to function properly. Please grant these permissions in the next steps.'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Okay'),
),
],
),
);
}
Future<bool> _requestPermissionsAndInitialize() async {
AppInitializer initializer = AppInitializer();
return await initializer.initializeApp();
}
Future<void> _showRetryCloseDialog() async {
await showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) => AlertDialog(
title: const Text('Initialization Failed'),
content: const Text(
'The app was unable to start properly due to missing permissions.'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Close'),
),
TextButton(
onPressed: () async {
Navigator.of(context).pop();
bool retrySuccess = await _requestPermissionsAndInitialize();
if (!retrySuccess) {
await _showRetryCloseDialog();
} else {
setState(() {
_isInitialized = true;
});
}
},
child: const Text('Retry'),
),
],
),
);
}
ErrorScreen({required Object error}) {}
}

View File

@ -0,0 +1,109 @@
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:geolocator/geolocator.dart';
import 'package:rogapp/features/data/user_location.dart';
import 'package:rogapp/features/state/game_view_model.dart';
/// The state for the location feature
class LocationState {
final Position? currentPosition;
final bool isStreamPaused;
LocationState({
this.currentPosition,
this.isStreamPaused = false,
});
LocationState copyWith({
Position? currentPosition,
bool? isStreamPaused,
}) {
return LocationState(
currentPosition: currentPosition ?? this.currentPosition,
isStreamPaused: isStreamPaused ?? this.isStreamPaused,
);
}
}
/// A StateNotifier that manages the location state
class LocationNotifier extends StateNotifier<LocationState> {
final Ref ref;
LocationNotifier(this.ref) : super(LocationState()) {
startListening();
startPeriodicUpdates();
}
StreamSubscription<Position>? _positionSubscription;
Position? _lastPosition;
final double _distanceThreshold = 220.0;
Timer? _periodicUpdateTimer;
void startPeriodicUpdates() {
_periodicUpdateTimer?.cancel();
_periodicUpdateTimer = Timer.periodic(Duration(seconds: 5), (timer) {
if (state.currentPosition != null) {
// Convert Position to UserLocation
UserLocation userLocation =
UserLocation.fromPosition(state.currentPosition!);
ref.read(gameViewModelProvider.notifier).updateLocation(userLocation);
}
});
}
void startListening() {
_positionSubscription =
Geolocator.getPositionStream().listen((Position position) {
if (_shouldUpdatePosition(position)) {
state = state.copyWith(currentPosition: position);
_lastPosition = position;
}
});
}
bool _shouldUpdatePosition(Position newPosition) {
if (_lastPosition == null) return true;
double distance = Geolocator.distanceBetween(
_lastPosition!.latitude,
_lastPosition!.longitude,
newPosition.latitude,
newPosition.longitude,
);
return distance < _distanceThreshold;
}
void stopListening() {
_positionSubscription?.cancel();
_positionSubscription = null;
state = state.copyWith(
currentPosition: null); // Reset the position state or handle as needed
}
void pauseListening() {
_positionSubscription?.pause();
}
void resumeListening() {
_positionSubscription?.resume();
}
void updateCurrentPosition(Position? position) {
state = state.copyWith(currentPosition: position);
}
void updateStreamPaused(bool isPaused) {
state = state.copyWith(isStreamPaused: isPaused);
}
@override
void dispose() {
_positionSubscription?.cancel();
super.dispose();
}
}
final locationNotifierProvider =
StateNotifierProvider<LocationNotifier, LocationState>((ref) {
return LocationNotifier(ref); // Pass ref directly
});

View File

@ -0,0 +1,14 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:rogapp/features/data/user.dart';
import 'package:rogapp/features/services/auth_service.dart';
final authProvider =
FutureProvider.family<User?, String>((ref, credentials) async {
// Assuming credentials is a combined string of "email|password"
List<String> parts = credentials.split('|');
String email = parts[0];
String password = parts[1];
AuthService authService = AuthService();
return await authService.userLogin(email, password);
});

View File

@ -0,0 +1,44 @@
import 'package:rogapp/features/data/user.dart';
import '../utils/const.dart';
import 'package:dio/dio.dart';
class AuthService {
Dio dio = Dio();
Future<User?> userLogin(String email, String password) async {
final String serverUrl = ConstValues.currentServer();
final String url = '$serverUrl/api/login/';
try {
final response = await dio.post(
url,
data: {
'email': email,
'password': password,
},
options: Options(
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
),
);
if (response.statusCode == 200 && response.data != null) {
final data = response.data;
User user = User.fromMap(data["user"]);
final String token = data["token"];
user.auth_token = token;
return user;
}
return null;
} on DioException catch (e) {
print("Dio error: ${e.response?.statusCode} - ${e.message}");
return null;
} catch (e) {
print("Unexpected error: $e");
return null;
}
}
}

View File

@ -0,0 +1,32 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:rogapp/features/data/checkpoint.dart';
import 'package:rogapp/features/data/data_provider.dart';
import 'package:rogapp/features/services/checkpoint_service.dart'; // Import your ApiService file
Future<void> storeFetchedFeatures(FeatureCollection featureCollection) async {
final dataProvider =
DataProvider(); // Ensure DataProvider is initialized appropriately
for (Feature feature in featureCollection.features) {
await dataProvider.insertMarker(feature);
}
}
final checkpointProvider = FutureProvider<List<Feature>>((ref) async {
final dataProvider =
DataProvider(); // Assuming you create or access an instance here
final markers = await dataProvider.getMarkers();
return markers;
});
final featureCheckpointCollectionProvider =
FutureProvider<FeatureCollection>((ref) async {
final apiService =
ApiService(); // Ensure ApiService is initialized appropriately
final featureCollection = await apiService.fetchFeatures();
// Store the fetched features in the local database
await storeFetchedFeatures(featureCollection);
return featureCollection; // Return the fetched feature collection
});

View File

@ -0,0 +1,27 @@
import 'package:dio/dio.dart';
import 'package:rogapp/features/data/checkpoint.dart';
class ApiService {
final Dio _dio = Dio();
Future<FeatureCollection> fetchFeatures() async {
try {
final tmp_url =
"https://rogaining.sumasen.net/api/inbound?rog=True&grp=養老ロゲ&ln1=136.52675654298724&la1=35.19710769333327&ln2=136.52675654298724&la2=35.38264844179004&ln3=136.65061968584442&la3=35.38264844179004&ln4=136.65061968584442&la4=35.19710769333327";
final response = await _dio.get(tmp_url);
if (response.statusCode == 200) {
return FeatureCollection.fromJson(response.data);
} else {
throw Exception('Failed to load features');
}
} on DioException catch (e) {
// Handle Dio errors here
print('Dio error: $e');
throw Exception('Failed to load features');
} catch (e) {
// Handle any other errors
print('General error: $e');
throw Exception('Failed to load features');
}
}
}

View File

@ -0,0 +1,98 @@
import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart';
import 'package:rogapp/features/data/checkpoint.dart';
import 'package:rogapp/features/data/user_location.dart';
enum GameStatus {
notStarted,
started,
inProgress,
atCheckpoint,
gameCompleted,
}
enum PendingAction {
none,
checkInPhoto,
receiptPhoto,
endGamePhoto,
}
@immutable
class GameState {
final GameStatus status;
final List<Feature> markers;
final UserLocation? currentLocation;
final Set<Feature> visitedMarkers;
final bool isWithinStartRadius;
final bool hasLeftStartArea;
final bool hasVisitedCheckpoint;
final Feature? currentCheckpoint;
final bool checkpointPhotoTaken;
final bool receiptPhotoTaken;
final PendingAction pendingAction;
const GameState({
this.status = GameStatus.notStarted,
this.markers = const [],
this.currentLocation,
this.visitedMarkers = const {},
this.isWithinStartRadius = false,
this.hasLeftStartArea = false,
this.hasVisitedCheckpoint = false,
this.currentCheckpoint,
this.checkpointPhotoTaken = false,
this.receiptPhotoTaken = false,
this.pendingAction = PendingAction.none,
});
LatLng? getUserLatLng() {
return currentLocation != null
? LatLng(currentLocation!.latitude, currentLocation!.longitude)
: null;
}
GameState.fromMap(Map<String, dynamic> map)
: status = GameStatus.values[map['status']],
markers =
List<Feature>.from(map['markers'].map((x) => Feature.fromMap(x))),
currentLocation = UserLocation.fromMap(map['currentLocation']),
visitedMarkers = Set<Feature>.from(
map['visitedMarkers'].map((x) => Feature.fromMap(x))),
isWithinStartRadius = map['isWithinStartRadius'],
hasLeftStartArea = map['hasLeftStartArea'],
hasVisitedCheckpoint = map['hasVisitedCheckpoint'],
currentCheckpoint = map['currentCheckpoint'] != null
? Feature.fromMap(map['currentCheckpoint'])
: null,
checkpointPhotoTaken = map['checkpointPhotoTaken'],
receiptPhotoTaken = map['receiptPhotoTaken'],
pendingAction = PendingAction.values[map['pendingAction']];
GameState copyWith({
GameStatus? status,
List<Feature>? markers,
UserLocation? currentLocation,
Set<Feature>? visitedMarkers,
bool? isWithinStartRadius,
bool? hasLeftStartArea,
bool? hasVisitedCheckpoint,
Feature? currentCheckpoint,
bool? checkpointPhotoTaken,
bool? receiptPhotoTaken,
PendingAction? pendingAction,
}) {
return GameState(
status: status ?? this.status,
markers: markers ?? this.markers,
currentLocation: currentLocation ?? this.currentLocation,
visitedMarkers: visitedMarkers ?? this.visitedMarkers,
isWithinStartRadius: isWithinStartRadius ?? this.isWithinStartRadius,
hasLeftStartArea: hasLeftStartArea ?? this.hasLeftStartArea,
hasVisitedCheckpoint: hasVisitedCheckpoint ?? this.hasVisitedCheckpoint,
currentCheckpoint: currentCheckpoint ?? this.currentCheckpoint,
checkpointPhotoTaken: checkpointPhotoTaken ?? this.checkpointPhotoTaken,
receiptPhotoTaken: receiptPhotoTaken ?? this.receiptPhotoTaken,
pendingAction: pendingAction ?? this.pendingAction);
}
}

View File

@ -0,0 +1,199 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:latlong2/latlong.dart';
import 'package:rogapp/features/data/checkpoint.dart';
import 'package:rogapp/features/data/data_provider.dart';
import 'package:rogapp/features/data/user_location.dart';
import 'package:rogapp/features/state/game_state.dart';
import 'package:rogapp/features/utils/params.dart';
final gameViewModelProvider =
StateNotifierProvider<GameViewModel, GameState>((ref) {
final dataProvider = DataProvider();
return GameViewModel(dataProvider);
});
class GameViewModel extends StateNotifier<GameState> {
final DataProvider _dataProvider;
GameViewModel(this._dataProvider) : super(const GameState()) {
loadInitialData();
}
Future<void> loadInitialData() async {
List<Feature> markers = await _dataProvider.getMarkers();
state = state.copyWith(markers: markers);
}
void setMarkers(List<Feature> markers) {
state = state.copyWith(markers: markers);
}
void startGame() {
if (state.isWithinStartRadius) {
state = state.copyWith(status: GameStatus.started);
}
}
void updateLocation(UserLocation location) {
Feature startMarker = state.markers.firstWhere(
(marker) => marker.properties.cp == -1,
orElse: () => Feature.empty());
List<Feature> checkpoints =
state.markers.where((marker) => marker.properties.cp != -1).toList();
bool isWithinStartRadius =
_checkProximity(location, startMarker /* start marker location */
);
bool hasLeftArea =
state.hasLeftStartArea || _checkHasLeftStartArea(location, startMarker);
Feature? atCheckpoint = _checkAtAnyCheckpoint(location, checkpoints);
// Check if at any checkpoint (except start point after game started)
bool isAtCheckpoint =
state.status == GameStatus.started && atCheckpoint != null;
if (isAtCheckpoint) {
_handleCheckpointArrival(location, atCheckpoint);
}
// Update game state based on the new location
state = state.copyWith(
currentLocation: location,
isWithinStartRadius: isWithinStartRadius,
hasLeftStartArea: hasLeftArea,
status: isAtCheckpoint ? GameStatus.atCheckpoint : state.status,
);
}
void _handleCheckpointArrival(UserLocation location, Feature marker) {
// Directly set the current checkpoint; no need to check if the photo was taken here
state = state.copyWith(currentCheckpoint: marker);
// If no checkpoint photo has been taken yet, set the pending action to take a checkpoint photo
if (!state.checkpointPhotoTaken) {
state = state.copyWith(pendingAction: PendingAction.checkInPhoto);
}
// Additional actions specific to buy points can be prepared here, but it's usually better
// to handle them after the checkpoint photo is submitted, not at the moment of arrival.
}
void submitCheckInPhoto(String photoPath) {
// Logic to save or upload the check-in photo...
state = state.copyWith(checkpointPhotoTaken: true);
// Determine the next action based on the checkpoint type
if (state.currentCheckpoint != null &&
isBuyPoint(state.currentCheckpoint!)) {
state = state.copyWith(pendingAction: PendingAction.receiptPhoto);
} else {
// If it's not a buy point, mark the checkpoint visit as complete
_markCheckpointVisited(state.currentCheckpoint!);
}
}
void submitReceiptPhoto(String photoPath) {
// Logic to save or upload the receipt photo...
state = state.copyWith(receiptPhotoTaken: true);
// After submitting the receipt photo, mark the checkpoint visit as complete
_markCheckpointVisited(state.currentCheckpoint!);
}
void _markCheckpointVisited(Feature checkpoint) {
state = state.copyWith(
visitedMarkers: Set.from(state.visitedMarkers)..add(checkpoint),
hasVisitedCheckpoint: true,
pendingAction: PendingAction.none, // Reset the pending action
);
// Additional logic to determine if the game is completed...
}
bool isBuyPoint(Feature checkpoint) {
if (checkpoint.properties.buyPoint != null) {
return true;
}
return false;
}
bool _checkProximity(UserLocation location, Feature startMarker) {
const Distance distance = Distance();
return distance(
LatLng(location.latitude, location.longitude),
LatLng(startMarker.geometry.latitude,
startMarker.geometry.longitude)) <=
startMinDistance;
}
bool _checkHasLeftStartArea(UserLocation location, Feature startMarker) {
const Distance distance = Distance();
return distance(
LatLng(location.latitude, location.longitude),
LatLng(startMarker.geometry.latitude,
startMarker.geometry.longitude)) >=
minAwayfromStartDistance;
}
Feature? _checkAtAnyCheckpoint(UserLocation location, List<Feature> markers) {
const Distance distance = Distance();
for (Feature marker in markers) {
final double distanceInMeters = distance(
LatLng(location.latitude, location.longitude),
LatLng(marker.geometry.latitude, marker.geometry.longitude),
);
if (marker.properties.checkinRadious != null &&
marker.properties.checkinRadious! >= distanceInMeters) {
return marker;
}
}
return null;
}
Feature? _getCheckpointFromLocation(UserLocation location) {
const thresholdDistance = 50.0; // meters, adjust based on your game design
const Distance distance = Distance();
for (final marker in state.markers) {
final double distanceInMeters = distance(
LatLng(location.latitude, location.longitude),
LatLng(marker.geometry.latitude, marker.geometry.longitude),
);
if (distanceInMeters <=
(marker.properties.checkinRadious ?? thresholdDistance)) {
return marker;
}
}
return null; // No checkpoint is close enough to the user's location
}
bool _isBackAtStart(UserLocation location) {
const startThresholdDistance = 150.0; // Define proximity threshold
const Distance distance = Distance();
// Assuming the start point is the first marker
// Adjust based on your game's logic
final startPoint = state.markers.first;
final double distanceToStart = distance(
LatLng(location.latitude, location.longitude),
LatLng(startPoint.geometry.latitude, startPoint.geometry.longitude),
);
// User is considered back at the start if within the threshold distance
return distanceToStart <= startThresholdDistance;
}
void endGame() {
if (state.status == GameStatus.gameCompleted) {
// Perform any end game actions
}
}
void loadGameState(GameState gameState) {
state = gameState;
}
}

View File

@ -0,0 +1,18 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:rogapp/features/data/user.dart';
class UserNotifier extends StateNotifier<User?> {
UserNotifier() : super(null);
void setUser(User? user) {
state = user;
}
void clearUser() {
state = null;
}
}
final userNotifierProvider = StateNotifierProvider<UserNotifier, User?>((ref) {
return UserNotifier();
});

View File

@ -0,0 +1,13 @@
class ConstValues {
static const container_svr = "http://container.intranet.sumasen.net:8100";
static const server_uri = "https://rogaining.sumasen.net";
static const dev_server = "http://localhost:8100";
static const dev_ip_server = "http://192.168.8.100:8100";
static const dev_home_ip_server = "http://172.20.10.9:8100";
static const dev_home_ip_mserver = "http://192.168.1.10:8100";
static String currentServer() {
//return dev_ip_server;
return server_uri;
}
}

View File

@ -0,0 +1,29 @@
import 'dart:math';
import 'package:latlong2/latlong.dart';
class DistanceCalculatorHS {
static const int _radius = 6371; // Radius of the Earth in kilometers
static double _degreeToRadian(double degree) {
return degree * pi / 180;
}
static double calculateDistance(LatLng location1, LatLng location2) {
double latDistance =
_degreeToRadian(location2.latitude - location1.latitude);
double lonDistance =
_degreeToRadian(location2.longitude - location1.longitude);
double a = sin(latDistance / 2) * sin(latDistance / 2) +
cos(_degreeToRadian(location1.latitude)) *
cos(_degreeToRadian(location2.latitude)) *
sin(lonDistance / 2) *
sin(lonDistance / 2);
double c = 2 * atan2(sqrt(a), sqrt(1 - a));
double distance = _radius * c * 1000; // Convert to meters
return distance;
}
}

View File

@ -0,0 +1,17 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
final errorReportingProvider = Provider<ErrorReporter>((ref) {
return ErrorReporterImpl();
});
class ErrorReporter {
void reportError(Object error, StackTrace stackTrace) {}
}
class ErrorReporterImpl extends ErrorReporter {
@override
void reportError(Object error, StackTrace stackTrace) {
// Send error and stackTrace to the server
// Include app state if necessary
}
}

View File

@ -0,0 +1,2 @@
int startMinDistance = 150;
int minAwayfromStartDistance = 300;

View File

@ -1,147 +1,47 @@
import 'package:flutter/material.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
import 'package:get/get.dart';
import 'package:rogapp/pages/destination/destination_controller.dart';
import 'package:rogapp/pages/index/index_binding.dart';
import 'package:rogapp/routes/app_pages.dart';
import 'package:rogapp/utils/location_controller.dart';
import 'package:rogapp/utils/string_values.dart';
import 'package:shared_preferences/shared_preferences.dart';
// import 'package:is_lock_screen/is_lock_screen.dart';
void saveGameState() async {
DestinationController destinationController =
Get.find<DestinationController>();
SharedPreferences pref = await SharedPreferences.getInstance();
pref.setBool("is_in_rog", destinationController.isInRog.value);
pref.setBool(
"rogaining_counted", destinationController.rogainingCounted.value);
pref.setBool("ready_for_goal", DestinationController.ready_for_goal);
}
void restoreGame() async {
SharedPreferences pref = await SharedPreferences.getInstance();
DestinationController destinationController =
Get.find<DestinationController>();
destinationController.skipGps = false;
destinationController.isInRog.value = pref.getBool("is_in_rog") ?? false;
destinationController.rogainingCounted.value =
pref.getBool("rogaining_counted") ?? false;
DestinationController.ready_for_goal =
pref.getBool("ready_for_goal") ?? false;
//print(
// "--restored -- destinationController.isInRog.value ${pref.getBool("is_in_rog")} -- ${pref.getBool("rogaining_counted")}");
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterMapTileCaching.initialise();
final StoreDirectory instanceA = FMTC.instance('OpenStreetMap (A)');
await instanceA.manage.createAsync();
await instanceA.metadata.addAsync(
key: 'sourceURL',
value: 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
);
await instanceA.metadata.addAsync(
key: 'validDuration',
value: '14',
);
await instanceA.metadata.addAsync(
key: 'behaviour',
value: 'cacheFirst',
);
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
// This widget is the root of your application.
@override
void initState() {
super.initState();
if (context.mounted) {
restoreGame();
}
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
// void saveGameState() async {
// DestinationController destinationController = Get.find<DestinationController>();
// SharedPreferences pref = await SharedPreferences.getInstance();
// pref.setBool("is_in_rog", destinationController.is_in_rog.value);
// pref.setBool("rogaining_counted", destinationController.rogaining_counted.value);
// }
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
LocationController locationController = Get.find<LocationController>();
DestinationController destinationController =
Get.find<DestinationController>();
switch (state) {
case AppLifecycleState.resumed:
locationController.resumePositionStream();
//print("RESUMED");
restoreGame();
break;
case AppLifecycleState.inactive:
locationController.resumePositionStream();
//print("INACTIVE");
break;
case AppLifecycleState.paused:
locationController.resumePositionStream();
//print("PAUSED");
saveGameState();
break;
case AppLifecycleState.detached:
locationController.resumePositionStream();
//print("DETACHED");
saveGameState();
break;
case AppLifecycleState.hidden:
locationController.resumePositionStream();
//print("DETACHED");
saveGameState();
break;
}
}
@override
Widget build(BuildContext context) {
return GetMaterialApp(
translations: StringValues(),
locale: const Locale('ja', 'JP'),
//locale: const Locale('en', 'US'),
fallbackLocale: const Locale('en', 'US'),
title: 'ROGAINING',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color.fromARGB(255, 36, 135, 221)),
useMaterial3: true,
),
debugShowCheckedModeBanner: false,
defaultTransition: Transition.cupertino,
opaqueRoute: Get.isOpaqueRouteDefault,
popGesture: Get.isPopGestureEnable,
transitionDuration: const Duration(milliseconds: 230),
initialBinding: IndexBinding(), //HomeBinding(),
initialRoute: AppPages.PERMISSION,
getPages: AppPages.routes,
enableLog: true,
);
}
}
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:rogapp/features/landing/landing_page.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:rogapp/features/utils/error_reporter.dart';
void reportError(
ProviderContainer container, Object error, StackTrace stackTrace) {
// Log the error locally or report to a server
print('Caught an error: $error');
container.read(errorReportingProvider).reportError(error, stackTrace);
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final container = ProviderContainer();
runZonedGuarded(() {
runApp(UncontrolledProviderScope(container: container, child: const App()));
FlutterError.onError = (FlutterErrorDetails details) {
final error = details.exception;
final stackTrace = details.stack ?? StackTrace.current;
reportError(container, error, stackTrace);
};
}, (error, stackTrace) {
reportError(container, error, stackTrace ?? StackTrace.current);
});
}
class App extends StatelessWidget {
const App({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color.fromARGB(255, 0, 35, 140)), //Samurai Blue
useMaterial3: true,
),
home: const LandingPage(),
);
}
}

146
lib/main_mapsix.dart Normal file
View File

@ -0,0 +1,146 @@
// import 'package:flutter/material.dart';
// import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
// import 'package:get/get.dart';
// import 'package:rogapp/pages/destination/destination_controller.dart';
// import 'package:rogapp/pages/index/index_binding.dart';
// import 'package:rogapp/routes/app_pages.dart';
// import 'package:rogapp/utils/location_controller.dart';
// import 'package:rogapp/utils/string_values.dart';
// import 'package:shared_preferences/shared_preferences.dart';
// // import 'package:is_lock_screen/is_lock_screen.dart';
// void saveGameState() async {
// DestinationController destinationController =
// Get.find<DestinationController>();
// SharedPreferences pref = await SharedPreferences.getInstance();
// pref.setBool("is_in_rog", destinationController.isInRog.value);
// pref.setBool(
// "rogaining_counted", destinationController.rogainingCounted.value);
// pref.setBool("ready_for_goal", DestinationController.ready_for_goal);
// }
// void restoreGame() async {
// SharedPreferences pref = await SharedPreferences.getInstance();
// DestinationController destinationController =
// Get.find<DestinationController>();
// destinationController.skipGps = false;
// destinationController.isInRog.value = pref.getBool("is_in_rog") ?? false;
// destinationController.rogainingCounted.value =
// pref.getBool("rogaining_counted") ?? false;
// DestinationController.ready_for_goal =
// pref.getBool("ready_for_goal") ?? false;
// //print(
// // "--restored -- destinationController.isInRog.value ${pref.getBool("is_in_rog")} -- ${pref.getBool("rogaining_counted")}");
// }
// void main() async {
// WidgetsFlutterBinding.ensureInitialized();
// await FlutterMapTileCaching.initialise();
// final StoreDirectory instanceA = FMTC.instance('OpenStreetMap (A)');
// await instanceA.manage.createAsync();
// await instanceA.metadata.addAsync(
// key: 'sourceURL',
// value: 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
// );
// await instanceA.metadata.addAsync(
// key: 'validDuration',
// value: '14',
// );
// await instanceA.metadata.addAsync(
// key: 'behaviour',
// value: 'cacheFirst',
// );
// runApp(const MyApp());
// }
// class MyApp extends StatefulWidget {
// const MyApp({Key? key}) : super(key: key);
// @override
// State<MyApp> createState() => _MyAppState();
// }
// class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
// // This widget is the root of your application.
// @override
// void initState() {
// super.initState();
// if (context.mounted) {
// restoreGame();
// }
// WidgetsBinding.instance.addObserver(this);
// }
// @override
// void dispose() {
// WidgetsBinding.instance.removeObserver(this);
// super.dispose();
// }
// // void saveGameState() async {
// // DestinationController destinationController = Get.find<DestinationController>();
// // SharedPreferences pref = await SharedPreferences.getInstance();
// // pref.setBool("is_in_rog", destinationController.is_in_rog.value);
// // pref.setBool("rogaining_counted", destinationController.rogaining_counted.value);
// // }
// @override
// void didChangeAppLifecycleState(AppLifecycleState state) {
// LocationController locationController = Get.find<LocationController>();
// DestinationController destinationController =
// Get.find<DestinationController>();
// switch (state) {
// case AppLifecycleState.resumed:
// locationController.resumePositionStream();
// //print("RESUMED");
// restoreGame();
// break;
// case AppLifecycleState.inactive:
// locationController.resumePositionStream();
// //print("INACTIVE");
// break;
// case AppLifecycleState.paused:
// locationController.resumePositionStream();
// //print("PAUSED");
// saveGameState();
// break;
// case AppLifecycleState.detached:
// locationController.resumePositionStream();
// //print("DETACHED");
// saveGameState();
// break;
// case AppLifecycleState.hidden:
// locationController.resumePositionStream();
// //print("DETACHED");
// saveGameState();
// break;
// }
// }
// @override
// Widget build(BuildContext context) {
// return GetMaterialApp(
// translations: StringValues(),
// locale: const Locale('ja', 'JP'),
// //locale: const Locale('en', 'US'),
// fallbackLocale: const Locale('en', 'US'),
// title: 'ROGAINING',
// theme: ThemeData(
// colorScheme: ColorScheme.fromSeed(
// seedColor: const Color.fromARGB(255, 36, 135, 221)),
// useMaterial3: true,
// ),
// debugShowCheckedModeBanner: false,
// defaultTransition: Transition.cupertino,
// opaqueRoute: Get.isOpaqueRouteDefault,
// popGesture: Get.isPopGestureEnable,
// transitionDuration: const Duration(milliseconds: 230),
// initialBinding: IndexBinding(), //HomeBinding(),
// initialRoute: AppPages.PERMISSION,
// getPages: AppPages.routes,
// enableLog: true,
// );
// }
// }

View File

@ -1,61 +1,61 @@
import 'package:flutter/material.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:rogapp/nrog/pages/permission_page.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
// import 'package:flutter_riverpod/flutter_riverpod.dart';
// import 'package:rogapp/nrog/pages/permission_page.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// void main() async {
// WidgetsFlutterBinding.ensureInitialized();
await FlutterMapTileCaching.initialise();
final StoreDirectory instanceA = FMTC.instance('OpenStreetMap (A)');
await instanceA.manage.createAsync();
await instanceA.metadata.addAsync(
key: 'sourceURL',
value: 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
);
await instanceA.metadata.addAsync(
key: 'validDuration',
value: '14',
);
await instanceA.metadata.addAsync(
key: 'behaviour',
value: 'cacheFirst',
);
runApp(
const ProviderScope(child: MyApp()),
);
}
// await FlutterMapTileCaching.initialise();
// final StoreDirectory instanceA = FMTC.instance('OpenStreetMap (A)');
// await instanceA.manage.createAsync();
// await instanceA.metadata.addAsync(
// key: 'sourceURL',
// value: 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
// );
// await instanceA.metadata.addAsync(
// key: 'validDuration',
// value: '14',
// );
// await instanceA.metadata.addAsync(
// key: 'behaviour',
// value: 'cacheFirst',
// );
// runApp(
// const ProviderScope(child: MyApp()),
// );
// }
class MyApp extends StatefulWidget {
const MyApp({super.key});
// class MyApp extends StatefulWidget {
// const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
// @override
// State<MyApp> createState() => _MyAppState();
// }
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
// class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
// @override
// void initState() {
// super.initState();
// WidgetsBinding.instance.addObserver(this);
// }
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
// @override
// void dispose() {
// WidgetsBinding.instance.removeObserver(this);
// super.dispose();
// }
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color.fromARGB(255, 124, 156, 9)),
useMaterial3: true,
),
home: const PermissionPage());
}
}
// @override
// Widget build(BuildContext context) {
// return MaterialApp(
// debugShowCheckedModeBanner: false,
// title: 'Flutter Demo',
// theme: ThemeData(
// colorScheme: ColorScheme.fromSeed(
// seedColor: const Color.fromARGB(255, 124, 156, 9)),
// useMaterial3: true,
// ),
// home: const PermissionPage());
// }
// }

View File

@ -95,7 +95,7 @@ class _HomePageState extends ConsumerState<HomePage> {
// .hideAllPopups(), // Hide popup when the map is tapped.
),
children: [
const BaseLayer(),
//const BaseLayer(),
CurrentLocationLayer(
followOnLocationUpdate: FollowOnLocationUpdate.once,
turnOnHeadingUpdate: TurnOnHeadingUpdate.never,

View File

@ -6,6 +6,6 @@ class DestinationBinding extends Bindings {
@override
void dependencies() {
Get.put<DestinationController>(DestinationController());
restoreGame();
//restoreGame();
}
}

View File

@ -650,7 +650,7 @@ class DestinationController extends GetxController {
}
isInRog.value = true;
saveGameState();
//saveGameState();
}
Future<void> cancelBuyPoint(Destination destination) async {

View File

@ -155,7 +155,7 @@ class DestinationMapPage extends StatelessWidget {
interactiveFlags: InteractiveFlag.pinchZoom | InteractiveFlag.drag,
),
children: [
const BaseLayer(),
//const BaseLayer(),
Obx(
() => indexController.routePointLenght > 0
? PolylineLayer(

View File

@ -123,7 +123,7 @@ class _GpsPageState extends State<GpsPage> {
onTap: (tapPos, cord) {}, // Hide popup when the map is tapped.
),
children: [
const BaseLayer(),
//const BaseLayer(),
MarkerLayer(
markers: gpsData.map((i) {
return Marker(

View File

@ -116,8 +116,8 @@ class IndexController extends GetxController {
@override
void onInit() {
_connectivitySubscription =
_connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
// _connectivitySubscription =
// _connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
super.onInit();
}
@ -133,16 +133,16 @@ class IndexController extends GetxController {
}
Future<void> initConnectivity() async {
late ConnectivityResult result;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
result = await _connectivity.checkConnectivity();
} on PlatformException catch (_) {
//print('Couldn\'t check connectivity status --- $e');
return;
}
// late ConnectivityResult result;
// // Platform messages may fail, so we use a try/catch PlatformException.
// try {
// //result = await _connectivity.checkConnectivity();
// } on PlatformException catch (_) {
// //print('Couldn\'t check connectivity status --- $e');
// return;
// }
return _updateConnectionStatus(result);
// return _updateConnectionStatus(result);
}
LatLngBounds boundsFromLatLngList(List<LatLng> list) {

View File

@ -107,7 +107,7 @@ class LocationService {
'$serverUrl/api/inbound?ln1=$lon1&la1=$lat1&ln2=$lon2&la2=$lat2&ln3=$lon3&la3=$lat3&ln4=$lon4&la4=$lat4';
}
}
//print('++++++++$url');
print('++++++++$url');
final response = await http.get(
Uri.parse(url),
headers: <String, String>{

View File

@ -1,24 +1,24 @@
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter_map/flutter_map.dart';
// import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
class BaseLayer extends StatelessWidget {
const BaseLayer({Key? key}) : super(key: key);
// class BaseLayer extends StatelessWidget {
// const BaseLayer({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return TileLayer(
urlTemplate: "https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png",
tileProvider: FMTC.instance('OpenStreetMap (A)').getTileProvider(
settings: FMTCTileProviderSettings(
behavior: CacheBehavior.values.byName('cacheFirst'),
cachedValidDuration: const Duration(days: 14),
),
),
);
}
}
// @override
// Widget build(BuildContext context) {
// return TileLayer(
// urlTemplate: "https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png",
// tileProvider: FMTC.instance('OpenStreetMap (A)').getTileProvider(
// settings: FMTCTileProviderSettings(
// behavior: CacheBehavior.values.byName('cacheFirst'),
// cachedValidDuration: const Duration(days: 14),
// ),
// ),
// );
// }
// }
// var Esri_WorldImagery = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
// attribution: 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
// });
// // var Esri_WorldImagery = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
// // attribution: 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
// // });

View File

@ -195,7 +195,7 @@ class BottomSheetNew extends GetView<BottomSheetController> {
destinationController.currentLon,
destination.location_id!,
);
saveGameState();
//saveGameState();
await ExternalService().startRogaining();
Get.back(); // Close the dialog and potentially navigate away
},

View File

@ -237,7 +237,7 @@ class _MapWidgetState extends State<MapWidget> {
.hideAllPopups(), // Hide popup when the map is tapped.
),
children: [
const BaseLayer(),
//const BaseLayer(),
Obx(
() => indexController.routePointLenght > 0
? PolylineLayer(