ecra de mapa adicionado

This commit is contained in:
2026-02-25 15:20:42 +00:00
parent 803bc197d4
commit bbd6185401
19 changed files with 564 additions and 60 deletions

View File

@@ -1,6 +1,6 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'constants/app_colors.dart';
import 'screens/google_map_screen.dart';
void main() {
// O ponto de entrada do aplicativo.
@@ -82,10 +82,7 @@ class _RunningScreenState extends State<RunningScreen>
),
const Text(
"COMPLETO",
style: TextStyle(
color: Colors.white70,
fontSize: 12,
),
style: TextStyle(color: Colors.white70, fontSize: 12),
),
],
),
@@ -98,7 +95,8 @@ class _RunningScreenState extends State<RunningScreen>
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.background, // Cor de fundo escura definida nas constantes.
backgroundColor:
AppColors.background, // Cor de fundo escura definida nas constantes.
body: Stack(
children: [
// 1. Indicador de progresso circular posicionado no topo central.
@@ -117,7 +115,10 @@ class _RunningScreenState extends State<RunningScreen>
right: 0,
child: Center(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 8,
),
decoration: BoxDecoration(
color: AppColors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
@@ -155,11 +156,19 @@ class _RunningScreenState extends State<RunningScreen>
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildStatItem(Icons.directions_run, "3219", "PASSOS"),
_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"),
_buildStatItem(
Icons.local_fire_department,
"480",
"K/CAL",
),
],
),
),
@@ -167,19 +176,32 @@ class _RunningScreenState extends State<RunningScreen>
// Coluna direita contendo o desenho do mapa simulado.
Expanded(
flex: 6,
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.
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.
),
],
),
),
),
),
@@ -191,7 +213,8 @@ class _RunningScreenState extends State<RunningScreen>
// 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.
left:
40, // Espaçamento igual na esquerda e direita para centralizar.
right: 40,
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
@@ -199,7 +222,9 @@ class _RunningScreenState extends State<RunningScreen>
value: progress,
minHeight: 12,
backgroundColor: AppColors.backgroundGrey.withOpacity(0.3),
valueColor: const AlwaysStoppedAnimation<Color>(AppColors.white),
valueColor: const AlwaysStoppedAnimation<Color>(
AppColors.white,
),
),
),
),
@@ -215,8 +240,16 @@ class _RunningScreenState extends State<RunningScreen>
_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),
_buildMenuButton(
Icons.notifications_none,
'Notificações clicado!',
showBadge: true,
),
_buildMenuButton(
Icons.person,
'Perfil clicado!',
isAvatar: true,
),
],
),
),
@@ -237,10 +270,17 @@ class _RunningScreenState extends State<RunningScreen>
},
child: Container(
padding: const EdgeInsets.all(8),
decoration: const BoxDecoration(color: AppColors.backgroundGrey, shape: BoxShape.circle),
decoration: const BoxDecoration(
color: AppColors.backgroundGrey,
shape: BoxShape.circle,
),
child: Stack(
children: [
const Icon(Icons.bluetooth, color: AppColors.white, size: 20),
const Icon(
Icons.bluetooth,
color: AppColors.white,
size: 20,
),
// Pontinho vermelho indicando status ou notificação.
Positioned(
left: 0,
@@ -248,7 +288,10 @@ class _RunningScreenState extends State<RunningScreen>
child: Container(
width: 6,
height: 6,
decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle),
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
),
),
],
@@ -271,8 +314,18 @@ class _RunningScreenState extends State<RunningScreen>
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)),
Text(
value,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Text(
label,
style: const TextStyle(color: Colors.white60, fontSize: 10),
),
],
),
],
@@ -280,7 +333,12 @@ class _RunningScreenState extends State<RunningScreen>
}
/// 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}) {
Widget _buildMenuButton(
IconData icon,
String message, {
bool showBadge = false,
bool isAvatar = false,
}) {
return GestureDetector(
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
@@ -294,14 +352,22 @@ class _RunningScreenState extends State<RunningScreen>
children: [
// Exibe um avatar circular ou um ícone padrão.
isAvatar
? const CircleAvatar(
? CircleAvatar(
radius: 20,
backgroundColor: Colors.orange,
child: CircleAvatar(radius: 18, backgroundImage: NetworkImage('https://i.pravatar.cc/150?u=1')),
child: 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),
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.
@@ -312,7 +378,10 @@ class _RunningScreenState extends State<RunningScreen>
child: Container(
width: 8,
height: 8,
decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle),
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
),
),
],
@@ -334,8 +403,18 @@ class MapPainter extends CustomPainter {
// 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.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.
@@ -346,7 +425,12 @@ class MapPainter extends CustomPainter {
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);
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);
@@ -400,7 +484,10 @@ class CircularProgressPainter extends CustomPainter {
..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.
final sweepAngle =
2 *
3.14159265359 *
progress; // Define o tamanho do arco com base no progresso.
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius),
@@ -412,5 +499,6 @@ class CircularProgressPainter extends CustomPainter {
}
@override
bool shouldRepaint(CircularProgressPainter oldDelegate) => oldDelegate.progress != progress;
bool shouldRepaint(covariant CircularProgressPainter oldDelegate) =>
oldDelegate.progress != progress;
}

View File

@@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class GoogleMapScreen extends StatefulWidget {
const GoogleMapScreen({super.key});
@override
State<GoogleMapScreen> createState() => _GoogleMapScreenState();
}
class _GoogleMapScreenState extends State<GoogleMapScreen> {
late GoogleMapController mapController;
final LatLng _center = const LatLng(38.7223, -9.1393); // Lisbon coordinates
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Mapa da Corrida'),
backgroundColor: Colors.black, // or your AppColors.background
elevation: 0,
),
body: GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(target: _center, zoom: 13.0),
),
);
}
}