Add Android support with configurable server URL and location permissions
- Add shared_preferences for persisting server URL - Add SettingsService and PlatformService - Add server URL input field on non-web platforms - Make ApiConfig baseUrl configurable at runtime - Add Android location permissions (ACCESS_FINE/COURSE_LOCATION, INTERNET) - Request location permission on login and map init - Fix geo_id type: use String instead of int (UUID format) - Align share_service with API spec: remove unique_id, use share_id only - Fix watch endpoint response: last_update instead of created_at - Add error handling with SnackBars for geo operations - Wrap login screen in SingleChildScrollView for keyboard handling - Update map tile layer with userAgentPackageName for OSM
This commit is contained in:
+64
-29
@@ -43,6 +43,17 @@ class _MapScreenState extends State<MapScreen> {
|
||||
return;
|
||||
}
|
||||
|
||||
final status = await Geolocator.checkPermission();
|
||||
if (status == LocationPermission.denied) {
|
||||
final requested = await Geolocator.requestPermission();
|
||||
if (requested == LocationPermission.denied) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (status == LocationPermission.deniedForever) {
|
||||
return;
|
||||
}
|
||||
|
||||
final position = await Geolocator.getCurrentPosition();
|
||||
setState(() {
|
||||
_center = LatLng(position.latitude, position.longitude);
|
||||
@@ -54,11 +65,18 @@ class _MapScreenState extends State<MapScreen> {
|
||||
final shareProvider = context.read<ShareProvider>();
|
||||
|
||||
if (authProvider.token.isNotEmpty) {
|
||||
await shareProvider.createShare(
|
||||
authProvider.token,
|
||||
position.longitude,
|
||||
position.latitude,
|
||||
);
|
||||
try {
|
||||
await shareProvider.createShare(
|
||||
authProvider.token,
|
||||
position.longitude,
|
||||
position.latitude,
|
||||
);
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Ошибка геолокации: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
@@ -75,6 +93,11 @@ class _MapScreenState extends State<MapScreen> {
|
||||
|
||||
Future<void> _updateGeolocation() async {
|
||||
try {
|
||||
final status = await Geolocator.checkPermission();
|
||||
if (status == LocationPermission.denied) {
|
||||
final requested = await Geolocator.requestPermission();
|
||||
if (requested == LocationPermission.denied) return;
|
||||
}
|
||||
final position = await Geolocator.getCurrentPosition();
|
||||
setState(() {
|
||||
_center = LatLng(position.latitude, position.longitude);
|
||||
@@ -85,13 +108,20 @@ class _MapScreenState extends State<MapScreen> {
|
||||
final authProvider = context.read<AuthProvider>();
|
||||
final shareProvider = context.read<ShareProvider>();
|
||||
|
||||
await GeoService().updatePosition(
|
||||
authProvider.token,
|
||||
shareProvider.geoId,
|
||||
position.longitude,
|
||||
position.latitude,
|
||||
);
|
||||
} catch (_) {}
|
||||
if (shareProvider.geoId.isEmpty) return;
|
||||
|
||||
await GeoService().updatePosition(
|
||||
authProvider.token,
|
||||
shareProvider.geoId,
|
||||
position.longitude,
|
||||
position.latitude,
|
||||
);
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Ошибка обновления позиции: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _copyShareLink() {
|
||||
@@ -115,19 +145,22 @@ class _MapScreenState extends State<MapScreen> {
|
||||
final TextEditingController _controller = TextEditingController();
|
||||
return AlertDialog(
|
||||
title: const Text('Отслеживание'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('Введите Share ID для отслеживания:'),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: _controller,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Введите Share ID',
|
||||
border: OutlineInputBorder(),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Text('Введите Share ID для отслеживания:'),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: _controller,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Введите Share ID',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
@@ -174,8 +207,8 @@ class _MapScreenState extends State<MapScreen> {
|
||||
Marker? trackedMarker;
|
||||
if (shareProvider.trackedPosition != null) {
|
||||
final pos = shareProvider.trackedPosition!;
|
||||
final lat = pos['y'] as double;
|
||||
final lng = pos['x'] as double;
|
||||
final lat = pos['y'] is String ? double.parse(pos['y']) : pos['y'] as double;
|
||||
final lng = pos['x'] is String ? double.parse(pos['x']) : pos['x'] as double;
|
||||
trackedMarker = Marker(
|
||||
point: LatLng(lat, lng),
|
||||
width: 40,
|
||||
@@ -226,12 +259,14 @@ class _MapScreenState extends State<MapScreen> {
|
||||
),
|
||||
body: FlutterMap(
|
||||
options: MapOptions(
|
||||
center: _center,
|
||||
zoom: 12.0,
|
||||
initialCenter: _center,
|
||||
initialZoom: 12.0,
|
||||
),
|
||||
children: [
|
||||
TileLayer(
|
||||
TileLayer(
|
||||
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
maxZoom: 20,
|
||||
userAgentPackageName:'FamilySafety/1.0.0',
|
||||
),
|
||||
MarkerLayer(
|
||||
markers: [
|
||||
|
||||
Reference in New Issue
Block a user