import 'package:flutter/material.dart'; import 'constants/app_colors.dart'; import 'screens/google_map_screen.dart'; import 'bluetooth_screen.dart'; void main() { // O ponto de entrada do aplicativo. runApp(const MyApp()); } /// Widget raiz do aplicativo que configura o MaterialApp. class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( // Remove a bandeira de "debug" no canto superior direito. debugShowCheckedModeBanner: false, // Define a tela inicial como RunningScreen. home: RunningScreen(), ); } } /// Tela principal que exibe o progresso, estatísticas e menu. class RunningScreen extends StatefulWidget { const RunningScreen({super.key}); @override State createState() => _RunningScreenState(); } class _RunningScreenState extends State with SingleTickerProviderStateMixin { // Variáveis de estado para controlar os dados da corrida. double progress = 0.0; // Progresso de 0.0 a 1.0 (ex: 0.5 = 50%). double targetDistance = 8.0; // Distância alvo em KM. double currentDistance = 0.0; // Distância atual percorrida. /// Constrói o indicador de progresso circular central. Widget _buildCircularProgressIndicator() { return SizedBox( width: 200, height: 200, child: Stack( alignment: Alignment.center, children: [ // TweenAnimationBuilder cria uma animação suave quando o valor do progresso muda. TweenAnimationBuilder( tween: Tween(begin: 0.0, end: progress), duration: const Duration(milliseconds: 500), builder: (context, value, _) { return CustomPaint( size: const Size(200, 200), painter: CircularProgressPainter( progress: value, strokeWidth: 12, progressColor: AppColors.white, backgroundColor: AppColors.white.withOpacity(0.3), ), ); }, ), // Círculo interno cinza que contém o texto da porcentagem. Container( width: 170, height: 170, decoration: const BoxDecoration( color: AppColors.backgroundGrey, shape: BoxShape.circle, ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( "${(progress * 100).toInt()}%", style: const TextStyle( fontSize: 36, fontWeight: FontWeight.bold, color: Colors.white, ), ), const Text( "COMPLETO", style: TextStyle(color: Colors.white70, fontSize: 12), ), ], ), ), ], ), ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.background, // Cor de fundo escura definida nas constantes. body: Stack( children: [ // 1. Indicador de progresso circular posicionado no topo central. Align( alignment: Alignment.topCenter, child: Padding( padding: const EdgeInsets.only(top: 60), child: _buildCircularProgressIndicator(), ), ), // 2. Exibição da distância (ex: 0.0 KM | 8.0 KM). Positioned( top: 280, left: 0, right: 0, child: Center( child: Container( padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 8, ), decoration: BoxDecoration( color: AppColors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(20), ), child: Text( "${currentDistance.toStringAsFixed(1)} KM | ${targetDistance.toStringAsFixed(1)} KM", style: const TextStyle( color: AppColors.white, fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ), ), // 3. Contêiner de estatísticas (Passos, BPM, K/CAL) e o mapa simulado. Positioned( top: 340, left: 20, right: 20, child: Container( height: 200, decoration: BoxDecoration( color: AppColors.backgroundGrey, borderRadius: BorderRadius.circular(24), ), child: Row( children: [ // Coluna esquerda com ícones e valores de estatística. Expanded( flex: 4, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildStatItem( Icons.directions_run, "3219", "PASSOS", ), const Divider(color: Colors.white24, height: 1), _buildStatItem(Icons.favorite_border, "98", "BPM"), const Divider(color: Colors.white24, height: 1), _buildStatItem( Icons.local_fire_department, "480", "K/CAL", ), ], ), ), ), // Coluna direita contendo o desenho do mapa simulado. Expanded( flex: 6, child: GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => const GoogleMapScreen(), ), ); }, child: ClipRRect( borderRadius: const BorderRadius.only( topRight: Radius.circular(24), bottomRight: Radius.circular(24), ), child: Stack( children: [ Container( color: const Color(0xFF3A3A3C), ), // Fundo do mapa. CustomPaint( size: Size.infinite, painter: MapPainter(), // Desenha as linhas e o marcador. ), ], ), ), ), ), ], ), ), ), // 4. Barra de progresso linear (centralizada acima dos botões inferiores). Positioned( bottom: 160, left: 40, // Espaçamento igual na esquerda e direita para centralizar. right: 40, child: ClipRRect( borderRadius: BorderRadius.circular(20), child: LinearProgressIndicator( value: progress, minHeight: 12, backgroundColor: AppColors.backgroundGrey.withOpacity(0.3), valueColor: const AlwaysStoppedAnimation( AppColors.white, ), ), ), ), // 5. Botões de menu inferiores. Positioned( bottom: 60, left: 0, right: 0, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildMenuButton(Icons.settings, 'Configurações clicado!'), _buildMenuButton(Icons.group_outlined, 'Grupos clicado!'), _buildMenuButton(Icons.access_time, 'Histórico clicado!'), _buildMenuButton( Icons.notifications_none, 'Notificações clicado!', showBadge: true, ), _buildMenuButton( Icons.person, 'Perfil clicado!', isAvatar: true, ), ], ), ), // 6. Botão de Bluetooth no canto superior direito. Positioned( top: 60, right: 30, child: GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => const BluetoothScreen(), ), ); }, child: Container( padding: const EdgeInsets.all(8), decoration: const BoxDecoration( color: AppColors.backgroundGrey, shape: BoxShape.circle, ), child: Stack( children: [ const Icon( Icons.bluetooth, color: AppColors.white, size: 20, ), // Pontinho vermelho indicando status ou notificação. Positioned( left: 0, bottom: 0, child: Container( width: 6, height: 6, decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), ), ), ], ), ), ), ), ], ), ); } /// Constrói uma linha de estatística com ícone, valor e rótulo. Widget _buildStatItem(IconData icon, String value, String label) { return Row( children: [ Icon(icon, color: Colors.white70, size: 24), const SizedBox(width: 12), Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( value, style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), Text( label, style: const TextStyle(color: Colors.white60, fontSize: 10), ), ], ), ], ); } /// Constrói um botão de menu clicável que exibe um SnackBar. Widget _buildMenuButton( IconData icon, String message, { bool showBadge = false, bool isAvatar = false, }) { return GestureDetector( onTap: () { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), duration: const Duration(seconds: 1), ), ); }, child: Stack( children: [ // Exibe um avatar circular ou um ícone padrão. isAvatar ? CircleAvatar( radius: 20, backgroundColor: Colors.orange, child: const CircleAvatar( radius: 18, backgroundImage: NetworkImage( 'https://i.pravatar.cc/150?u=1', ), ), ) : Container( padding: const EdgeInsets.all(10), decoration: const BoxDecoration( color: AppColors.backgroundGrey, shape: BoxShape.circle, ), child: Icon(icon, color: AppColors.white, size: 24), ), // Exibe um pontinho vermelho de notificação se showBadge for true. if (showBadge) Positioned( left: 0, bottom: 0, child: Container( width: 8, height: 8, decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), ), ), ], ), ); } } /// Pintor customizado para desenhar o traçado do mapa simulado. class MapPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.white38 ..strokeWidth = 3 ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round; // Desenha a linha sinuosa do percurso. final path = Path(); path.moveTo(size.width * 0.1, size.height * 0.8); path.quadraticBezierTo( size.width * 0.3, size.height * 0.7, size.width * 0.4, size.height * 0.4, ); path.quadraticBezierTo( size.width * 0.5, size.height * 0.1, size.width * 0.7, size.height * 0.3, ); path.lineTo(size.width * 0.9, size.height * 0.2); // Desenha uma "estrada" mais grossa branca. final roadPaint = Paint() ..color = Colors.white ..strokeWidth = 8 ..style = PaintingStyle.stroke; final roadPath = Path(); roadPath.moveTo(size.width * 0.6, size.height * 1.1); roadPath.quadraticBezierTo( size.width * 0.7, size.height * 0.8, size.width * 1.1, size.height * 0.7, ); canvas.drawPath(path, paint); canvas.drawPath(roadPath, roadPaint); // Desenha o marcador circular (o pino no mapa). final markerPaint = Paint()..color = const Color(0xFFFF6B6B); final markerPos = Offset(size.width * 0.4, size.height * 0.4); canvas.drawCircle(markerPos, 6, markerPaint); // Desenha o centro branco do marcador. final innerPaint = Paint()..color = Colors.white; canvas.drawCircle(markerPos, 2, innerPaint); } @override bool shouldRepaint(CustomPainter oldDelegate) => false; } /// Pintor customizado para desenhar o arco de progresso circular. class CircularProgressPainter extends CustomPainter { final double progress; final double strokeWidth; final Color progressColor; final Color backgroundColor; CircularProgressPainter({ required this.progress, required this.strokeWidth, required this.progressColor, required this.backgroundColor, }); @override void paint(Canvas canvas, Size size) { final center = Offset(size.width / 2, size.height / 2); final radius = (size.width - strokeWidth) / 2; // Desenha o círculo de fundo (cinza transparente). final backgroundPaint = Paint() ..color = backgroundColor ..strokeWidth = strokeWidth ..style = PaintingStyle.stroke; canvas.drawCircle(center, radius, backgroundPaint); // Desenha o arco de progresso (branco). final progressPaint = Paint() ..color = progressColor ..strokeWidth = strokeWidth ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round; const startAngle = -3.14159265359 / 2; // Começa no topo (-90 graus). final sweepAngle = 2 * 3.14159265359 * progress; // Define o tamanho do arco com base no progresso. canvas.drawArc( Rect.fromCircle(center: center, radius: radius), startAngle, sweepAngle, false, progressPaint, ); } @override bool shouldRepaint(covariant CircularProgressPainter oldDelegate) => oldDelegate.progress != progress; }