Files
geo_front/lib/screens/map_screen.dart
T

255 lines
7.1 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:geolocator/geolocator.dart';
import 'package:provider/provider.dart';
import '../providers/auth_provider.dart';
import '../providers/map_provider.dart';
import '../providers/share_provider.dart';
import '../services/geo_service.dart';
class MapScreen extends StatefulWidget {
const MapScreen({super.key});
@override
State<MapScreen> createState() => _MapScreenState();
}
class _MapScreenState extends State<MapScreen> {
LatLng _center = const LatLng(40.7128, -74.0060);
bool _loading = true;
Timer? _trackingTimer;
Timer? _geoTimer;
@override
void initState() {
super.initState();
_initLocationAndShare();
}
@override
void dispose() {
_trackingTimer?.cancel();
_geoTimer?.cancel();
super.dispose();
}
Future<void> _initLocationAndShare() async {
try {
if (!await Geolocator.isLocationServiceEnabled()) {
setState(() => _loading = false);
return;
}
final position = await Geolocator.getCurrentPosition();
setState(() {
_center = LatLng(position.latitude, position.longitude);
});
if (!mounted) return;
final authProvider = context.read<AuthProvider>();
final shareProvider = context.read<ShareProvider>();
if (authProvider.token.isNotEmpty) {
await shareProvider.createShare(
authProvider.token,
position.longitude,
position.latitude,
);
}
if (!mounted) return;
setState(() => _loading = false);
_geoTimer = Timer.periodic(
const Duration(seconds: 30),
(_) => _updateGeolocation(),
);
} catch (_) {
setState(() => _loading = false);
}
}
Future<void> _updateGeolocation() async {
try {
final position = await Geolocator.getCurrentPosition();
setState(() {
_center = LatLng(position.latitude, position.longitude);
});
if (!mounted) return;
final authProvider = context.read<AuthProvider>();
final shareProvider = context.read<ShareProvider>();
await GeoService().updatePosition(
authProvider.token,
shareProvider.geoId,
position.longitude,
position.latitude,
);
} catch (_) {}
}
void _copyShareLink() {
final shareProvider = context.read<ShareProvider>();
if (shareProvider.shareId.isNotEmpty) {
Clipboard.setData(ClipboardData(text: 'watch/${shareProvider.shareId}'));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Ссылка скопирована')),
);
}
}
void _startTracking() {
final authProvider = context.read<AuthProvider>();
final shareProvider = context.read<ShareProvider>();
if (shareProvider.trackingShareId.isEmpty) {
showDialog(
context: context,
builder: (context) {
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(),
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Отмена'),
),
TextButton(
onPressed: () async {
final shareId = _controller.text.trim();
if (shareId.isNotEmpty) {
Navigator.pop(context);
final token = authProvider.token;
await shareProvider.startTracking(shareId, token);
_trackingTimer = Timer.periodic(
const Duration(seconds: 5),
(_) => shareProvider.updateTrackedPosition(),
);
}
},
child: const Text('Начать'),
),
],
);
},
);
} else {
shareProvider.stopTracking();
_trackingTimer?.cancel();
}
}
@override
Widget build(BuildContext context) {
final authProvider = context.watch<AuthProvider>();
final _ = context.watch<MapProvider>();
final shareProvider = context.watch<ShareProvider>();
if (_loading) {
return Scaffold(
body: const Center(child: CircularProgressIndicator()),
);
}
Marker? trackedMarker;
if (shareProvider.trackedPosition != null) {
final pos = shareProvider.trackedPosition!;
final lat = pos['y'] as double;
final lng = pos['x'] as double;
trackedMarker = Marker(
point: LatLng(lat, lng),
width: 40,
height: 40,
child: const Icon(
Icons.person,
color: Colors.red,
size: 30,
),
);
}
return Scaffold(
appBar: AppBar(
title: Text(
'Family Safety Map\n${_center.latitude.toStringAsFixed(4)}, ${_center.longitude.toStringAsFixed(4)}',
textAlign: TextAlign.center,
),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _updateGeolocation,
),
IconButton(
icon: const Icon(Icons.share),
onPressed: _copyShareLink,
),
IconButton(
icon: Icon(
shareProvider.trackingShareId.isEmpty
? Icons.person_search
: Icons.person,
color: shareProvider.trackingShareId.isEmpty
? null
: Colors.red,
),
onPressed: _startTracking,
),
IconButton(
icon: const Icon(Icons.logout),
onPressed: () {
_geoTimer?.cancel();
authProvider.logout();
Navigator.of(context).pushReplacementNamed('/');
},
),
],
),
body: FlutterMap(
options: MapOptions(
center: _center,
zoom: 12.0,
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
),
MarkerLayer(
markers: [
Marker(
point: _center,
width: 40,
height: 40,
child: const Icon(
Icons.my_location,
color: Colors.blue,
size: 30,)
),
if (trackedMarker != null) trackedMarker,
],
),
],
),
);
}
}