im so tired man

This commit is contained in:
Wowieee4
2026-05-28 11:27:06 +07:00
parent 66da2473e1
commit 6272ece15d
86 changed files with 1759 additions and 4090 deletions
@@ -0,0 +1,3 @@
Navigation mode application layer.
This layer is reserved for navigation Cubit/BLoC orchestration.
@@ -0,0 +1,3 @@
Navigation mode data layer.
This layer is reserved for location, OSM, and OSRM data adapters.
@@ -0,0 +1,3 @@
Navigation mode domain layer.
This layer is reserved for route, waypoint, and navigation use cases.
@@ -61,35 +61,40 @@ class _NavState extends Cubit<int> {
StreamSubscription<Position>? _posStream;
void _set(_NavPhase p, String status) {
if (isClosed) return;
phase = p;
statusText = status;
_notify();
}
void _notify() => emit(state + 1);
void _notify() {
if (isClosed) return;
emit(state + 1);
}
// ── locate ──────────────────────────────────────────────────────────────
Future<bool> locate() async {
_set(_NavPhase.locating, 'Mencari lokasi GPS…');
final located = await guarded<bool>(
() async {
LocationPermission perm = await Geolocator.checkPermission();
if (perm == LocationPermission.denied) {
perm = await Geolocator.requestPermission();
}
if (perm == LocationPermission.deniedForever) {
_set(_NavPhase.error, 'Izin lokasi diblokir permanen.');
return false;
}
final pos = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
).timeout(const Duration(seconds: 12));
currentPosition = LatLng(pos.latitude, pos.longitude);
_set(_NavPhase.idle, 'Lokasi ditemukan. Cari tujuan atau ketuk peta.');
_reportToBackend(pos);
return true;
LocationPermission perm = await Geolocator.checkPermission();
if (perm == LocationPermission.denied) {
perm = await Geolocator.requestPermission();
}
if (perm == LocationPermission.deniedForever) {
_set(_NavPhase.error, 'Izin lokasi diblokir permanen.');
return false;
}
final pos = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
).timeout(const Duration(seconds: 12));
currentPosition = LatLng(pos.latitude, pos.longitude);
_set(_NavPhase.idle, 'Lokasi ditemukan. Cari tujuan atau ketuk peta.');
_reportToBackend(pos);
return true;
},
onTimeout: () => _set(_NavPhase.error, 'GPS timeout. Pastikan GPS aktif.'),
onTimeout: () =>
_set(_NavPhase.error, 'GPS timeout. Pastikan GPS aktif.'),
onError: (_) => _set(_NavPhase.error,
'Lokasi belum bisa dibaca. Pastikan GPS dan izin lokasi aktif.'),
);
@@ -113,33 +118,37 @@ class _NavState extends Cubit<int> {
Future<List<_Place>> searchPlaces(String query) async {
if (query.trim().isEmpty) return const [];
return await guarded<List<_Place>>(
() async {
final res = await Dio().get(
'https://nominatim.openstreetmap.org/search',
queryParameters: {
'q': query,
'format': 'jsonv2',
'limit': 6,
'addressdetails': 0,
if (currentPosition != null) 'viewbox': _viewbox(currentPosition!),
if (currentPosition != null) 'bounded': 0,
},
options: Options(
headers: {'User-Agent': 'WalkGuide/1.0 (walkguide@campus.ac.id)'},
receiveTimeout: const Duration(seconds: 8),
),
);
final list = res.data as List;
return list.map((e) {
final lat = double.tryParse(e['lat'].toString()) ?? 0;
final lng = double.tryParse(e['lon'].toString()) ?? 0;
return _Place(
displayName: e['display_name'].toString(),
position: LatLng(lat, lng),
);
}).toList();
},
) ?? const [];
() async {
final res = await Dio().get(
'https://nominatim.openstreetmap.org/search',
queryParameters: {
'q': query,
'format': 'jsonv2',
'limit': 6,
'addressdetails': 0,
if (currentPosition != null)
'viewbox': _viewbox(currentPosition!),
if (currentPosition != null) 'bounded': 0,
},
options: Options(
headers: {
'User-Agent': 'WalkGuide/1.0 (walkguide@campus.ac.id)'
},
receiveTimeout: const Duration(seconds: 8),
),
);
final list = res.data as List;
return list.map((e) {
final lat = double.tryParse(e['lat'].toString()) ?? 0;
final lng = double.tryParse(e['lon'].toString()) ?? 0;
return _Place(
displayName: e['display_name'].toString(),
position: LatLng(lat, lng),
);
}).toList();
},
) ??
const [];
}
String _viewbox(LatLng c) =>
@@ -148,23 +157,25 @@ class _NavState extends Cubit<int> {
// ── reverse geocode ──────────────────────────────────────────────────────
Future<String> reverseGeocode(LatLng pos) async {
return await guarded<String>(
() async {
final res = await Dio().get(
'https://nominatim.openstreetmap.org/reverse',
queryParameters: {
'lat': pos.latitude,
'lon': pos.longitude,
'format': 'jsonv2',
},
options: Options(
headers: {'User-Agent': 'WalkGuide/1.0 (walkguide@campus.ac.id)'},
receiveTimeout: const Duration(seconds: 6),
),
);
return res.data['display_name']?.toString() ??
'${pos.latitude.toStringAsFixed(5)}, ${pos.longitude.toStringAsFixed(5)}';
},
) ??
() async {
final res = await Dio().get(
'https://nominatim.openstreetmap.org/reverse',
queryParameters: {
'lat': pos.latitude,
'lon': pos.longitude,
'format': 'jsonv2',
},
options: Options(
headers: {
'User-Agent': 'WalkGuide/1.0 (walkguide@campus.ac.id)'
},
receiveTimeout: const Duration(seconds: 6),
),
);
return res.data['display_name']?.toString() ??
'${pos.latitude.toStringAsFixed(5)}, ${pos.longitude.toStringAsFixed(5)}';
},
) ??
'${pos.latitude.toStringAsFixed(5)}, ${pos.longitude.toStringAsFixed(5)}';
}
@@ -180,47 +191,47 @@ class _NavState extends Cubit<int> {
final origin = currentPosition!;
await guarded<void>(
() async {
final url = 'http://router.project-osrm.org/route/v1/foot/'
'${origin.longitude},${origin.latitude};'
'${dest.position.longitude},${dest.position.latitude}'
'?steps=true&geometries=geojson&overview=full&annotations=false';
final url = 'http://router.project-osrm.org/route/v1/foot/'
'${origin.longitude},${origin.latitude};'
'${dest.position.longitude},${dest.position.latitude}'
'?steps=true&geometries=geojson&overview=full&annotations=false';
final res = await Dio().get(
url,
options: Options(receiveTimeout: const Duration(seconds: 12)),
);
final res = await Dio().get(
url,
options: Options(receiveTimeout: const Duration(seconds: 12)),
);
final route = res.data['routes'][0];
final geom = route['geometry']['coordinates'] as List;
routePoints = geom.map((c) {
final lst = c as List;
return LatLng((lst[1] as num).toDouble(), (lst[0] as num).toDouble());
}).toList();
final route = res.data['routes'][0];
final geom = route['geometry']['coordinates'] as List;
routePoints = geom.map((c) {
final lst = c as List;
return LatLng((lst[1] as num).toDouble(), (lst[0] as num).toDouble());
}).toList();
// parse steps
final legs = route['legs'] as List;
final rawSteps = <_Step>[];
for (final leg in legs) {
for (final step in leg['steps'] as List) {
final maneuver = step['maneuver'] as Map;
final instruction = _buildInstruction(maneuver, step);
final dist = (step['distance'] as num?)?.toDouble() ?? 0;
final loc = maneuver['location'] as List;
rawSteps.add(_Step(
instruction: instruction,
distanceM: dist,
point:
LatLng((loc[1] as num).toDouble(), (loc[0] as num).toDouble()),
));
// parse steps
final legs = route['legs'] as List;
final rawSteps = <_Step>[];
for (final leg in legs) {
for (final step in leg['steps'] as List) {
final maneuver = step['maneuver'] as Map;
final instruction = _buildInstruction(maneuver, step);
final dist = (step['distance'] as num?)?.toDouble() ?? 0;
final loc = maneuver['location'] as List;
rawSteps.add(_Step(
instruction: instruction,
distanceM: dist,
point: LatLng(
(loc[1] as num).toDouble(), (loc[0] as num).toDouble()),
));
}
}
}
steps = rawSteps;
currentStepIndex = 0;
steps = rawSteps;
currentStepIndex = 0;
_set(_NavPhase.navigating,
steps.isNotEmpty ? steps[0].instruction : 'Ikuti rute biru.');
_notify();
_startTracking();
_set(_NavPhase.navigating,
steps.isNotEmpty ? steps[0].instruction : 'Ikuti rute biru.');
_notify();
_startTracking();
},
onError: (_) => _set(_NavPhase.error,
'Gagal mendapat rute. Periksa koneksi lalu coba lagi.'),
@@ -289,6 +300,7 @@ class _NavState extends Cubit<int> {
distanceFilter: 5,
),
).listen((pos) {
if (isClosed) return;
currentPosition = LatLng(pos.latitude, pos.longitude);
_reportToBackend(pos);
_updateStep();
@@ -298,6 +310,7 @@ class _NavState extends Cubit<int> {
void _updateStep() {
if (steps.isEmpty || phase != _NavPhase.navigating) return;
if (currentPosition == null) return;
if (currentStepIndex >= steps.length - 1) return;
final current = steps[currentStepIndex];
@@ -317,6 +330,7 @@ class _NavState extends Cubit<int> {
}
void stopNavigation() {
if (isClosed) return;
_posStream?.cancel();
_posStream = null;
destination = null;