feat: Add location permissions and implement current location retrieval in map component
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
|
||||
class MapContent extends StatefulWidget {
|
||||
const MapContent({super.key});
|
||||
@@ -9,22 +10,100 @@ class MapContent extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _MapContentState extends State<MapContent> {
|
||||
final LatLng _initialPosition = const LatLng(48.8566, 2.3522); // Paris
|
||||
GoogleMapController? _mapController;
|
||||
LatLng _initialPosition = const LatLng(48.8566, 2.3522); // Paris par défaut
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
bool _isLoadingLocation = false;
|
||||
Position? _currentPosition;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getCurrentLocation();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.dispose();
|
||||
_mapController?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// Obtenir la position actuelle
|
||||
Future<void> _getCurrentLocation() async {
|
||||
setState(() {
|
||||
_isLoadingLocation = true;
|
||||
});
|
||||
|
||||
try {
|
||||
// Vérifier si la localisation est activée
|
||||
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
_showError('Veuillez activer les services de localisation');
|
||||
setState(() {
|
||||
_isLoadingLocation = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifier les permissions
|
||||
LocationPermission permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
_showError('Permission de localisation refusée');
|
||||
setState(() {
|
||||
_isLoadingLocation = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
_showError('Permission de localisation refusée définitivement');
|
||||
setState(() {
|
||||
_isLoadingLocation = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Obtenir la position actuelle
|
||||
Position position = await Geolocator.getCurrentPosition(
|
||||
desiredAccuracy: LocationAccuracy.high,
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_currentPosition = position;
|
||||
_initialPosition = LatLng(position.latitude, position.longitude);
|
||||
_isLoadingLocation = false;
|
||||
});
|
||||
|
||||
// Animer la caméra vers la position actuelle
|
||||
if (_mapController != null) {
|
||||
_mapController!.animateCamera(
|
||||
CameraUpdate.newLatLngZoom(_initialPosition, 14),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
_showError('Erreur lors de la récupération de la position: $e');
|
||||
setState(() {
|
||||
_isLoadingLocation = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _showError(String message) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(message), backgroundColor: Colors.red),
|
||||
);
|
||||
}
|
||||
|
||||
void _searchLocation() {
|
||||
// TODO: Implémenter la logique de recherche
|
||||
final searchQuery = _searchController.text.trim();
|
||||
if (searchQuery.isNotEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Recherche de: $searchQuery')),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Recherche de: $searchQuery')));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,65 +129,62 @@ class _MapContentState extends State<MapContent> {
|
||||
),
|
||||
onSubmitted: (_) => _searchLocation(),
|
||||
),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Bouton chercher
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 48,
|
||||
child: ElevatedButton(
|
||||
onPressed: _searchLocation,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'Chercher',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Container avec la carte
|
||||
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Google Maps
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16.0),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.1),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
child: Stack(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: GoogleMap(
|
||||
initialCameraPosition: CameraPosition(
|
||||
target: _initialPosition,
|
||||
zoom: 14,
|
||||
),
|
||||
onMapCreated: (GoogleMapController controller) {
|
||||
_mapController = controller;
|
||||
},
|
||||
myLocationEnabled: true,
|
||||
myLocationButtonEnabled: false,
|
||||
zoomControlsEnabled: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16.0),
|
||||
child: GoogleMap(
|
||||
mapType: MapType.normal,
|
||||
initialCameraPosition: CameraPosition(
|
||||
target: _initialPosition,
|
||||
zoom: 12,
|
||||
),
|
||||
onMapCreated: (GoogleMapController controller) {},
|
||||
myLocationEnabled: true,
|
||||
myLocationButtonEnabled: true,
|
||||
zoomControlsEnabled: true,
|
||||
compassEnabled: true,
|
||||
),
|
||||
),
|
||||
|
||||
// Indicateur de chargement
|
||||
if (_isLoadingLocation)
|
||||
Center(
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CircularProgressIndicator(),
|
||||
SizedBox(height: 8),
|
||||
Text('Localisation en cours...'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Bouton flottant pour recentrer sur la position actuelle
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _getCurrentLocation,
|
||||
tooltip: 'Ma position',
|
||||
child: Icon(Icons.my_location),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,16 +53,16 @@ class MyApp extends StatelessWidget {
|
||||
create: (context) => ThemeBloc()..add(ThemeLoadRequested()),
|
||||
),
|
||||
BlocProvider<AuthBloc>(
|
||||
create: (context) => AuthBloc(
|
||||
authRepository: context.read<AuthRepository>(),
|
||||
)..add(AuthCheckRequested()),
|
||||
create: (context) =>
|
||||
AuthBloc(authRepository: context.read<AuthRepository>())
|
||||
..add(AuthCheckRequested()),
|
||||
),
|
||||
BlocProvider(create: (context) => GroupBloc(
|
||||
context.read<GroupRepository>(),
|
||||
)),
|
||||
BlocProvider(create: (context) => TripBloc(
|
||||
tripRepository: context.read<TripRepository>(),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) => GroupBloc(context.read<GroupRepository>()),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) =>
|
||||
TripBloc(tripRepository: context.read<TripRepository>()),
|
||||
),
|
||||
BlocProvider(create: (context) => UserBloc()),
|
||||
],
|
||||
@@ -86,14 +86,7 @@ class MyApp extends StatelessWidget {
|
||||
),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: BlocBuilder<AuthBloc, AuthState>(
|
||||
builder: (context, authState) {
|
||||
if (authState is AuthAuthenticated) {
|
||||
return const HomePage();
|
||||
}
|
||||
return const LoginPage();
|
||||
},
|
||||
),
|
||||
home: const LoginPage(),
|
||||
routes: {
|
||||
'/login': (context) => const LoginPage(),
|
||||
'/signup': (context) => const SignUpPage(),
|
||||
|
||||
Reference in New Issue
Block a user