feat: Add logger service and improve expense dialog with enhanced receipt management and calculation logic.
This commit is contained in:
@@ -21,19 +21,20 @@ class _MapContentState extends State<MapContent> {
|
||||
bool _isLoadingLocation = false;
|
||||
bool _isSearching = false;
|
||||
Position? _currentPosition;
|
||||
|
||||
|
||||
final Set<Marker> _markers = {};
|
||||
final Set<Circle> _circles = {};
|
||||
|
||||
|
||||
List<PlaceSuggestion> _suggestions = [];
|
||||
|
||||
|
||||
static final String _apiKey = dotenv.env['GOOGLE_MAPS_API_KEY'] ?? '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Si une recherche initiale est fournie, la pré-remplir et lancer la recherche
|
||||
if (widget.initialSearchQuery != null && widget.initialSearchQuery!.isNotEmpty) {
|
||||
if (widget.initialSearchQuery != null &&
|
||||
widget.initialSearchQuery!.isNotEmpty) {
|
||||
_searchController.text = widget.initialSearchQuery!;
|
||||
// Lancer la recherche automatiquement après un court délai pour laisser l'interface se charger
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
@@ -65,17 +66,19 @@ class _MapContentState extends State<MapContent> {
|
||||
'https://maps.googleapis.com/maps/api/place/autocomplete/json'
|
||||
'?input=${Uri.encodeComponent(query)}'
|
||||
'&key=$_apiKey'
|
||||
'&language=fr'
|
||||
'&language=fr',
|
||||
);
|
||||
|
||||
final response = await http.get(url);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
|
||||
|
||||
if (data['status'] == 'OK') {
|
||||
final predictions = data['predictions'] as List;
|
||||
|
||||
|
||||
if (predictions.isNotEmpty) {
|
||||
// Prendre automatiquement la première suggestion
|
||||
final firstPrediction = predictions.first;
|
||||
@@ -83,7 +86,7 @@ class _MapContentState extends State<MapContent> {
|
||||
placeId: firstPrediction['place_id'],
|
||||
description: firstPrediction['description'],
|
||||
);
|
||||
|
||||
|
||||
// Effectuer la sélection automatique
|
||||
await _selectPlaceForInitialSearch(suggestion);
|
||||
} else {
|
||||
@@ -117,9 +120,11 @@ class _MapContentState extends State<MapContent> {
|
||||
|
||||
final response = await http.get(url);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
|
||||
|
||||
if (data['status'] == 'OK') {
|
||||
final location = data['result']['geometry']['location'];
|
||||
final lat = location['lat'];
|
||||
@@ -132,7 +137,7 @@ class _MapContentState extends State<MapContent> {
|
||||
setState(() {
|
||||
// Garder le marqueur de position utilisateur s'il existe
|
||||
_markers.removeWhere((m) => m.markerId.value != 'user_location');
|
||||
|
||||
|
||||
// Ajouter le nouveau marqueur de lieu
|
||||
_markers.add(
|
||||
Marker(
|
||||
@@ -230,9 +235,7 @@ class _MapContentState extends State<MapContent> {
|
||||
const size = 120.0;
|
||||
|
||||
// Dessiner l'icône person_pin_circle en bleu
|
||||
final iconPainter = TextPainter(
|
||||
textDirection: TextDirection.ltr,
|
||||
);
|
||||
final iconPainter = TextPainter(textDirection: TextDirection.ltr);
|
||||
iconPainter.text = TextSpan(
|
||||
text: String.fromCharCode(Icons.person_pin_circle.codePoint),
|
||||
style: TextStyle(
|
||||
@@ -242,26 +245,20 @@ class _MapContentState extends State<MapContent> {
|
||||
),
|
||||
);
|
||||
iconPainter.layout();
|
||||
iconPainter.paint(
|
||||
canvas,
|
||||
Offset(
|
||||
(size - iconPainter.width) / 2,
|
||||
0,
|
||||
),
|
||||
);
|
||||
iconPainter.paint(canvas, Offset((size - iconPainter.width) / 2, 0));
|
||||
|
||||
final picture = pictureRecorder.endRecording();
|
||||
final image = await picture.toImage(size.toInt(), size.toInt());
|
||||
final bytes = await image.toByteData(format: ui.ImageByteFormat.png);
|
||||
|
||||
return BitmapDescriptor.fromBytes(bytes!.buffer.asUint8List());
|
||||
return BitmapDescriptor.bytes(bytes!.buffer.asUint8List());
|
||||
}
|
||||
|
||||
// Ajouter le marqueur avec l'icône personnalisée
|
||||
Future<void> _addUserLocationMarker(LatLng position) async {
|
||||
_markers.clear();
|
||||
_circles.clear();
|
||||
|
||||
|
||||
// Ajouter un cercle de précision
|
||||
_circles.add(
|
||||
Circle(
|
||||
@@ -284,10 +281,14 @@ class _MapContentState extends State<MapContent> {
|
||||
markerId: const MarkerId('user_location'),
|
||||
position: position,
|
||||
icon: icon,
|
||||
anchor: const Offset(0.5, 0.85), // Ancrer au bas de l'icône (le point du pin)
|
||||
anchor: const Offset(
|
||||
0.5,
|
||||
0.85,
|
||||
), // Ancrer au bas de l'icône (le point du pin)
|
||||
infoWindow: InfoWindow(
|
||||
title: 'Ma position',
|
||||
snippet: 'Lat: ${position.latitude.toStringAsFixed(4)}, Lng: ${position.longitude.toStringAsFixed(4)}',
|
||||
snippet:
|
||||
'Lat: ${position.latitude.toStringAsFixed(4)}, Lng: ${position.longitude.toStringAsFixed(4)}',
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -311,23 +312,27 @@ class _MapContentState extends State<MapContent> {
|
||||
'https://maps.googleapis.com/maps/api/place/autocomplete/json'
|
||||
'?input=${Uri.encodeComponent(query)}'
|
||||
'&key=$_apiKey'
|
||||
'&language=fr'
|
||||
'&language=fr',
|
||||
);
|
||||
|
||||
final response = await http.get(url);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
|
||||
|
||||
if (data['status'] == 'OK') {
|
||||
final predictions = data['predictions'] as List;
|
||||
|
||||
|
||||
setState(() {
|
||||
_suggestions = predictions
|
||||
.map((p) => PlaceSuggestion(
|
||||
placeId: p['place_id'],
|
||||
description: p['description'],
|
||||
))
|
||||
.map(
|
||||
(p) => PlaceSuggestion(
|
||||
placeId: p['place_id'],
|
||||
description: p['description'],
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
_isSearching = false;
|
||||
});
|
||||
@@ -363,9 +368,11 @@ class _MapContentState extends State<MapContent> {
|
||||
|
||||
final response = await http.get(url);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
|
||||
|
||||
if (data['status'] == 'OK') {
|
||||
final location = data['result']['geometry']['location'];
|
||||
final lat = location['lat'];
|
||||
@@ -378,7 +385,7 @@ class _MapContentState extends State<MapContent> {
|
||||
setState(() {
|
||||
// Garder le marqueur de position utilisateur
|
||||
_markers.removeWhere((m) => m.markerId.value != 'user_location');
|
||||
|
||||
|
||||
// Ajouter le nouveau marqueur de lieu
|
||||
_markers.add(
|
||||
Marker(
|
||||
@@ -394,7 +401,9 @@ class _MapContentState extends State<MapContent> {
|
||||
CameraUpdate.newLatLngZoom(newPosition, 15),
|
||||
);
|
||||
|
||||
FocusScope.of(context).unfocus();
|
||||
if (mounted) {
|
||||
FocusScope.of(context).unfocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -545,7 +554,10 @@ class _MapContentState extends State<MapContent> {
|
||||
: Icon(Icons.search, color: Colors.grey[700]),
|
||||
suffixIcon: _searchController.text.isNotEmpty
|
||||
? IconButton(
|
||||
icon: Icon(Icons.clear, color: Colors.grey[700]),
|
||||
icon: Icon(
|
||||
Icons.clear,
|
||||
color: Colors.grey[700],
|
||||
),
|
||||
onPressed: () {
|
||||
_searchController.clear();
|
||||
setState(() {
|
||||
@@ -567,7 +579,8 @@ class _MapContentState extends State<MapContent> {
|
||||
),
|
||||
onChanged: (value) {
|
||||
// Ne pas rechercher si c'est juste le remplissage initial
|
||||
if (widget.initialSearchQuery != null && value == widget.initialSearchQuery) {
|
||||
if (widget.initialSearchQuery != null &&
|
||||
value == widget.initialSearchQuery) {
|
||||
return;
|
||||
}
|
||||
_searchPlaces(value);
|
||||
@@ -601,10 +614,8 @@ class _MapContentState extends State<MapContent> {
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: _suggestions.length,
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
height: 1,
|
||||
color: Colors.grey[300],
|
||||
),
|
||||
separatorBuilder: (context, index) =>
|
||||
Divider(height: 1, color: Colors.grey[300]),
|
||||
itemBuilder: (context, index) {
|
||||
final suggestion = _suggestions[index];
|
||||
return InkWell(
|
||||
@@ -664,8 +675,5 @@ class PlaceSuggestion {
|
||||
final String placeId;
|
||||
final String description;
|
||||
|
||||
PlaceSuggestion({
|
||||
required this.placeId,
|
||||
required this.description,
|
||||
});
|
||||
}
|
||||
PlaceSuggestion({required this.placeId, required this.description});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user