From 256d34eb798b8aab206dc423c7f63b65ce3c7187 Mon Sep 17 00:00:00 2001 From: 240405 <240405@epvc.pt> Date: Wed, 11 Mar 2026 15:36:21 +0000 Subject: [PATCH] =?UTF-8?q?Simplifica=C3=A7=C3=A3o=20do=20main.dart?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 503 +------------------------------- lib/screens/running_screen.dart | 497 +++++++++++++++++++++++++++++++ lib/screens/welcome_screen.dart | 100 +++++++ 3 files changed, 599 insertions(+), 501 deletions(-) create mode 100644 lib/screens/running_screen.dart create mode 100644 lib/screens/welcome_screen.dart diff --git a/lib/main.dart b/lib/main.dart index 2dd0f19..7dc95ac 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,517 +1,18 @@ import 'package:flutter/material.dart'; -import 'constants/app_colors.dart'; -import 'constants/app_strings.dart'; -import 'screens/google_map_screen.dart'; -import 'screens/bluetooth_connection_screen.dart'; +import 'screens/welcome_screen.dart'; void main() { - // 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(), + home: WelcomeScreen(), ); } } - -/// 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.35; // Progresso inicial simulado para estética. - double targetDistance = 8.0; // Distância alvo em KM. - double currentDistance = 2.8; // Distância atual percorrida simulada. - - /// Constrói o indicador de progresso circular central com melhorias estéticas. - Widget _buildCircularProgressIndicator() { - return SizedBox( - width: 210, - height: 210, - child: Stack( - alignment: Alignment.center, - children: [ - // Efeito de brilho (glow) sutil atrás do progresso. - Container( - width: 180, - height: 180, - decoration: BoxDecoration( - shape: BoxShape.circle, - boxShadow: [ - BoxShadow( - color: AppColors.white.withValues(alpha: 0.05), - blurRadius: 30, - spreadRadius: 10, - ), - ], - ), - ), - // TweenAnimationBuilder cria uma animação suave quando o valor do progresso muda. - TweenAnimationBuilder( - tween: Tween(begin: 0.0, end: progress), - duration: const Duration(seconds: 1), - curve: Curves.easeInOut, - builder: (context, value, _) { - return CustomPaint( - size: const Size(210, 210), - painter: CircularProgressPainter( - progress: value, - strokeWidth: 14, - progressColor: AppColors.white, - backgroundColor: AppColors.white.withValues(alpha: 0.15), - ), - ); - }, - ), - // Círculo interno que contém o texto da porcentagem. - Container( - width: 175, - height: 175, - decoration: const BoxDecoration( - color: AppColors.backgroundGrey, - shape: BoxShape.circle, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - "${(progress * 100).toInt()}%", - style: const TextStyle( - fontSize: 42, - fontWeight: FontWeight.w900, - color: AppColors.white, - letterSpacing: -1, - ), - ), - Text( - AppStrings.complete, - style: TextStyle( - color: AppColors.white.withValues(alpha: 0.5), - fontSize: 11, - fontWeight: FontWeight.bold, - letterSpacing: 1.5, - ), - ), - ], - ), - ), - ], - ), - ); - } - - @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: 70), - child: _buildCircularProgressIndicator(), - ), - ), - - // 2. Exibição da distância estilizada como um badge. - Positioned( - top: 300, - left: 0, - right: 0, - child: Center( - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 22, vertical: 10), - decoration: BoxDecoration( - color: AppColors.white.withValues(alpha: 0.08), - borderRadius: BorderRadius.circular(25), - border: Border.all(color: AppColors.white.withValues(alpha: 0.1)), - ), - child: Text( - "${currentDistance.toStringAsFixed(1)} ${AppStrings.kmUnit} | ${targetDistance.toStringAsFixed(1)} ${AppStrings.kmUnit}", - style: const TextStyle( - color: AppColors.white, - fontSize: 15, - fontWeight: FontWeight.w800, - letterSpacing: 0.5, - ), - ), - ), - ), - ), - - // 3. Contêiner de estatísticas e o mapa interativo. - Positioned( - top: 360, - left: 20, - right: 20, - child: Container( - height: 210, - decoration: BoxDecoration( - color: AppColors.backgroundGrey, - borderRadius: BorderRadius.circular(30), - boxShadow: [ - BoxShadow( - color: AppColors.black.withValues(alpha: 0.3), - blurRadius: 20, - offset: const Offset(0, 10), - ), - ], - ), - child: Row( - children: [ - // Lado Esquerdo: Estatísticas. - Expanded( - flex: 4, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - _buildStatItem(Icons.directions_run_rounded, "3219", AppStrings.steps), - Divider(color: AppColors.white.withValues(alpha: 0.1), height: 1), - _buildStatItem(Icons.favorite_rounded, "98", AppStrings.bpm), - Divider(color: AppColors.white.withValues(alpha: 0.1), height: 1), - _buildStatItem(Icons.local_fire_department_rounded, "480", AppStrings.kcal), - ], - ), - ), - ), - // Lado Direito: Miniatura do Mapa Clicável. - Expanded( - flex: 6, - child: GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute(builder: (context) => const GoogleMapScreen()), - ); - }, - child: Container( - margin: const EdgeInsets.all(8), - child: ClipRRect( - borderRadius: BorderRadius.circular(22), - child: Stack( - children: [ - Container(color: const Color(0xFF2C2C2E)), - CustomPaint( - size: Size.infinite, - painter: MapPainter(), - ), - // Overlay estético indicando interatividade. - Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - AppColors.transparent, - AppColors.black.withValues(alpha: 0.4), - ], - ), - ), - ), - Positioned( - bottom: 12, - right: 12, - child: Container( - padding: const EdgeInsets.all(6), - decoration: BoxDecoration( - color: AppColors.background.withValues(alpha: 0.8), - shape: BoxShape.circle, - ), - child: const Icon(Icons.fullscreen_rounded, color: AppColors.white, size: 20), - ), - ), - const Positioned( - top: 12, - left: 12, - child: Text( - AppStrings.mapPreview, - style: TextStyle( - color: AppColors.white, - fontSize: 10, - fontWeight: FontWeight.w900, - letterSpacing: 1, - ), - ), - ), - ], - ), - ), - ), - ), - ), - ], - ), - ), - ), - - // 4. Barra de progresso linear centralizada. - Positioned( - bottom: 170, - left: 50, - right: 50, - child: ClipRRect( - borderRadius: BorderRadius.circular(10), - child: LinearProgressIndicator( - value: progress, - minHeight: 8, - backgroundColor: AppColors.white.withValues(alpha: 0.1), - valueColor: const AlwaysStoppedAnimation(AppColors.white), - ), - ), - ), - - // 5. Menu de navegação inferior. - 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), - ], - ), - ), - - // 6. Ação rápida de Bluetooth. - Positioned( - top: 60, - right: 25, - child: _buildSmallActionButton(Icons.bluetooth, AppColors.error), - ), - ], - ), - ); - } - - /// Item de estatística with design refinado. - Widget _buildStatItem(IconData icon, String value, String label) { - return Row( - children: [ - Icon(icon, color: AppColors.coral.withValues(alpha: 0.8), size: 22), - const SizedBox(width: 12), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - value, - style: const TextStyle( - color: AppColors.white, - fontSize: 19, - fontWeight: FontWeight.w900, - ), - ), - Text( - label, - style: TextStyle( - color: AppColors.white.withValues(alpha: 0.4), - fontSize: 9, - fontWeight: FontWeight.bold, - letterSpacing: 0.5, - ), - ), - ], - ), - ], - ); - } - - /// Botões do menu com melhorias visuais. - Widget _buildMenuButton(IconData icon, String message, {bool showBadge = false, bool isAvatar = false}) { - return GestureDetector( - onTap: () { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(message), - behavior: SnackBarBehavior.floating, - duration: const Duration(seconds: 1), - ), - ); - }, - child: Stack( - clipBehavior: Clip.none, - children: [ - isAvatar - ? Container( - padding: const EdgeInsets.all(3), - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all(color: AppColors.coral, width: 2), - ), - child: const CircleAvatar( - radius: 18, - backgroundImage: NetworkImage('https://i.pravatar.cc/150?u=1'), - ), - ) - : Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: AppColors.backgroundGrey, - borderRadius: BorderRadius.circular(18), - border: Border.all(color: AppColors.white.withValues(alpha: 0.05)), - ), - child: Icon(icon, color: AppColors.white.withValues(alpha: 0.9), size: 24), - ), - if (showBadge) - Positioned( - right: -2, - top: -2, - child: Container( - width: 10, - height: 10, - decoration: BoxDecoration( - color: AppColors.coral, - shape: BoxShape.circle, - border: Border.all(color: AppColors.background, width: 2), - ), - ), - ), - ], - ), - ); - } - - /// Botão de ação rápida (Bluetooth). - Widget _buildSmallActionButton(IconData icon, Color badgeColor) { - return GestureDetector( - onTap: () { - // Navegando para a nova tela de conexão Bluetooth - Navigator.push( - context, - MaterialPageRoute(builder: (context) => const BluetoothConnectionScreen()), - ); - }, - child: Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: AppColors.backgroundGrey, - shape: BoxShape.circle, - border: Border.all(color: AppColors.white.withValues(alpha: 0.05)), - ), - child: Stack( - children: [ - Icon(icon, color: AppColors.white, size: 20), - Positioned( - right: 0, - top: 0, - child: Container( - width: 7, - height: 7, - decoration: BoxDecoration( - color: badgeColor, - shape: BoxShape.circle, - border: Border.all(color: AppColors.backgroundGrey, width: 1.5), - ), - ), - ), - ], - ), - ), - ); - } -} - -/// Pintor customizado para o mapa miniatura estético. -class MapPainter extends CustomPainter { - @override - void paint(Canvas canvas, Size size) { - final paintPath = Paint() - ..color = AppColors.coral.withValues(alpha: 0.5) - ..strokeWidth = 3 - ..style = PaintingStyle.stroke - ..strokeCap = StrokeCap.round; - - final path = Path(); - path.moveTo(size.width * 0.1, size.height * 0.8); - path.quadraticBezierTo(size.width * 0.3, size.height * 0.9, size.width * 0.5, size.height * 0.5); - path.quadraticBezierTo(size.width * 0.7, size.height * 0.1, size.width * 0.9, size.height * 0.3); - - final paintRoad = Paint() - ..color = AppColors.white.withValues(alpha: 0.1) - ..strokeWidth = 10 - ..style = PaintingStyle.stroke; - - final road = Path(); - road.moveTo(0, size.height * 0.5); - road.lineTo(size.width, size.height * 0.6); - - canvas.drawPath(road, paintRoad); - canvas.drawPath(path, paintPath); - - final markerPaint = Paint()..color = AppColors.coral; - canvas.drawCircle(Offset(size.width * 0.5, size.height * 0.5), 5, markerPaint); - canvas.drawCircle(Offset(size.width * 0.5, size.height * 0.5), 8, Paint()..color = AppColors.coral.withValues(alpha: 0.3)); - } - - @override - bool shouldRepaint(CustomPainter oldDelegate) => false; -} - -/// Pintor customizado para 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; - - canvas.drawCircle(center, radius, Paint() - ..color = backgroundColor - ..strokeWidth = strokeWidth - ..style = PaintingStyle.stroke); - - final progressPaint = Paint() - ..color = progressColor - ..strokeWidth = strokeWidth - ..style = PaintingStyle.stroke - ..strokeCap = StrokeCap.round; - - canvas.drawArc( - Rect.fromCircle(center: center, radius: radius), - -1.5708, // -90 graus em radianos - 6.2831 * progress, // 360 graus em radianos * progresso - false, - progressPaint, - ); - } - - @override - bool shouldRepaint(covariant CircularProgressPainter oldDelegate) => - oldDelegate.progress != progress; -} diff --git a/lib/screens/running_screen.dart b/lib/screens/running_screen.dart new file mode 100644 index 0000000..bcf7799 --- /dev/null +++ b/lib/screens/running_screen.dart @@ -0,0 +1,497 @@ +import 'package:flutter/material.dart'; +import '../constants/app_colors.dart'; +import '../constants/app_strings.dart'; +import 'google_map_screen.dart'; +import 'bluetooth_connection_screen.dart'; + +/// 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.35; // Progresso inicial simulado para estética. + double targetDistance = 8.0; // Distância alvo em KM. + double currentDistance = 2.8; // Distância atual percorrida simulada. + + /// Constrói o indicador de progresso circular central com melhorias estéticas. + Widget _buildCircularProgressIndicator() { + return SizedBox( + width: 210, + height: 210, + child: Stack( + alignment: Alignment.center, + children: [ + // Efeito de brilho (glow) sutil atrás do progresso. + Container( + width: 180, + height: 180, + decoration: BoxDecoration( + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: AppColors.white.withValues(alpha: 0.05), + blurRadius: 30, + spreadRadius: 10, + ), + ], + ), + ), + // TweenAnimationBuilder cria uma animação suave quando o valor do progresso muda. + TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: progress), + duration: const Duration(seconds: 1), + curve: Curves.easeInOut, + builder: (context, value, _) { + return CustomPaint( + size: const Size(210, 210), + painter: CircularProgressPainter( + progress: value, + strokeWidth: 14, + progressColor: AppColors.white, + backgroundColor: AppColors.white.withValues(alpha: 0.15), + ), + ); + }, + ), + // Círculo interno que contém o texto da porcentagem. + Container( + width: 175, + height: 175, + decoration: const BoxDecoration( + color: AppColors.backgroundGrey, + shape: BoxShape.circle, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "${(progress * 100).toInt()}%", + style: const TextStyle( + fontSize: 42, + fontWeight: FontWeight.w900, + color: AppColors.white, + letterSpacing: -1, + ), + ), + Text( + AppStrings.complete, + style: TextStyle( + color: AppColors.white.withValues(alpha: 0.5), + fontSize: 11, + fontWeight: FontWeight.bold, + letterSpacing: 1.5, + ), + ), + ], + ), + ), + ], + ), + ); + } + + @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: 70), + child: _buildCircularProgressIndicator(), + ), + ), + + // 2. Exibição da distância estilizada como um badge. + Positioned( + top: 300, + left: 0, + right: 0, + child: Center( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 22, vertical: 10), + decoration: BoxDecoration( + color: AppColors.white.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(25), + border: Border.all(color: AppColors.white.withValues(alpha: 0.1)), + ), + child: Text( + "${currentDistance.toStringAsFixed(1)} ${AppStrings.kmUnit} | ${targetDistance.toStringAsFixed(1)} ${AppStrings.kmUnit}", + style: const TextStyle( + color: AppColors.white, + fontSize: 15, + fontWeight: FontWeight.w800, + letterSpacing: 0.5, + ), + ), + ), + ), + ), + + // 3. Contêiner de estatísticas e o mapa interativo. + Positioned( + top: 360, + left: 20, + right: 20, + child: Container( + height: 210, + decoration: BoxDecoration( + color: AppColors.backgroundGrey, + borderRadius: BorderRadius.circular(30), + boxShadow: [ + BoxShadow( + color: AppColors.black.withValues(alpha: 0.3), + blurRadius: 20, + offset: const Offset(0, 10), + ), + ], + ), + child: Row( + children: [ + // Lado Esquerdo: Estatísticas. + Expanded( + flex: 4, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _buildStatItem(Icons.directions_run_rounded, "3219", AppStrings.steps), + Divider(color: AppColors.white.withValues(alpha: 0.1), height: 1), + _buildStatItem(Icons.favorite_rounded, "98", AppStrings.bpm), + Divider(color: AppColors.white.withValues(alpha: 0.1), height: 1), + _buildStatItem(Icons.local_fire_department_rounded, "480", AppStrings.kcal), + ], + ), + ), + ), + // Lado Direito: Miniatura do Mapa Clicável. + Expanded( + flex: 6, + child: GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const GoogleMapScreen()), + ); + }, + child: Container( + margin: const EdgeInsets.all(8), + child: ClipRRect( + borderRadius: BorderRadius.circular(22), + child: Stack( + children: [ + Container(color: const Color(0xFF2C2C2E)), + CustomPaint( + size: Size.infinite, + painter: MapPainter(), + ), + // Overlay estético indicando interatividade. + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + AppColors.transparent, + AppColors.black.withValues(alpha: 0.4), + ], + ), + ), + ), + Positioned( + bottom: 12, + right: 12, + child: Container( + padding: const EdgeInsets.all(6), + decoration: BoxDecoration( + color: AppColors.background.withValues(alpha: 0.8), + shape: BoxShape.circle, + ), + child: const Icon(Icons.fullscreen_rounded, color: AppColors.white, size: 20), + ), + ), + const Positioned( + top: 12, + left: 12, + child: Text( + AppStrings.mapPreview, + style: TextStyle( + color: AppColors.white, + fontSize: 10, + fontWeight: FontWeight.w900, + letterSpacing: 1, + ), + ), + ), + ], + ), + ), + ), + ), + ), + ], + ), + ), + ), + + // 4. Barra de progresso linear centralizada. + Positioned( + bottom: 170, + left: 50, + right: 50, + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: LinearProgressIndicator( + value: progress, + minHeight: 8, + backgroundColor: AppColors.white.withValues(alpha: 0.1), + valueColor: const AlwaysStoppedAnimation(AppColors.white), + ), + ), + ), + + // 5. Menu de navegação inferior. + 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), + ], + ), + ), + + // 6. Ação rápida de Bluetooth. + Positioned( + top: 60, + right: 25, + child: _buildSmallActionButton(Icons.bluetooth, AppColors.error), + ), + ], + ), + ); + } + + /// Item de estatística with design refinado. + Widget _buildStatItem(IconData icon, String value, String label) { + return Row( + children: [ + Icon(icon, color: AppColors.coral.withValues(alpha: 0.8), size: 22), + const SizedBox(width: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + value, + style: const TextStyle( + color: AppColors.white, + fontSize: 19, + fontWeight: FontWeight.w900, + ), + ), + Text( + label, + style: TextStyle( + color: AppColors.white.withValues(alpha: 0.4), + fontSize: 9, + fontWeight: FontWeight.bold, + letterSpacing: 0.5, + ), + ), + ], + ), + ], + ); + } + + /// Botões do menu com melhorias visuais. + Widget _buildMenuButton(IconData icon, String message, {bool showBadge = false, bool isAvatar = false}) { + return GestureDetector( + onTap: () { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message), + behavior: SnackBarBehavior.floating, + duration: const Duration(seconds: 1), + ), + ); + }, + child: Stack( + clipBehavior: Clip.none, + children: [ + isAvatar + ? Container( + padding: const EdgeInsets.all(3), + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all(color: AppColors.coral, width: 2), + ), + child: const CircleAvatar( + radius: 18, + backgroundImage: NetworkImage('https://i.pravatar.cc/150?u=1'), + ), + ) + : Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: AppColors.backgroundGrey, + borderRadius: BorderRadius.circular(18), + border: Border.all(color: AppColors.white.withValues(alpha: 0.05)), + ), + child: Icon(icon, color: AppColors.white.withValues(alpha: 0.9), size: 24), + ), + if (showBadge) + Positioned( + right: -2, + top: -2, + child: Container( + width: 10, + height: 10, + decoration: BoxDecoration( + color: AppColors.coral, + shape: BoxShape.circle, + border: Border.all(color: AppColors.background, width: 2), + ), + ), + ), + ], + ), + ); + } + + /// Botão de ação rápida (Bluetooth). + Widget _buildSmallActionButton(IconData icon, Color badgeColor) { + return GestureDetector( + onTap: () { + // Navegando para a nova tela de conexão Bluetooth + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const BluetoothConnectionScreen()), + ); + }, + child: Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: AppColors.backgroundGrey, + shape: BoxShape.circle, + border: Border.all(color: AppColors.white.withValues(alpha: 0.05)), + ), + child: Stack( + children: [ + Icon(icon, color: AppColors.white, size: 20), + Positioned( + right: 0, + top: 0, + child: Container( + width: 7, + height: 7, + decoration: BoxDecoration( + color: badgeColor, + shape: BoxShape.circle, + border: Border.all(color: AppColors.backgroundGrey, width: 1.5), + ), + ), + ), + ], + ), + ), + ); + } +} + +/// Pintor customizado para o mapa miniatura estético. +class MapPainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + final paintPath = Paint() + ..color = AppColors.coral.withValues(alpha: 0.5) + ..strokeWidth = 3 + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round; + + final path = Path(); + path.moveTo(size.width * 0.1, size.height * 0.8); + path.quadraticBezierTo(size.width * 0.3, size.height * 0.9, size.width * 0.5, size.height * 0.5); + path.quadraticBezierTo(size.width * 0.7, size.height * 0.1, size.width * 0.9, size.height * 0.3); + + final paintRoad = Paint() + ..color = AppColors.white.withValues(alpha: 0.1) + ..strokeWidth = 10 + ..style = PaintingStyle.stroke; + + final road = Path(); + road.moveTo(0, size.height * 0.5); + road.lineTo(size.width, size.height * 0.6); + + canvas.drawPath(road, paintRoad); + canvas.drawPath(path, paintPath); + + final markerPaint = Paint()..color = AppColors.coral; + canvas.drawCircle(Offset(size.width * 0.5, size.height * 0.5), 5, markerPaint); + canvas.drawCircle(Offset(size.width * 0.5, size.height * 0.5), 8, Paint()..color = AppColors.coral.withValues(alpha: 0.3)); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => false; +} + +/// Pintor customizado para 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; + + canvas.drawCircle(center, radius, Paint() + ..color = backgroundColor + ..strokeWidth = strokeWidth + ..style = PaintingStyle.stroke); + + final progressPaint = Paint() + ..color = progressColor + ..strokeWidth = strokeWidth + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round; + + canvas.drawArc( + Rect.fromCircle(center: center, radius: radius), + -1.5708, // -90 graus em radianos + 6.2831 * progress, // 360 graus em radianos * progresso + false, + progressPaint, + ); + } + + @override + bool shouldRepaint(covariant CircularProgressPainter oldDelegate) => + oldDelegate.progress != progress; +} diff --git a/lib/screens/welcome_screen.dart b/lib/screens/welcome_screen.dart new file mode 100644 index 0000000..66c7e65 --- /dev/null +++ b/lib/screens/welcome_screen.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import '../constants/app_colors.dart'; +import 'running_screen.dart'; + +class WelcomeScreen extends StatelessWidget { + const WelcomeScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.background, + body: Container( + width: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + AppColors.coral.withValues(alpha: 0.1), + AppColors.background, + ], + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Spacer(flex: 2), + // Logo or Icon + Container( + padding: const EdgeInsets.all(30), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: AppColors.coral.withValues(alpha: 0.1), + border: Border.all(color: AppColors.coral, width: 2), + ), + child: const Icon( + Icons.directions_run_rounded, + size: 80, + color: AppColors.coral, + ), + ), + const SizedBox(height: 40), + const Text( + 'RUN VISION PRO', + style: TextStyle( + color: AppColors.white, + fontSize: 32, + fontWeight: FontWeight.w900, + letterSpacing: 4, + ), + ), + const SizedBox(height: 10), + Text( + 'Sua jornada começa aqui', + style: TextStyle( + color: AppColors.white.withValues(alpha: 0.6), + fontSize: 16, + fontWeight: FontWeight.w300, + ), + ), + const Spacer(), + // Start Button + Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + onPressed: () { + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => const RunningScreen()), + ); + }, + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.coral, + foregroundColor: AppColors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), + ), + elevation: 0, + ), + child: const Text( + 'COMEÇAR', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + letterSpacing: 2, + ), + ), + ), + ), + ), + const SizedBox(height: 60), + ], + ), + ), + ); + } +}