cambios en topología

This commit is contained in:
Abraham
2025-07-22 20:59:17 -07:00
parent dee9e52ae0
commit 974a757220

View File

@@ -3,7 +3,9 @@ import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:nethive_neo/providers/nethive/empresas_negocios_provider.dart'; import 'package:nethive_neo/providers/nethive/empresas_negocios_provider.dart';
import 'package:nethive_neo/models/nethive/negocio_model.dart';
import 'package:nethive_neo/theme/theme.dart'; import 'package:nethive_neo/theme/theme.dart';
import 'package:nethive_neo/helpers/globals.dart';
class NegociosMapView extends StatefulWidget { class NegociosMapView extends StatefulWidget {
final EmpresasNegociosProvider provider; final EmpresasNegociosProvider provider;
@@ -83,8 +85,8 @@ class _NegociosMapViewState extends State<NegociosMapView>
setState(() { setState(() {
_hoveredNegocioId = negocioId; _hoveredNegocioId = negocioId;
_tooltipPosition = Offset( _tooltipPosition = Offset(
position.dx + 20, // Offset del cursor para evitar interferencia position.dx - 300, // Más cerca del cursor
position.dy - 80, // Arriba del cursor position.dy - 500, // Más cerca del cursor
); );
_showTooltip = true; _showTooltip = true;
}); });
@@ -232,6 +234,7 @@ class _NegociosMapViewState extends State<NegociosMapView>
width: 50, width: 50,
height: 50, height: 50,
child: MouseRegion( child: MouseRegion(
cursor: SystemMouseCursors.click, // Cursor de puntero
onEnter: (event) { onEnter: (event) {
_showTooltipForNegocio(negocio.id, event.position); _showTooltipForNegocio(negocio.id, event.position);
}, },
@@ -240,8 +243,8 @@ class _NegociosMapViewState extends State<NegociosMapView>
if (_hoveredNegocioId == negocio.id) { if (_hoveredNegocioId == negocio.id) {
setState(() { setState(() {
_tooltipPosition = Offset( _tooltipPosition = Offset(
event.position.dx + 20, event.position.dx - 300, // Más cerca del cursor
event.position.dy - 80, event.position.dy - 400, // Más cerca del cursor
); );
}); });
} }
@@ -278,10 +281,8 @@ class _NegociosMapViewState extends State<NegociosMapView>
width: isHovered ? 3 : 2, width: isHovered ? 3 : 2,
), ),
), ),
child: Icon( child: ClipOval(
Icons.store, child: _buildMarkerContent(negocio, isHovered),
color: Colors.white,
size: isHovered ? 22 : 18,
), ),
), ),
); );
@@ -293,6 +294,51 @@ class _NegociosMapViewState extends State<NegociosMapView>
}).toList(); }).toList();
} }
Widget _buildMarkerContent(Negocio negocio, bool isHovered) {
// Verificar si tiene imagen válida
if (negocio.imagenUrl != null && negocio.imagenUrl!.isNotEmpty) {
final imageUrl =
"${supabaseLU.supabaseUrl}/storage/v1/object/public/nethive/imagenes/${negocio.imagenUrl}";
return Image.network(
imageUrl,
fit: BoxFit.cover,
width: 40,
height: 40,
errorBuilder: (context, error, stackTrace) {
// Si falla la imagen, mostrar el ícono
return _buildMarkerIcon(isHovered);
},
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) {
return child;
}
// Mientras carga, mostrar el ícono
return _buildMarkerIcon(isHovered);
},
);
} else {
// Si no hay imagen, mostrar el ícono
return _buildMarkerIcon(isHovered);
}
}
Widget _buildMarkerIcon(bool isHovered) {
return Container(
width: 40,
height: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.transparent,
),
child: Icon(
Icons.store,
color: Colors.white,
size: isHovered ? 22 : 18,
),
);
}
Widget _buildAnimatedTooltip() { Widget _buildAnimatedTooltip() {
final negocio = widget.provider.negocios.firstWhere( final negocio = widget.provider.negocios.firstWhere(
(n) => n.id == _hoveredNegocioId, (n) => n.id == _hoveredNegocioId,
@@ -327,36 +373,68 @@ class _NegociosMapViewState extends State<NegociosMapView>
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
// Header con imagen y nombre
Row( Row(
children: [ children: [
// Imagen del negocio o ícono por defecto
Container( Container(
padding: const EdgeInsets.all(8), width: 50,
height: 50,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2), color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(10),
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 2,
),
), ),
child: Icon( child: ClipRRect(
Icons.store, borderRadius: BorderRadius.circular(8),
color: Colors.white, child: _buildTooltipImage(negocio),
size: 20,
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded( Expanded(
child: Text( child: Column(
negocio.nombre, crossAxisAlignment: CrossAxisAlignment.start,
style: const TextStyle( children: [
color: Colors.white, Text(
fontSize: 16, negocio.nombre,
fontWeight: FontWeight.bold, style: const TextStyle(
), color: Colors.white,
maxLines: 2, fontSize: 16,
overflow: TextOverflow.ellipsis, fontWeight: FontWeight.bold,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(10),
),
child: Text(
negocio.tipoLocal,
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
),
],
), ),
), ),
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
// Información de ubicación
Row( Row(
children: [ children: [
Icon( Icon(
@@ -379,47 +457,55 @@ class _NegociosMapViewState extends State<NegociosMapView>
], ],
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
// Coordenadas
Row( Row(
children: [ children: [
Icon( Icon(
Icons.business, Icons.my_location,
color: Colors.white.withOpacity(0.8), color: Colors.white.withOpacity(0.8),
size: 16, size: 16,
), ),
const SizedBox(width: 6), const SizedBox(width: 6),
Text( Text(
negocio.tipoLocal, 'Lat: ${negocio.latitud.toStringAsFixed(4)}, Lng: ${negocio.longitud.toStringAsFixed(4)}',
style: TextStyle( style: TextStyle(
color: Colors.white.withOpacity(0.9), color: Colors.white.withOpacity(0.9),
fontSize: 14, fontSize: 12,
fontWeight: FontWeight.w500, fontFamily: 'monospace',
), ),
), ),
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
// Call to action
Container( Container(
width: double.infinity,
padding: padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 6), const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2), color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
border: Border.all(
color: Colors.white.withOpacity(0.3),
),
), ),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon( Icon(
Icons.touch_app, Icons.touch_app,
color: Colors.white, color: Colors.white,
size: 14, size: 16,
), ),
const SizedBox(width: 6), const SizedBox(width: 8),
Text( Text(
'Clic para ver infraestructura', 'Clic para ver infraestructura',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 12, fontSize: 13,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w600,
), ),
), ),
], ],
@@ -433,6 +519,60 @@ class _NegociosMapViewState extends State<NegociosMapView>
); );
} }
Widget _buildTooltipImage(Negocio negocio) {
// Verificar si tiene imagen válida
if (negocio.imagenUrl != null && negocio.imagenUrl!.isNotEmpty) {
final imageUrl =
"${supabaseLU.supabaseUrl}/storage/v1/object/public/nethive/imagenes/${negocio.imagenUrl}";
return Image.network(
imageUrl,
fit: BoxFit.cover,
width: 50,
height: 50,
errorBuilder: (context, error, stackTrace) {
// Si falla la imagen, mostrar el ícono
return _buildTooltipIcon();
},
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) {
return child;
}
// Mientras carga, mostrar un spinner
return Center(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
);
},
);
} else {
// Si no hay imagen, mostrar el ícono
return _buildTooltipIcon();
}
}
Widget _buildTooltipIcon() {
return Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.store,
color: Colors.white,
size: 24,
),
);
}
Widget _buildMapHeader() { Widget _buildMapHeader() {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),