diff --git a/lib/constants/app_strings.dart b/lib/constants/app_strings.dart index cf77044..e985c5a 100644 --- a/lib/constants/app_strings.dart +++ b/lib/constants/app_strings.dart @@ -10,6 +10,14 @@ class AppStrings { static const String history = "Histórico"; static const String notifications = "Notificações"; static const String profile = "Perfil"; + static const String welcome = "Bem-vindo"; + static const String myActivities = "Minhas Atividades"; + static const String bestDistance = "Melhor Distância"; + static const String bestSpeed = "Velocidade Máxima"; + static const String connectDevice = "Conectar Dispositivo"; + static const String viewMap = "Ver Mapa"; + static const String dailyGoal = "Meta Diária"; + static const String startTraining = "INICIAR TREINO"; // Bluetooth Screen static const String bluetoothTitle = "DISPOSITIVOS"; @@ -58,6 +66,10 @@ class AppStrings { static const String markDestination = "Marcar Destino"; static const String chooseRoute = "Escolher Rota"; static const String confirmDestination = "Confirmar Destino?"; + static const String startRunQuestion = "Iniciar Corrida?"; + static const String startRunDescription = "Deseja começar o monitoramento agora?"; + static const String prepare = "PREPARAR"; + static const String maxSpeed = "VELOCIDADE MÁX"; static const String cancel = "Cancelar"; static const String yes = "Sim"; static const String runFinished = "Corrida Finalizada!"; diff --git a/lib/screens/bluethoot_cpnnection_screen.dart b/lib/screens/bluetooth_connection_screen.dart similarity index 100% rename from lib/screens/bluethoot_cpnnection_screen.dart rename to lib/screens/bluetooth_connection_screen.dart diff --git a/lib/screens/google_map_screen.dart b/lib/screens/google_map_screen.dart index e0d337f..be6a795 100644 --- a/lib/screens/google_map_screen.dart +++ b/lib/screens/google_map_screen.dart @@ -4,7 +4,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:geolocator/geolocator.dart'; -import 'package:flutter_polyline_points/flutter_polyline_points.dart'; import '../constants/app_colors.dart'; import '../constants/app_strings.dart'; @@ -16,34 +15,28 @@ class GoogleMapScreen extends StatefulWidget { } class _GoogleMapScreenState extends State { - // CONFIGURAÇÃO: Insira aqui sua chave da Google Cloud com Directions API ativa - final String googleApiKey = "AIzaSyCk84rxmF044cxKLABf55rEKHDqOcyoV5k"; - GoogleMapController? _mapController; StreamSubscription? _positionStreamSubscription; Timer? _stopwatchTimer; DateTime? _lastUpdate; final List _routePoints = []; - List _remainingPlannedPoints = []; // Lista para o trajeto que encurta final Set _polylines = {}; final Set _markers = {}; - LatLng? _plannedEnd; - bool _isPlanningMode = false; bool _isRunning = false; bool _isLoading = true; double _currentSpeed = 0.0; + double _maxSpeed = 0.0; double _totalDistance = 0.0; - LatLng _currentPosition = const LatLng(38.7223, -9.1393); + LatLng _currentPosition = const LatLng(0, 0); int _secondsElapsed = 0; int _countdownValue = 3; bool _isCountingDown = false; BitmapDescriptor? _arrowIcon; - BitmapDescriptor? _finishIcon; @override void initState() { @@ -52,36 +45,11 @@ class _GoogleMapScreenState extends State { } Future _setupIconsAndTracking() async { - _finishIcon = await _createPremiumMarker(AppColors.coral, Icons.flag_rounded, 65); - // Borda agora é proporcional ao tamanho (25). - _arrowIcon = await _createArrowMarker(Colors.black, Colors.white, 85); + // Icons reduced by ~70% as requested + _arrowIcon = await _createArrowMarker(Colors.black, Colors.white, 25); await _initTracking(); } - Future _createPremiumMarker(Color color, IconData icon, double size) async { - final ui.PictureRecorder recorder = ui.PictureRecorder(); - final Canvas canvas = Canvas(recorder); - final Paint shadowPaint = Paint() - ..color = Colors.black.withOpacity(0.4) - ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 6); - canvas.drawCircle(Offset(size / 2, size / 2 + 3), size / 2, shadowPaint); - canvas.drawCircle(Offset(size / 2, size / 2), size / 2, Paint()..color = Colors.white); - canvas.drawCircle(Offset(size / 2, size / 2), size / 2 - 5, Paint()..color = color); - TextPainter textPainter = TextPainter(textDirection: TextDirection.ltr); - textPainter.text = TextSpan( - text: String.fromCharCode(icon.codePoint), - style: TextStyle( - fontSize: size * 0.6, - fontFamily: icon.fontFamily, - color: Colors.white, - fontWeight: FontWeight.bold)); - textPainter.layout(); - textPainter.paint(canvas, Offset((size - textPainter.width) / 2, (size - textPainter.height) / 2)); - final ui.Image image = await recorder.endRecording().toImage(size.toInt(), size.toInt() + 6); - final ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png); - return BitmapDescriptor.bytes(byteData!.buffer.asUint8List()); - } - Future _createArrowMarker(Color color, Color borderColor, double size) async { final ui.PictureRecorder recorder = ui.PictureRecorder(); final Canvas canvas = Canvas(recorder); @@ -93,17 +61,16 @@ class _GoogleMapScreenState extends State { path.close(); canvas.drawPath( - path.shift(const Offset(0, 4)), + path.shift(const Offset(0, 2)), Paint() ..color = Colors.black38 - ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 4)); + ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 2)); canvas.drawPath(path, Paint()..color = color); - // CORREÇÃO: Borda escala proporcionalmente (12% do tamanho) double strokeWidth = size * 0.12; canvas.drawPath(path, Paint()..color = borderColor..style = ui.PaintingStyle.stroke..strokeWidth = strokeWidth); - final ui.Image image = await recorder.endRecording().toImage(size.toInt(), size.toInt() + 6); + final ui.Image image = await recorder.endRecording().toImage(size.toInt(), size.toInt() + 4); final ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png); return BitmapDescriptor.bytes(byteData!.buffer.asUint8List()); } @@ -126,21 +93,19 @@ class _GoogleMapScreenState extends State { } Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.bestForNavigation); _currentPosition = LatLng(position.latitude, position.longitude); - - // Adiciona ponto inicial para permitir rotação imediata _routePoints.add(_currentPosition); setState(() => _isLoading = false); - _updateMarkers(); // Força a exibição imediata + _updateMarkers(); _startLocationStream(); } void _startLocationStream() { _positionStreamSubscription = Geolocator.getPositionStream( - locationSettings: const LocationSettings(accuracy: LocationAccuracy.bestForNavigation, distanceFilter: 0) + locationSettings: const LocationSettings(accuracy: LocationAccuracy.bestForNavigation, distanceFilter: 1) ).listen((Position position) { final now = DateTime.now(); - if (_lastUpdate == null || now.difference(_lastUpdate!).inMilliseconds > 500) { + if (_lastUpdate == null || now.difference(_lastUpdate!).inMilliseconds > 1000) { _lastUpdate = now; _updatePosition(LatLng(position.latitude, position.longitude), position.speed); } @@ -150,45 +115,30 @@ class _GoogleMapScreenState extends State { void _updatePosition(LatLng newPoint, double speed) { if (!mounted) return; setState(() { + double currentSpeedKmh = speed * 3.6; + if (currentSpeedKmh < 0) currentSpeedKmh = 0; + + _currentSpeed = currentSpeedKmh; + if (_isRunning) { - if (_routePoints.isNotEmpty) { - _totalDistance += Geolocator.distanceBetween(_currentPosition.latitude, _currentPosition.longitude, newPoint.latitude, newPoint.longitude); + if (currentSpeedKmh > _maxSpeed) { + _maxSpeed = currentSpeedKmh; } + + _totalDistance += Geolocator.distanceBetween( + _currentPosition.latitude, _currentPosition.longitude, + newPoint.latitude, newPoint.longitude + ); + _routePoints.add(newPoint); _updateTraveledPolylines(); - - // Lógica para consumir o trajeto planejado à medida que passamos por ele - if (_remainingPlannedPoints.isNotEmpty) { - while (_remainingPlannedPoints.length > 1) { - double distanceToPoint = Geolocator.distanceBetween( - newPoint.latitude, newPoint.longitude, - _remainingPlannedPoints[0].latitude, _remainingPlannedPoints[0].longitude - ); - // Se estivermos a menos de 15m do ponto do trajeto, removemo-lo - if (distanceToPoint < 15) { - _remainingPlannedPoints.removeAt(0); - } else { - break; - } - } - _updateRemainingPolyline(); - } } else { - // Antes da corrida, mantém os últimos pontos para calcular a rotação if (_routePoints.length >= 2) _routePoints.removeAt(0); _routePoints.add(newPoint); } - _currentSpeed = speed >= 0 ? speed : 0; _currentPosition = newPoint; _updateMarkers(); - - if (_plannedEnd != null && _isRunning) { - double distToEnd = Geolocator.distanceBetween(newPoint.latitude, newPoint.longitude, _plannedEnd!.latitude, _plannedEnd!.longitude); - if (distToEnd < 15) { - _finishRun(); - } - } }); if (_mapController != null) { @@ -198,30 +148,29 @@ class _GoogleMapScreenState extends State { void _updateTraveledPolylines() { if (_routePoints.length > 1) { - _polylines.removeWhere((p) => p.polylineId.value == 'route' || p.polylineId.value == 'route_glow'); - _polylines.add(Polyline(polylineId: const PolylineId('route_glow'), points: List.from(_routePoints), color: Colors.cyanAccent.withOpacity(0.3), width: 14, zIndex: 9)); - _polylines.add(Polyline(polylineId: const PolylineId('route'), points: List.from(_routePoints), color: Colors.white, width: 6, patterns: [PatternItem.dot, PatternItem.gap(15)], jointType: JointType.round, zIndex: 10)); - } - } - - void _updateRemainingPolyline() { - _polylines.removeWhere((p) => p.polylineId.value == 'planned_preview'); - if (_remainingPlannedPoints.isNotEmpty) { - // A linha coral começa na posição atual do usuário e segue o que resta - List pointsToDraw = [_currentPosition, ..._remainingPlannedPoints]; + _polylines.clear(); + // Glow effect for the trail _polylines.add(Polyline( - polylineId: const PolylineId('planned_preview'), - points: pointsToDraw, - color: AppColors.coral.withOpacity(0.5), - width: 4, - patterns: [PatternItem.dash(20), PatternItem.gap(10)], - zIndex: 8, + polylineId: const PolylineId('route_glow'), + points: List.from(_routePoints), + color: AppColors.coral.withOpacity(0.3), + width: 12, + zIndex: 9 + )); + // Main trail line + _polylines.add(Polyline( + polylineId: const PolylineId('route'), + points: List.from(_routePoints), + color: AppColors.coral, + width: 5, + jointType: JointType.round, + zIndex: 10 )); } } void _updateMarkers() { - _markers.removeWhere((m) => m.markerId.value == 'follower'); + _markers.clear(); _markers.add(Marker( markerId: const MarkerId('follower'), position: _currentPosition, @@ -240,97 +189,23 @@ class _GoogleMapScreenState extends State { return Geolocator.bearingBetween(p1.latitude, p1.longitude, p2.latitude, p2.longitude); } - Future> _getRoutePolyline(LatLng start, LatLng end) async { - try { - PolylinePoints polylinePoints = PolylinePoints(); - PolylineResult result = await polylinePoints.getRouteBetweenCoordinates( - request: PolylineRequest( - origin: PointLatLng(start.latitude, start.longitude), - destination: PointLatLng(end.latitude, end.longitude), - mode: TravelMode.walking, - ), - googleApiKey: googleApiKey, - ); - - List coords = []; - if (result.points.isNotEmpty) { - for (var point in result.points) { - coords.add(LatLng(point.latitude, point.longitude)); - } - } - return coords; - } catch (e) { - debugPrint("Erro ao buscar rota: $e"); - return []; - } - } - - void _onMapTap(LatLng point) async { - if (!_isPlanningMode) return; - - setState(() { - _plannedEnd = point; - _markers.removeWhere((m) => m.markerId.value == 'planned_end'); - _markers.add(Marker(markerId: const MarkerId('planned_end'), position: point, icon: _finishIcon ?? BitmapDescriptor.defaultMarker, zIndex: 5, infoWindow: InfoWindow(title: AppStrings.finishPoint))); - }); - - List streetPoints = await _getRoutePolyline(_currentPosition, point); - if (streetPoints.isEmpty) { - streetPoints = [_currentPosition, point]; - } - - setState(() { - _remainingPlannedPoints = List.from(streetPoints); - _updateRemainingPolyline(); - }); - - _showConfirmationDialog(); - } - - void _showStartOptions() { - showModalBottomSheet( - context: context, - backgroundColor: Colors.transparent, - builder: (context) => Container( - margin: const EdgeInsets.all(20), - decoration: BoxDecoration(color: AppColors.backgroundGrey, borderRadius: BorderRadius.circular(30), border: Border.all(color: Colors.white10)), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 15), - Container(width: 40, height: 4, decoration: BoxDecoration(color: Colors.white24, borderRadius: BorderRadius.circular(2))), - const SizedBox(height: 25), - _buildOptionTile(Icons.location_on_rounded, AppStrings.markDestination, () { - Navigator.pop(context); - setState(() => _isPlanningMode = true); - }), - const Divider(color: Colors.white10, indent: 20, endIndent: 20), - _buildOptionTile(Icons.history_rounded, AppStrings.chooseRoute, () { - Navigator.pop(context); - }), - const SizedBox(height: 20), - ], - ), - ), - ); - } - - void _showConfirmationDialog() { + void _showStartConfirmation() { showDialog( context: context, builder: (context) => AlertDialog( backgroundColor: AppColors.backgroundGrey, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)), - title: Text(AppStrings.confirmDestination, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), + title: const Text("Iniciar Corrida?", style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), + content: const Text("Deseja começar o monitoramento agora?", style: TextStyle(color: Colors.white70)), actions: [ - TextButton(onPressed: () => Navigator.pop(context), child: Text(AppStrings.cancel, style: const TextStyle(color: Colors.white54))), + TextButton(onPressed: () => Navigator.pop(context), child: const Text(AppStrings.cancel, style: TextStyle(color: Colors.white54))), ElevatedButton( onPressed: () { Navigator.pop(context); _startCountdown(); }, style: ElevatedButton.styleFrom(backgroundColor: AppColors.coral, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))), - child: Text(AppStrings.yes, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), + child: const Text(AppStrings.yes, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), ), ], ), @@ -341,7 +216,6 @@ class _GoogleMapScreenState extends State { setState(() { _isCountingDown = true; _countdownValue = 3; - _isPlanningMode = false; }); Timer.periodic(const Duration(seconds: 1), (timer) { @@ -359,9 +233,11 @@ class _GoogleMapScreenState extends State { _isCountingDown = false; _isRunning = true; _totalDistance = 0.0; + _maxSpeed = 0.0; _secondsElapsed = 0; _routePoints.clear(); _routePoints.add(_currentPosition); + _polylines.clear(); }); _stopwatchTimer = Timer.periodic(const Duration(seconds: 1), (timer) { @@ -371,40 +247,127 @@ class _GoogleMapScreenState extends State { void _finishRun() { _stopwatchTimer?.cancel(); + final finalRoute = List.from(_routePoints); + final finalDistance = _totalDistance; + final finalTime = _secondsElapsed; + final finalMaxSpeed = _maxSpeed; + setState(() { _isRunning = false; - _remainingPlannedPoints.clear(); - _polylines.removeWhere((p) => p.polylineId.value == 'planned_preview'); }); - showDialog( + showGeneralDialog( context: context, barrierDismissible: false, - builder: (context) => AlertDialog( - backgroundColor: AppColors.backgroundGrey, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.check_circle_rounded, color: Colors.greenAccent, size: 70), - const SizedBox(height: 20), - Text(AppStrings.runFinished, style: const TextStyle(color: Colors.white, fontSize: 22, fontWeight: FontWeight.w900)), - const SizedBox(height: 25), - _buildResultRow(AppStrings.totalDistance, _formatDistance(_totalDistance)), - const SizedBox(height: 15), - _buildResultRow(AppStrings.totalTime, _formatTime(_secondsElapsed)), - const SizedBox(height: 30), - SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: () => Navigator.pop(context), - style: ElevatedButton.styleFrom(backgroundColor: AppColors.coral, padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))), - child: Text(AppStrings.close, style: const TextStyle(fontWeight: FontWeight.bold)), + barrierLabel: "Resultados", + pageBuilder: (context, anim1, anim2) => const SizedBox(), + transitionBuilder: (context, anim1, anim2, child) { + return FadeTransition( + opacity: anim1, + child: ScaleTransition( + scale: anim1, + child: AlertDialog( + backgroundColor: AppColors.background, + contentPadding: EdgeInsets.zero, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(35), side: const BorderSide(color: Colors.white10, width: 2)), + content: Container( + width: MediaQuery.of(context).size.width * 0.85, + padding: const EdgeInsets.all(25), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.all(15), + decoration: const BoxDecoration(color: AppColors.coral, shape: BoxShape.circle), + child: const Icon(Icons.emoji_events_rounded, color: Colors.white, size: 40), + ), + const SizedBox(height: 20), + const Text(AppStrings.runFinished, style: TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.w900, letterSpacing: 1)), + const SizedBox(height: 30), + + // Mini Map Container + Container( + height: 180, + decoration: BoxDecoration( + color: AppColors.backgroundGrey, + borderRadius: BorderRadius.circular(25), + border: Border.all(color: Colors.white10), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(23), + child: GoogleMap( + initialCameraPosition: CameraPosition(target: finalRoute.isNotEmpty ? finalRoute.first : _currentPosition, zoom: 15), + onMapCreated: (controller) { + if (finalRoute.isNotEmpty) { + Future.delayed(const Duration(milliseconds: 500), () { + controller.animateCamera(CameraUpdate.newLatLngBounds( + _getBounds(finalRoute), 40 + )); + }); + } + }, + polylines: { + Polyline(polylineId: const PolylineId('final_path'), points: finalRoute, color: AppColors.coral, width: 4) + }, + zoomControlsEnabled: false, + myLocationButtonEnabled: false, + compassEnabled: false, + mapToolbarEnabled: false, + liteModeEnabled: true, + ), + ), + ), + const SizedBox(height: 25), + + _buildResultRow(AppStrings.totalDistance, _formatDistance(finalDistance)), + const Divider(color: Colors.white10, height: 25), + _buildResultRow(AppStrings.totalTime, _formatTime(finalTime)), + const Divider(color: Colors.white10, height: 25), + _buildResultRow("VELOCIDADE MÁX", "${finalMaxSpeed.toStringAsFixed(1)} ${AppStrings.kmhUnit}"), + + const SizedBox(height: 35), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () { + Navigator.pop(context); + setState(() { + _polylines.clear(); + _routePoints.clear(); + _routePoints.add(_currentPosition); + }); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + foregroundColor: AppColors.background, + padding: const EdgeInsets.symmetric(vertical: 18), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + elevation: 0, + ), + child: Text(AppStrings.close.toUpperCase(), style: const TextStyle(fontWeight: FontWeight.w900, letterSpacing: 2)), + ), + ), + ], + ), ), ), - ], - ), - ), + ), + ); + }, + ); + } + + LatLngBounds _getBounds(List points) { + double? minLat, maxLat, minLng, maxLng; + for (LatLng p in points) { + if (minLat == null || p.latitude < minLat) minLat = p.latitude; + if (maxLat == null || p.latitude > maxLat) maxLat = p.latitude; + if (minLng == null || p.longitude < minLng) minLng = p.longitude; + if (maxLng == null || p.longitude > maxLng) maxLng = p.longitude; + } + return LatLngBounds( + southwest: LatLng(minLat!, minLng!), + northeast: LatLng(maxLat!, maxLng!), ); } @@ -417,34 +380,77 @@ class _GoogleMapScreenState extends State { int hours = seconds ~/ 3600; int minutes = (seconds % 3600) ~/ 60; int remainingSeconds = seconds % 60; - return "${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}"; + if (hours > 0) { + return "${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}"; + } + return "${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}"; } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.background, - appBar: AppBar(title: Text(_isRunning ? AppStrings.mapTitleRunning : AppStrings.mapTitlePlanning, style: const TextStyle(fontWeight: FontWeight.w900, color: Colors.white, letterSpacing: 2)), centerTitle: true, backgroundColor: Colors.transparent, elevation: 0, leading: IconButton(icon: const Icon(Icons.arrow_back_ios_new, color: Colors.white), onPressed: () => Navigator.pop(context))), + appBar: AppBar( + title: Text( + _isRunning ? AppStrings.mapTitleRunning : AppStrings.appTitle.toUpperCase(), + style: const TextStyle(fontWeight: FontWeight.w900, color: Colors.white, letterSpacing: 2, fontSize: 18) + ), + centerTitle: true, + backgroundColor: Colors.transparent, + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back_ios_new, color: Colors.white), + onPressed: () => Navigator.pop(context) + ) + ), extendBodyBehindAppBar: true, body: Stack( children: [ + // Background Map Container Center( child: Container( - width: MediaQuery.of(context).size.width * 0.94, height: MediaQuery.of(context).size.height * 0.7, - decoration: BoxDecoration(color: AppColors.backgroundGrey, borderRadius: BorderRadius.circular(55), border: Border.all(color: Colors.white10, width: 3), boxShadow: const [BoxShadow(color: Colors.black54, blurRadius: 40)]), - child: ClipRRect(borderRadius: BorderRadius.circular(52), child: _isLoading ? const Center(child: CircularProgressIndicator(color: AppColors.coral)) : GoogleMap(initialCameraPosition: CameraPosition(target: _currentPosition, zoom: 17.5), onMapCreated: (controller) => _mapController = controller, onTap: _onMapTap, markers: _markers, polylines: _polylines, zoomControlsEnabled: false, myLocationButtonEnabled: false, compassEnabled: false, mapToolbarEnabled: false)) + width: MediaQuery.of(context).size.width * 0.94, + height: MediaQuery.of(context).size.height * 0.72, + decoration: BoxDecoration( + color: AppColors.backgroundGrey, + borderRadius: BorderRadius.circular(55), + border: Border.all(color: Colors.white10, width: 3), + boxShadow: const [BoxShadow(color: Colors.black54, blurRadius: 40, spreadRadius: -10)] + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(52), + child: _isLoading + ? const Center(child: CircularProgressIndicator(color: AppColors.coral)) + : GoogleMap( + initialCameraPosition: CameraPosition(target: _currentPosition, zoom: 17.5), + onMapCreated: (controller) => _mapController = controller, + markers: _markers, + polylines: _polylines, + zoomControlsEnabled: false, + myLocationButtonEnabled: false, + compassEnabled: false, + mapToolbarEnabled: false, + style: null, // You can add custom styling here + ) + ) ), ), + // Stats Overlay Positioned( - top: 115, left: 20, right: 20, + top: 115, left: 25, right: 25, child: Container( - padding: const EdgeInsets.symmetric(vertical: 20), - decoration: BoxDecoration(color: AppColors.background.withOpacity(0.95), borderRadius: BorderRadius.circular(25), border: Border.all(color: Colors.white10)), + padding: const EdgeInsets.symmetric(vertical: 22, horizontal: 10), + decoration: BoxDecoration( + color: AppColors.background.withOpacity(0.9), + borderRadius: BorderRadius.circular(30), + border: Border.all(color: Colors.white10), + boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.3), blurRadius: 15)] + ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - _buildStat(AppStrings.mapPace, (_currentSpeed * 3.6).toStringAsFixed(1), AppStrings.kmhUnit), + _buildStat(AppStrings.mapPace, _currentSpeed.toStringAsFixed(1), AppStrings.kmhUnit), _buildDivider(), _buildStat(AppStrings.mapRoute, _formatDistanceValue(_totalDistance), _totalDistance < 1000 ? AppStrings.metersUnit : AppStrings.kmUnit), _buildDivider(), @@ -454,51 +460,49 @@ class _GoogleMapScreenState extends State { ), ), + // Countdown Overlay if (_isCountingDown) Container( - color: Colors.black54, + color: Colors.black87, + width: double.infinity, + height: double.infinity, child: Center( - child: Text("$_countdownValue", style: const TextStyle(color: Colors.white, fontSize: 150, fontWeight: FontWeight.w900)), - ), - ), - - if (_isPlanningMode) - Positioned( - bottom: 120, left: 60, right: 60, - child: Container( - padding: const EdgeInsets.all(15), - decoration: BoxDecoration(color: AppColors.coral.withOpacity(0.9), borderRadius: BorderRadius.circular(20)), - child: Text(AppStrings.markDestination, textAlign: TextAlign.center, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text("PREPARAR", style: TextStyle(color: Colors.white54, fontSize: 24, fontWeight: FontWeight.bold, letterSpacing: 5)), + const SizedBox(height: 20), + Text("$_countdownValue", style: const TextStyle(color: AppColors.coral, fontSize: 160, fontWeight: FontWeight.w900)), + ], + ), ), ), ], ), - floatingActionButton: FloatingActionButton.extended( - onPressed: _isRunning ? _finishRun : _showStartOptions, - label: Text(_isRunning ? AppStrings.btnStop : AppStrings.btnStartRun, style: const TextStyle(fontWeight: FontWeight.w900, letterSpacing: 1.5)), - icon: Icon(_isRunning ? Icons.stop_rounded : Icons.play_arrow_rounded, size: 32), - backgroundColor: _isRunning ? AppColors.coral : Colors.white, - foregroundColor: _isRunning ? Colors.white : AppColors.background, - elevation: 15, + floatingActionButton: Padding( + padding: const EdgeInsets.only(bottom: 20.0), + child: FloatingActionButton.extended( + onPressed: _isRunning ? _finishRun : _showStartConfirmation, + label: Text( + _isRunning ? AppStrings.btnStop : AppStrings.btnStartRun, + style: const TextStyle(fontWeight: FontWeight.w900, letterSpacing: 1.5, fontSize: 16) + ), + icon: Icon(_isRunning ? Icons.stop_rounded : Icons.play_arrow_rounded, size: 28), + backgroundColor: _isRunning ? AppColors.coral : Colors.white, + foregroundColor: _isRunning ? Colors.white : AppColors.background, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + elevation: 10, + ), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, ); } - Widget _buildOptionTile(IconData icon, String title, VoidCallback onTap) { - return ListTile( - leading: Container(padding: const EdgeInsets.all(8), decoration: BoxDecoration(color: Colors.white.withOpacity(0.05), shape: BoxShape.circle), child: Icon(icon, color: AppColors.coral)), - title: Text(title, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), - trailing: const Icon(Icons.arrow_forward_ios_rounded, color: Colors.white24, size: 16), - onTap: onTap, - ); - } - Widget _buildResultRow(String label, String value) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(label, style: const TextStyle(color: Colors.white54, fontWeight: FontWeight.bold)), + Text(label, style: const TextStyle(color: Colors.white54, fontWeight: FontWeight.bold, fontSize: 13)), Text(value, style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.w900)), ], ); @@ -515,20 +519,23 @@ class _GoogleMapScreenState extends State { return "${mins.toString().padLeft(2, '0')}:${secs.toString().padLeft(2, '0')}"; } - Widget _buildDivider() => Container(width: 1, height: 30, color: Colors.white10); + Widget _buildDivider() => Container(width: 1, height: 35, color: Colors.white10); Widget _buildStat(String label, String value, String unit) { return Column( mainAxisSize: MainAxisSize.min, children: [ - Text(label, style: const TextStyle(color: Colors.white54, fontSize: 9, fontWeight: FontWeight.w900, letterSpacing: 1)), - const SizedBox(height: 4), + Text(label, style: const TextStyle(color: Colors.white54, fontSize: 10, fontWeight: FontWeight.w900, letterSpacing: 1.5)), + const SizedBox(height: 6), Row( crossAxisAlignment: CrossAxisAlignment.baseline, textBaseline: TextBaseline.alphabetic, children: [ - Text(value, style: const TextStyle(color: Colors.white, fontSize: 22, fontWeight: FontWeight.w900)), - if (unit.isNotEmpty) ...[const SizedBox(width: 2), Text(unit, style: const TextStyle(color: Colors.white54, fontSize: 9, fontWeight: FontWeight.bold))], + Text(value, style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.w900)), + if (unit.isNotEmpty) ...[ + const SizedBox(width: 3), + Text(unit, style: const TextStyle(color: Colors.white38, fontSize: 10, fontWeight: FontWeight.bold)) + ], ], ), ], diff --git a/lib/screens/logado_screen.dart b/lib/screens/logado_screen.dart index 5bf1d0b..3483db4 100644 --- a/lib/screens/logado_screen.dart +++ b/lib/screens/logado_screen.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import '../constants/app_colors.dart'; import '../constants/app_strings.dart'; +import '../services/supabase_service.dart'; import 'setting_screen.dart'; -import 'bluethoot_cpnnection_screen.dart'; +import 'bluetooth_connection_screen.dart'; import 'google_map_screen.dart'; class LogadoScreen extends StatefulWidget { @@ -13,419 +15,492 @@ class LogadoScreen extends StatefulWidget { } class _LogadoScreenState extends State { - double progress = 0.65; // Simulação de progresso + // Estado dinâmico do utilizador + double _dailyGoal = 0.0; // 0.0 significa que ainda não foi definida + double _currentDistance = 0.0; + double _bestDistance = 12.4; // Exemplo de recorde + double _bestSpeed = 16.8; // Exemplo de recorde + int _steps = 0; + int _totalTimeMinutes = 0; + + double get _progress => _dailyGoal > 0 ? (_currentDistance / _dailyGoal).clamp(0.0, 1.0) : 0.0; + + @override + void initState() { + super.initState(); + _loadUserData(); + } + + Future _loadUserData() async { + // No futuro, aqui buscaríamos os dados reais do Supabase ou Local Storage + setState(() { + // Simulação de dados carregados + _currentDistance = 0.0; // Começa o dia a zero + _steps = 0; + _totalTimeMinutes = 0; + }); + } + + void _showGoalDialog() { + showDialog( + context: context, + builder: (context) => AlertDialog( + backgroundColor: AppColors.backgroundGrey, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)), + title: const Text("Definir Meta Diária", style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ...[5, 10, 15, 20].map((km) => ListTile( + title: Text("$km KM", style: const TextStyle(color: Colors.white)), + onTap: () { + setState(() { + _dailyGoal = km.toDouble(); + }); + Navigator.pop(context); + }, + )), + const Divider(color: Colors.white10), + ListTile( + leading: const Icon(Icons.edit_note_rounded, color: AppColors.coral), + title: const Text("Personalizado", style: TextStyle(color: AppColors.coral, fontWeight: FontWeight.bold)), + onTap: () { + Navigator.pop(context); + _showCustomGoalDialog(); + }, + ), + ], + ), + ), + ); + } + + void _showCustomGoalDialog() { + final TextEditingController controller = TextEditingController(); + showDialog( + context: context, + builder: (context) => AlertDialog( + backgroundColor: AppColors.backgroundGrey, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)), + title: const Text("Meta Personalizada", style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), + content: TextField( + controller: controller, + keyboardType: const TextInputType.numberWithOptions(decimal: true), + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d*'))], + style: const TextStyle(color: Colors.white), + decoration: InputDecoration( + hintText: "Ex: 12.5", + hintStyle: const TextStyle(color: Colors.white24), + suffixText: "KM", + suffixStyle: const TextStyle(color: Colors.white54), + enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: Colors.white24)), + focusedBorder: UnderlineInputBorder(borderSide: BorderSide(color: AppColors.coral)), + ), + autofocus: true, + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text("CANCELAR", style: TextStyle(color: Colors.white54)), + ), + ElevatedButton( + onPressed: () { + final value = double.tryParse(controller.text); + if (value != null && value > 0) { + setState(() { + _dailyGoal = value; + }); + Navigator.pop(context); + } + }, + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.coral, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), + ), + child: const Text("DEFINIR", style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), + ), + ], + ), + ); + } @override Widget build(BuildContext context) { + final user = SupabaseService.currentUser; + final userName = user?.userMetadata?['name'] ?? user?.email?.split('@')[0] ?? 'Corredor'; + return Scaffold( backgroundColor: AppColors.background, body: Stack( children: [ - // Background triangles - Positioned( - top: -50, - left: -80, - child: CustomPaint( - size: const Size(160, 120), - painter: TrianglePainter( - color: Colors.grey.shade800.withValues(alpha: 0.4), + // Background Gradient + Positioned.fill( + child: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Color(0xFF2D2D31), AppColors.background], + ), ), ), ), - Positioned( - top: 20, - right: -60, - child: CustomPaint( - size: const Size(120, 90), - painter: TrianglePainter( - color: Colors.grey.shade800.withValues(alpha: 0.3), - ), - ), - ), - Positioned( - top: 80, - left: 40, - child: CustomPaint( - size: const Size(140, 105), - painter: TrianglePainter( - color: Colors.grey.shade800.withValues(alpha: 0.35), - ), - ), - ), - Positioned( - top: 120, - right: 80, - child: CustomPaint( - size: const Size(100, 75), - painter: TrianglePainter( - color: Colors.grey.shade800.withValues(alpha: 0.2), - ), - ), - ), - Positioned( - top: 160, - left: -40, - child: CustomPaint( - size: const Size(130, 98), - painter: TrianglePainter( - color: Colors.grey.shade800.withValues(alpha: 0.3), - ), - ), - ), - - // Main content + SafeArea( - child: Padding( - padding: const EdgeInsets.all(24.0), - child: Column( - children: [ - const SizedBox(height: 60), - - // Header with user info - Row( + child: Column( + children: [ + // Header Bar + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - 'Bem-vindo!', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.white, + Text( + AppStrings.welcome.toUpperCase(), + style: const TextStyle( + color: Colors.white38, + fontSize: 10, + fontWeight: FontWeight.w900, + letterSpacing: 2, ), ), Text( - 'Usuário Logado', - style: TextStyle( - fontSize: 16, - color: Colors.white.withValues(alpha: 0.7), + userName, + style: const TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.w900, + letterSpacing: -0.5, ), ), ], ), - Container( - width: 50, - height: 50, - decoration: BoxDecoration( - color: AppColors.buttonColor, - borderRadius: BorderRadius.circular(25), - ), - child: const Icon( - Icons.person, - color: Colors.white, - size: 30, - ), + Row( + children: [ + _buildIconButton( + Icons.bluetooth_audio_rounded, + () => Navigator.push(context, MaterialPageRoute(builder: (context) => const BluetoothConnectionScreen())), + ), + const SizedBox(width: 12), + _buildIconButton( + Icons.settings_rounded, + () => Navigator.push(context, MaterialPageRoute(builder: (context) => const SettingsScreen())), + ), + ], ), ], ), + ), - const SizedBox(height: 40), - - // Stats cards - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - _buildStatCard( - AppStrings.complete, - '85%', - AppColors.success, - ), - _buildStatCard( - AppStrings.steps, - '8,432', - AppColors.buttonColor, - ), - _buildStatCard(AppStrings.bpm, '72', AppColors.error), - _buildStatCard(AppStrings.kcal, '420', AppColors.coral), - ], - ), - - const SizedBox(height: 30), - - // Progress section - Container( - padding: const EdgeInsets.all(20), - decoration: BoxDecoration( - color: AppColors.backgroundGrey, - borderRadius: BorderRadius.circular(15), - ), + Expanded( + child: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric(horizontal: 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + const SizedBox(height: 20), + + // Main Tracking Card (Meta Diária) + _buildMainTrackingCard(), + + const SizedBox(height: 30), + + // Personal Bests Section + const Text( + "RECORDS PESSOAIS", + style: TextStyle( + color: Colors.white38, + fontSize: 11, + fontWeight: FontWeight.w900, + letterSpacing: 1.5, + ), + ), + const SizedBox(height: 15), Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text( - 'Meta Diária', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.white, + Expanded( + child: _buildRecordCard( + AppStrings.bestDistance, + _bestDistance.toStringAsFixed(1), + AppStrings.kmUnit, + Icons.auto_graph_rounded, + AppColors.coral, ), ), - Text( - '${(progress * 100).toInt()}%', - style: const TextStyle( - fontSize: 16, - color: Colors.white, + const SizedBox(width: 15), + Expanded( + child: _buildRecordCard( + AppStrings.bestSpeed, + _bestSpeed.toStringAsFixed(1), + AppStrings.kmhUnit, + Icons.speed_rounded, + Colors.cyanAccent, ), ), ], ), - const SizedBox(height: 15), - ClipRRect( - borderRadius: BorderRadius.circular(10), - child: LinearProgressIndicator( - value: progress, - minHeight: 8, - backgroundColor: AppColors.white.withValues( - alpha: 0.1, - ), - valueColor: const AlwaysStoppedAnimation( - AppColors.white, - ), + + const SizedBox(height: 25), + + // Steps Section (Atividade Geral) + const Text( + "ATIVIDADE GERAL", + style: TextStyle( + color: Colors.white38, + fontSize: 11, + fontWeight: FontWeight.w900, + letterSpacing: 1.5, ), ), + const SizedBox(height: 15), + _buildWideRecordCard( + AppStrings.steps, + _steps.toString(), + "passos hoje", + Icons.directions_walk_rounded, + AppColors.success, + ), + + const SizedBox(height: 120), // Espaço para o botão inferior ], ), ), - - const SizedBox(height: 30), - - // Map preview - GestureDetector( - onTap: () { - // Navigate to GoogleMapScreen - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const GoogleMapScreen(), - ), - ); - }, - child: Container( - height: 200, - decoration: BoxDecoration( - color: AppColors.backgroundGrey, - borderRadius: BorderRadius.circular(15), - ), - child: const Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.map_outlined, - size: 50, - color: Colors.white54, - ), - SizedBox(height: 10), - Text( - AppStrings.mapPreview, - style: TextStyle( - color: Colors.white54, - fontSize: 16, - ), - ), - ], - ), - ), - ), - ), + ), + ], + ), + ), + + // Bottom Action Button + Positioned( + bottom: 30, + left: 50, + right: 50, + child: Container( + height: 70, + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: AppColors.coral.withValues(alpha: 0.3), + blurRadius: 25, + spreadRadius: -5, + ) ], ), + child: ElevatedButton( + onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => const GoogleMapScreen())), + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.coral, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)), + elevation: 0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.play_arrow_rounded, size: 30), + const SizedBox(width: 10), + Text( + AppStrings.startTraining, + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w900, letterSpacing: 1.5), + ), + ], + ), + ), ), ), - - // Bottom navigation menu - Positioned( - bottom: 50, - left: 0, - right: 0, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - _buildMenuButton(Icons.settings_outlined, AppStrings.settings), - _buildMenuButton(Icons.group_outlined, AppStrings.groups), - _buildMenuButton(Icons.history_rounded, AppStrings.history), - _buildMenuButton( - Icons.notifications_none_rounded, - AppStrings.notifications, - showBadge: true, - ), - _buildMenuButton( - Icons.person_outline_rounded, - AppStrings.profile, - isAvatar: true, - ), - ], - ), - ), - - // Bluetooth action button - Positioned( - top: 60, - right: 25, - child: _buildSmallActionButton(Icons.bluetooth, AppColors.error), - ), ], ), ); } - Widget _buildStatCard(String title, String value, Color color) { + Widget _buildIconButton(IconData icon, VoidCallback onTap) { + return GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.05), + borderRadius: BorderRadius.circular(15), + border: Border.all(color: Colors.white10), + ), + child: Icon(icon, color: Colors.white, size: 22), + ), + ); + } + + Widget _buildMainTrackingCard() { return Container( - padding: const EdgeInsets.all(15), + width: double.infinity, + padding: const EdgeInsets.all(25), decoration: BoxDecoration( color: AppColors.backgroundGrey, - borderRadius: BorderRadius.circular(12), + borderRadius: BorderRadius.circular(45), + border: Border.all(color: Colors.white10, width: 2), + boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 20, offset: Offset(0, 10))], ), child: Column( children: [ - Text( - value, - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: color, - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + AppStrings.dailyGoal, + style: TextStyle(color: Colors.white54, fontWeight: FontWeight.bold, letterSpacing: 1), + ), + if (_dailyGoal > 0) + Text( + "${(_progress * 100).toInt()}%", + style: const TextStyle(color: AppColors.coral, fontWeight: FontWeight.w900), + ), + ], ), - const SizedBox(height: 5), - Text( - title, - style: const TextStyle(fontSize: 12, color: Colors.white70), + const SizedBox(height: 20), + Stack( + alignment: Alignment.center, + children: [ + SizedBox( + width: 180, + height: 180, + child: CircularProgressIndicator( + value: _progress, + strokeWidth: 15, + backgroundColor: Colors.white.withValues(alpha: 0.05), + valueColor: const AlwaysStoppedAnimation(AppColors.coral), + strokeCap: StrokeCap.round, + ), + ), + if (_dailyGoal == 0) + GestureDetector( + onTap: _showGoalDialog, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.all(15), + decoration: BoxDecoration( + color: AppColors.coral.withValues(alpha: 0.1), + shape: BoxShape.circle, + ), + child: const Icon(Icons.add_task_rounded, color: AppColors.coral, size: 40), + ), + const SizedBox(height: 10), + const Text( + "DEFINIR META", + style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w900, letterSpacing: 1), + ), + ], + ), + ) + else + Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text("DISTÂNCIA", style: TextStyle(color: Colors.white38, fontSize: 10, fontWeight: FontWeight.bold, letterSpacing: 2)), + Text( + _currentDistance.toStringAsFixed(1), + style: const TextStyle(color: Colors.white, fontSize: 48, fontWeight: FontWeight.w900), + ), + Text( + "/ ${_dailyGoal.toStringAsFixed(1)} ${AppStrings.kmUnit}", + style: const TextStyle(color: Colors.white54, fontSize: 14, fontWeight: FontWeight.bold), + ), + ], + ), + ], ), + const SizedBox(height: 30), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildSimpleStat("PASSOS", "${(_steps / 1000).toStringAsFixed(1)}k"), + const SizedBox(width: 40), + _buildSimpleStat("TEMPO", "${_totalTimeMinutes}m"), + ], + ) ], ), ); } - Widget _buildMenuButton( - IconData icon, - String label, { - bool showBadge = false, - bool isAvatar = false, - }) { - return GestureDetector( - onTap: () { - _handleMenuTap(label); - }, + Widget _buildSimpleStat(String label, String value) { + return Column( + children: [ + Text(value, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w900, fontSize: 16)), + Text(label, style: const TextStyle(color: Colors.white38, fontSize: 9, fontWeight: FontWeight.bold)), + ], + ); + } + + Widget _buildRecordCard(String title, String value, String unit, IconData icon, Color accentColor) { + return Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: AppColors.backgroundGrey, + borderRadius: BorderRadius.circular(30), + border: Border.all(color: Colors.white10), + ), child: Column( - mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - width: 50, - height: 50, - decoration: BoxDecoration( - color: AppColors.backgroundGrey, - borderRadius: BorderRadius.circular(25), - ), - child: Stack( - children: [ - Center(child: Icon(icon, color: Colors.white, size: 24)), - if (showBadge) - Positioned( - top: 8, - right: 8, - child: Container( - width: 8, - height: 8, - decoration: BoxDecoration( - color: AppColors.error, - borderRadius: BorderRadius.circular(4), - ), - ), - ), - ], - ), + padding: const EdgeInsets.all(8), + decoration: BoxDecoration(color: accentColor.withValues(alpha: 0.1), shape: BoxShape.circle), + child: Icon(icon, color: accentColor, size: 18), ), - const SizedBox(height: 8), - Text( - label, - style: const TextStyle(fontSize: 12, color: Colors.white70), + const SizedBox(height: 15), + Row( + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.alphabetic, + children: [ + Text(value, style: const TextStyle(color: Colors.white, fontSize: 22, fontWeight: FontWeight.w900)), + const SizedBox(width: 4), + Text(unit, style: const TextStyle(color: Colors.white38, fontSize: 10, fontWeight: FontWeight.bold)), + ], ), + Text(title, style: const TextStyle(color: Colors.white54, fontSize: 9, fontWeight: FontWeight.bold, letterSpacing: 0.5)), ], ), ); } - Widget _buildSmallActionButton(IconData icon, Color color) { - return GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const BluetoothConnectionScreen(), + Widget _buildWideRecordCard(String title, String value, String unit, IconData icon, Color accentColor) { + return Container( + width: double.infinity, + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: AppColors.backgroundGrey, + borderRadius: BorderRadius.circular(30), + border: Border.all(color: Colors.white10), + ), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration(color: accentColor.withValues(alpha: 0.1), shape: BoxShape.circle), + child: Icon(icon, color: accentColor, size: 24), ), - ); - }, - child: Container( - width: 45, - height: 45, - decoration: BoxDecoration( - color: color, - borderRadius: BorderRadius.circular(22.5), - boxShadow: [ - BoxShadow( - color: color.withValues(alpha: 0.3), - blurRadius: 10, - spreadRadius: 2, - ), - ], - ), - child: Icon(icon, color: Colors.white, size: 20), + const SizedBox(width: 20), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.alphabetic, + children: [ + Text(value, style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.w900)), + const SizedBox(width: 6), + Text(unit, style: const TextStyle(color: Colors.white38, fontSize: 12, fontWeight: FontWeight.bold)), + ], + ), + Text(title, style: const TextStyle(color: Colors.white54, fontSize: 11, fontWeight: FontWeight.bold, letterSpacing: 0.5)), + ], + ), + ], ), ); } - - void _handleMenuTap(String label) { - switch (label) { - case 'Configurações': - Navigator.push( - context, - MaterialPageRoute(builder: (context) => const SettingsScreen()), - ); - break; - case 'Perfil': - // Navigate to profile - break; - case 'Histórico': - // Navigate to history - break; - case 'Grupos': - // Navigate to groups - break; - case 'Notificações': - // Navigate to notifications - break; - } - } -} - -// Custom painter for triangles -class TrianglePainter extends CustomPainter { - final Color color; - - TrianglePainter({required this.color}); - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = color - ..style = PaintingStyle.fill; - - final path = Path(); - path.moveTo(size.width / 2, 0); - path.lineTo(0, size.height); - path.lineTo(size.width, size.height); - path.close(); - - canvas.drawPath(path, paint); - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) { - return false; - } }