From 6931d6ada24f1fdb11de19e2c1b66f8240a64f37 Mon Sep 17 00:00:00 2001 From: Carlos Correia <240402@epvc.ptm> Date: Thu, 5 Mar 2026 18:08:06 +0000 Subject: [PATCH] =?UTF-8?q?atualiza=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GOOGLE_MAPS_SETUP.md | 121 --- android/app/src/main/AndroidManifest.xml | 8 +- lib/constants/app_colors.dart | 8 +- lib/constants/app_strings.dart | 63 +- lib/main.dart | 5 +- lib/screens/bluethoot_cpnnection_screen.dart | 0 lib/screens/google_map_screen.dart | 0 lib/screens/google_maps_screen.dart | 891 ------------------ lib/screens/inicial_screen.dart | 175 ++-- lib/screens/logado_inicial_screen.dart | 652 ------------- lib/screens/logado_screen.dart | 430 ++++++--- lib/screens/setting_screen.dart | 34 +- lib/sheets/entrar_sheet.dart | 144 +-- lib/sheets/registrar_sheet.dart | 148 +-- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 190 +++- pubspec.yaml | 28 +- test/widget_test.dart | 17 +- 18 files changed, 725 insertions(+), 2191 deletions(-) delete mode 100644 GOOGLE_MAPS_SETUP.md create mode 100644 lib/screens/bluethoot_cpnnection_screen.dart create mode 100644 lib/screens/google_map_screen.dart delete mode 100644 lib/screens/google_maps_screen.dart delete mode 100644 lib/screens/logado_inicial_screen.dart diff --git a/GOOGLE_MAPS_SETUP.md b/GOOGLE_MAPS_SETUP.md deleted file mode 100644 index 9c0f271..0000000 --- a/GOOGLE_MAPS_SETUP.md +++ /dev/null @@ -1,121 +0,0 @@ -# Configuração do Google Maps API - -## ✅ **JÁ CONFIGURADO - ROTAS REAIS DO GOOGLE MAPS** - -Este projeto agora tem um mapa **100% funcional** com rotas **realistas** do Google Maps! - -### 🗺️ **Funcionalidades Implementadas:** -- ✅ **Localização em tempo real** do usuário -- ✅ **Pesquisa de destinos** (simulada para demonstração) -- ✅ **Rotas REALISTAS** usando Google Directions API -- ✅ **Caminho pelas ruas** (nÃO é linha reta!) -- ✅ **Permissões** configuradas para Android e iOS -- ✅ **Interface intuitiva** com barra de busca -- ✅ **Feedback visual** com SnackBars funcionais - -### �️ **Como as Rotas Funcionam:** - -**🔍 Google Directions API:** -- Faz requisição real para `maps.googleapis.com/maps/api/directions` -- Parâmetros: `origin`, `destination`, `mode=walking` -- Retorna passos detalhados da rota -- Extrai pontos de cada passo (`start_location`, `end_location`) -- Cria polyline com todos os pontos conectados - -**📍 Resultado Visual:** -- ✅ **Linha azul** seguindo ruas reais -- ✅ **Curvas e quinas** como GPS real -- ✅ **Pontos intermediários** das direções -- ✅ **Ajuste automático** de câmera - -### �📱 **Como Usar:** - -1. **Abrir o app** → Solicita permissão de localização -2. **Aguardar GPS** → Mostra sua localização atual (marcador azul) -3. **Pesquisar destino** → Digite "Parque Ibirapuera" ou clique nos chips -4. **Ver rota REAL** → Linha azul segue ruas reais -5. **Controles** → Zoom e botão de localização - -### 🔧 **Configuração Técnica:** - -#### Android: -- ✅ `AndroidManifest.xml` - Permissões de localização -- ✅ API Key configurada -- ✅ `google_maps_flutter` v2.5.3 -- ✅ `geolocator` v10.1.0 -- ✅ `http` v1.1.0 (para Directions API) - -#### iOS: -- ✅ `Info.plist` - Permissões de localização -- ✅ `AppDelegate.swift` - API Key configurada -- ✅ `Podfile` - Dependências iOS - -### 🎯 **Funcionalidades do Mapa:** - -**📍 Localização:** -- Obtém GPS automaticamente -- Marcador azul para posição atual -- Botão "Minha Localização" - -**🔍 Pesquisa:** -- Barra de busca intuitiva -- Sugestões de lugares populares -- Loading indicator durante busca - -**🛣️ Rota REALISTA:** -- **Google Directions API** integrada -- **Caminho pelas ruas** (linha curva) -- **Pontos detalhados** de cada passo -- **Distância e tempo** reais da API - -**📊 Estatísticas:** -- Distância real da API -- Tempo real da API -- Modo: Caminhada - -### 🚀 **Para Testar:** - -```bash -# Limpar e instalar dependências -flutter clean -flutter pub get - -# Rodar o app -flutter run -``` - -### ⚠️ **Importante:** -- **Permissões necessárias** - Aceite quando solicitado -- **GPS ativo** - Necessário para localização precisa -- **Internet** - Para Directions API -- **Modo avião** - Desative para funcionar - -### 📋 **Arquivos Configurados:** -- `lib/screens/google_maps_screen.dart` - Mapa com Directions API ✅ -- `android/app/src/main/AndroidManifest.xml` - Permissões Android ✅ -- `ios/Runner/Info.plist` - Permissões iOS ✅ -- `ios/Runner/AppDelegate.swift` - API Key iOS ✅ -- `pubspec.yaml` - Dependências incluindo http ✅ - -### 🔄 **Como as Rotas São Calculadas:** - -1. **Requisição HTTP** para Google Directions API -2. **Parâmetros:** origin, destination, mode=walking -3. **Resposta JSON** com routes[0].legs[0].steps[] -4. **Extração** de start_location e end_location -5. **Criação** de polyline com pontos conectados -6. **Renderização** no mapa como linha azul - -**Rotas 100% realistas e funcionais!** 🎉✨ - -### � **Exemplo de Console:** -``` -🛣️ Buscando rota real com Google Directions API... -🌐 URL da requisição: https://maps.googleapis.com/maps/api/directions/json?origin=-23.5505,-46.6333&destination=-23.5874,-46.6576&mode=walking&language=pt_BR&key=AIzaSyCk84rxmF044cxKLABf55rEKHDqOcyoV5k -📡 Status code: 200 -✅ Rota encontrada com 12 passos -📍 Total de pontos na rota: 14 -📊 Distância: 2.3 km, Tempo: 28 min -``` - -**Mapa com rotas realistas pronto para uso!** 🗺️🛣️ diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e1093b9..24fe3bc 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -3,8 +3,12 @@ + + + + - + diff --git a/lib/constants/app_colors.dart b/lib/constants/app_colors.dart index 64d8567..8c10458 100644 --- a/lib/constants/app_colors.dart +++ b/lib/constants/app_colors.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; class AppColors { // Background colors - static const Color background = Color.fromARGB(255, 49, 53, 77); + static const Color background = Color.fromARGB(255, 48, 48, 51); static const Color coral = Color(0xFFFF6B6B); // Button colors @@ -11,4 +11,10 @@ class AppColors { // Neutral colors static const Color backgroundGrey = Color.fromARGB(255, 89, 89, 89); + static const Color black = Colors.black; + + // Status colors + static const Color error = Colors.red; + static const Color success = Colors.green; + static const Color transparent = Colors.transparent; } diff --git a/lib/constants/app_strings.dart b/lib/constants/app_strings.dart index c9f2c03..dde70c8 100644 --- a/lib/constants/app_strings.dart +++ b/lib/constants/app_strings.dart @@ -1,8 +1,61 @@ class AppStrings { - // Initial screen - static const String registrar = 'Registrar'; - static const String entrar = 'Entrar'; + // Main Screen + static const String complete = "COMPLETO"; + static const String steps = "PASSOS"; + static const String bpm = "BPM"; + static const String kcal = "K/CAL"; + static const String mapPreview = "MAPA"; + static const String settings = "Configurações"; + static const String groups = "Grupos"; + static const String history = "Histórico"; + static const String notifications = "Notificações"; + static const String profile = "Perfil"; - // Common - static const String appTitle = 'Run Vision Pro'; + // Bluetooth Screen + static const String bluetoothTitle = "DISPOSITIVOS"; + static const String bluetoothConnect = "CONECTAR BLUETOOTH"; + static const String stopSearch = "PARAR BUSCA"; + static const String startSearch = "BUSCAR AGORA"; + static const String searching = "STATUS: BUSCANDO..."; + static const String statusReady = "STATUS: PRONTO"; + static const String statusConnected = "STATUS: CONECTADO"; + static const String nearbyDevices = "Dispositivos próximos"; + static const String active = "ATIVO"; + static const String startSearchInstruction = "Inicie a busca para conectar"; + static const String connect = "CONECTAR"; + static const String noDevicesFound = "Nenhum dispositivo encontrado."; + static const String foundDevices = "Encontrados"; + static const String oneDevice = "1 Dispositivo"; + static const String permissionsDenied = "Permissões de Bluetooth negadas."; + static const String turnOnBluetooth = + "Ligue o Bluetooth para buscar dispositivos."; + static const String scanError = "Erro ao iniciar scan: "; + static const String stopScanError = "Erro ao parar scan: "; + static const String defaultDeviceName = "Dispositivo"; + static const String connectingTo = "Conectando a "; + static const String connectedSuccess = "Conectado com sucesso!"; + static const String connectFail = "Falha ao conectar: "; + static const String deviceIdPrefix = "Disp. ["; + static const String unknownDevice = "Dispositivo Desconhecido"; + + // Map Screen + static const String mapTitleTracking = "TRACKING ATIVO"; + static const String mapTitlePlanning = "PLANEJAR TRAJETO"; + static const String mapTitleRunning = "CORRIDA"; + static const String mapPace = "RITMO"; + static const String mapRoute = "TRAJETO"; + static const String kmhUnit = "KM/H"; + static const String kmUnit = "KM"; + static const String planningInstruction = "Toque para definir Início e Fim"; + static const String btnStop = "PARAR"; + static const String btnSimulate = "SIMULAR"; + static const String btnStartRun = "INICIAR CORRIDA"; + static const String btnStopRun = "PARAR CORRIDA"; + static const String startPoint = "Partida"; + static const String finishPoint = "Chegada"; + + // Auth Screens + static const String entrar = "ENTRAR"; + static const String registrar = "REGISTRAR"; + static const String appTitle = "Run Vision Pro"; } diff --git a/lib/main.dart b/lib/main.dart index 118d08f..0baca23 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'screens/inicial_screen.dart'; import 'constants/app_strings.dart'; import 'services/supabase_service.dart'; -import 'constants/app_constants.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -38,7 +37,9 @@ class MyApp extends StatelessWidget { // // This works for code too, not just values: Most code changes can be // tested with just a hot reload. - colorScheme: .fromSeed(seedColor: Colors.deepPurple), + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.white.withValues(alpha: 0.1), + ), ), home: const InicialScreen(), ); diff --git a/lib/screens/bluethoot_cpnnection_screen.dart b/lib/screens/bluethoot_cpnnection_screen.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/screens/google_map_screen.dart b/lib/screens/google_map_screen.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/screens/google_maps_screen.dart b/lib/screens/google_maps_screen.dart deleted file mode 100644 index 92c47d1..0000000 --- a/lib/screens/google_maps_screen.dart +++ /dev/null @@ -1,891 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:geolocator/geolocator.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:http/http.dart' as http; -import 'dart:convert'; -import '../constants/app_colors.dart'; - -class GoogleMapScreen extends StatefulWidget { - const GoogleMapScreen({super.key}); - - @override - State createState() => _GoogleMapScreenState(); -} - -class _GoogleMapScreenState extends State { - late GoogleMapController mapController; - final Set _markers = {}; - Set _polylines = {}; - - LatLng? _currentLocation; - LatLng? _destination; - bool _isGettingLocation = true; - bool _isRequestingPermission = true; - final TextEditingController _searchController = TextEditingController(); - List> _placeSuggestions = []; - bool _isSearching = false; - - @override - void initState() { - super.initState(); - _requestLocationPermission(); - } - - Future _requestLocationPermission() async { - try { - print('🔐 Solicitando permissões de localização...'); - - // Verificar status atual das permissões - Map statuses = await [ - Permission.location, - Permission.locationWhenInUse, - Permission.locationAlways, - ].request(); - - bool locationGranted = - (statuses[Permission.location]?.isGranted == true) || - (statuses[Permission.locationWhenInUse]?.isGranted == true); - - if (locationGranted) { - print('✅ Permissões de localização concedidas'); - setState(() { - _isRequestingPermission = false; - }); - _getCurrentLocation(); - } else { - print('❌ Permissões de localização negadas'); - setState(() { - _isRequestingPermission = false; - }); - _showLocationPermissionDialog(); - } - } catch (e) { - print('❌ Erro ao solicitar permissões: $e'); - _showSnackBar('Erro ao solicitar permissões de localização', Colors.red); - } - } - - void _showLocationPermissionDialog() { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return AlertDialog( - backgroundColor: AppColors.backgroundGrey, - title: const Text( - 'Permissão de Localização', - style: TextStyle( - color: AppColors.white, - fontWeight: FontWeight.bold, - ), - ), - content: const Text( - 'Este aplicativo precisa acessar sua localização para mostrar rotas de caminhada e calcular distâncias. Por favor, habilite a localização nas configurações do seu dispositivo.', - style: TextStyle(color: AppColors.white), - ), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - _openAppSettings(); - }, - child: const Text( - 'Abrir Configurações', - style: TextStyle(color: AppColors.coral), - ), - ), - TextButton( - onPressed: () { - Navigator.of(context).pop(); - Navigator.of(context).pop(); // Volta para a tela anterior - }, - child: const Text( - 'Cancelar', - style: TextStyle(color: AppColors.white), - ), - ), - ], - ); - }, - ); - } - - Future _openAppSettings() async { - try { - await openAppSettings(); - } catch (e) { - print('❌ Erro ao abrir configurações: $e'); - _showSnackBar('Não foi possível abrir as configurações', Colors.red); - } - } - - Future _getCurrentLocation() async { - try { - print('🔍 Iniciando busca de localização...'); - - // Verificar permissões de localização - LocationPermission permission = await Geolocator.checkPermission(); - print('📍 Permissão atual: $permission'); - - if (permission == LocationPermission.denied) { - print('❌ Permissão negada, solicitando...'); - permission = await Geolocator.requestPermission(); - if (permission == LocationPermission.denied) { - _showSnackBar( - 'Permissão de localização negada. Habilite nas configurações.', - AppColors.coral, - ); - return; - } - } - - if (permission == LocationPermission.deniedForever) { - _showSnackBar( - 'Permissão de localização permanentemente negada. Habilite nas configurações do app.', - AppColors.coral, - ); - return; - } - - // Obter localização atual - print('🛰️ Buscando localização atual...'); - Position position = await Geolocator.getCurrentPosition( - desiredAccuracy: LocationAccuracy.high, - timeLimit: const Duration(seconds: 10), - ); - - print( - '✅ Localização encontrada: ${position.latitude}, ${position.longitude}', - ); - - setState(() { - _currentLocation = LatLng(position.latitude, position.longitude); - _isGettingLocation = false; - }); - - // Adicionar marcador da localização atual - _addCurrentLocationMarker(); - - // Mover câmera para localização atual - if (mounted && _currentLocation != null) { - mapController.animateCamera( - CameraUpdate.newCameraPosition( - CameraPosition(target: _currentLocation!, zoom: 15.0), - ), - ); - } - } catch (e) { - print('❌ Erro ao obter localização: $e'); - setState(() { - _isGettingLocation = false; - }); - _showSnackBar('Erro ao obter localização: ${e.toString()}', Colors.red); - } - } - - void _addCurrentLocationMarker() { - if (_currentLocation != null) { - print('📍 Adicionando marcador de localização atual'); - - // Remover marcador anterior se existir - _markers.removeWhere( - (marker) => marker.markerId.value == 'current_location', - ); - - // Adicionar novo marcador - _markers.add( - Marker( - markerId: const MarkerId('current_location'), - position: _currentLocation!, - infoWindow: const InfoWindow(title: 'Sua Localização'), - icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue), - ), - ); - - setState(() {}); - } - } - - Future _searchPlaces(String query) async { - if (query.isEmpty) { - setState(() { - _placeSuggestions = []; - }); - return; - } - - setState(() { - _isSearching = true; - }); - - try { - // Usar Google Places API para busca real baseada na localização atual - final String url = - 'https://maps.googleapis.com/maps/api/place/autocomplete/json' - '?input=${Uri.encodeComponent(query)}' - '&location=${_currentLocation?.latitude ?? -23.5505},${_currentLocation?.longitude ?? -46.6333}' - '&radius=30000' // 30km de raio - '&language=pt_BR' - '®ion=br' - '&components=country:br' - '&strictbounds=true' // Força busca dentro da área - '&key=AIzaSyCk84rxmF044cxKLABf55rEKHDqOcyoV5k'; - - print('🔍 Buscando lugares: $url'); - print( - '📍 Baseado na localização: ${_currentLocation?.latitude ?? -23.5505}, ${_currentLocation?.longitude ?? -46.6333}', - ); - - final response = await http.get(Uri.parse(url)); - - if (response.statusCode == 200) { - final data = json.decode(response.body); - - if (data['status'] == 'OK') { - final predictions = data['predictions'] as List; - - setState(() { - _placeSuggestions = predictions.map((prediction) { - return { - 'description': prediction['description'], - 'place_id': prediction['place_id'], - 'structured_formatting': prediction['structured_formatting'], - }; - }).toList(); - }); - - print( - '✅ ${_placeSuggestions.length} lugares encontrados próximos à sua localização', - ); - } else { - print( - '❌ Erro na API: ${data['status']} - ${data['error_message'] ?? ''}', - ); - } - } else { - print('❌ Erro HTTP: ${response.statusCode}'); - } - } catch (e) { - print('❌ Erro na busca: $e'); - } finally { - setState(() { - _isSearching = false; - }); - } - } - - Future _selectPlace(Map place) async { - try { - print('📍 Selecionado: ${place['description']}'); - - // Obter detalhes do lugar - final String detailsUrl = - 'https://maps.googleapis.com/maps/api/place/details/json' - '?place_id=${place['place_id']}' - '&fields=geometry' - '&key=AIzaSyCk84rxmF044cxKLABf55rEKHDqOcyoV5k'; - - final response = await http.get(Uri.parse(detailsUrl)); - - if (response.statusCode == 200) { - final data = json.decode(response.body); - - if (data['status'] == 'OK') { - final location = data['result']['geometry']['location']; - final destination = LatLng(location['lat'], location['lng']); - - setState(() { - _destination = destination; - _placeSuggestions = []; // Limpar sugestões - }); - - _addDestinationMarker(destination, place['description']); - await _getDirections(); - } - } - } catch (e) { - print('❌ Erro ao selecionar lugar: $e'); - _showSnackBar('Erro ao selecionar destino', Colors.red); - } - } - - void _addDestinationMarker(LatLng location, String name) { - print('📍 Adicionando marcador de destino: $name'); - - setState(() { - // Remover marcador de destino anterior se existir - _markers.removeWhere((marker) => marker.markerId.value == 'destination'); - - // Adicionar novo marcador de destino - _markers.add( - Marker( - markerId: const MarkerId('destination'), - position: location, - infoWindow: InfoWindow(title: name), - icon: BitmapDescriptor.defaultMarkerWithHue( - BitmapDescriptor.hueAzure, - ), - ), - ); - }); - - print('✅ Marcador de destino adicionado: $name'); - } - - Future _getDirections() async { - if (_currentLocation == null || _destination == null) { - print('❌ Localização ou destino nulo'); - return; - } - - try { - print('🛣️ Buscando rota real com Google Directions API...'); - - // Usar Google Directions API para obter rota realista - final String url = - 'https://maps.googleapis.com/maps/api/directions/json' - '?origin=${_currentLocation!.latitude},${_currentLocation!.longitude}' - '&destination=${_destination!.latitude},${_destination!.longitude}' - '&mode=walking' - '&language=pt_BR' - '&key=AIzaSyCk84rxmF044cxKLABf55rEKHDqOcyoV5k'; - - print('🌐 URL da requisição: $url'); - - final response = await http.get(Uri.parse(url)); - print('📡 Status code: ${response.statusCode}'); - - if (response.statusCode == 200) { - final data = json.decode(response.body); - - if (data['status'] == 'OK' && data['routes'].isNotEmpty) { - final route = data['routes'][0]; - final legs = route['legs'][0]; - final steps = legs['steps']; - - print('✅ Rota encontrada com ${steps.length} passos'); - - // Extrair pontos da rota - final List routePoints = []; - - // Adicionar ponto inicial - routePoints.add(_currentLocation!); - - // Processar cada passo da rota - for (var step in steps) { - final startLocation = step['start_location']; - final endLocation = step['end_location']; - - if (startLocation != null) { - routePoints.add( - LatLng(startLocation['lat'], startLocation['lng']), - ); - } - - if (endLocation != null) { - routePoints.add(LatLng(endLocation['lat'], endLocation['lng'])); - } - } - - // Adicionar ponto final - routePoints.add(_destination!); - - print('📍 Total de pontos na rota: ${routePoints.length}'); - - setState(() { - _polylines.clear(); - _polylines.add( - Polyline( - polylineId: const PolylineId('route'), - color: AppColors.backgroundGrey, - width: 5, - points: routePoints, - ), - ); - }); - - // Ajustar câmera para mostrar toda a rota - _adjustCameraToShowRoute(); - - // Calcular distância e tempo - final distance = legs['distance']['text'] ?? 'Calculando...'; - final duration = legs['duration']['text'] ?? 'Calculando...'; - - print('📊 Distância: $distance, Tempo: $duration'); - - _showSnackBar( - 'Rota de caminhada calculada!\nDistância: $distance\nTempo: $duration', - Colors.green, - ); - } else { - print( - '❌ Erro na resposta: ${data['status']} - ${data['error_message']}', - ); - _showSnackBar( - 'Não foi possível calcular a rota. Tente novamente.', - AppColors.coral, - ); - } - } else { - print('❌ Erro HTTP: ${response.statusCode}'); - _showSnackBar( - 'Erro ao conectar com o servidor. Verifique sua internet.', - Colors.red, - ); - } - } catch (e) { - print('❌ Erro ao calcular rota: $e'); - _showSnackBar('Erro ao calcular rota: ${e.toString()}', Colors.red); - } - } - - void _adjustCameraToShowRoute() { - if (_currentLocation == null || _destination == null) return; - - final double padding = 0.01; // Padding para mostrar os marcadores - final bounds = LatLngBounds( - southwest: LatLng( - (_currentLocation!.latitude < _destination!.latitude - ? _currentLocation!.latitude - : _destination!.latitude) - - padding, - (_currentLocation!.longitude < _destination!.longitude - ? _currentLocation!.longitude - : _destination!.longitude) - - padding, - ), - northeast: LatLng( - (_currentLocation!.latitude > _destination!.latitude - ? _currentLocation!.latitude - : _destination!.latitude) + - padding, - (_currentLocation!.longitude > _destination!.longitude - ? _currentLocation!.longitude - : _destination!.longitude) + - padding, - ), - ); - - print('📷 Ajustando câmera para mostrar rota completa'); - mapController.animateCamera(CameraUpdate.newLatLngBounds(bounds, 100)); - } - - void _onMapCreated(GoogleMapController controller) { - print('🗺️ Mapa criado'); - mapController = controller; - } - - void _showSnackBar(String message, Color color) { - print('💬 Mostrando mensagem: $message'); - if (!mounted) return; - - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(message), - backgroundColor: color, - duration: const Duration(seconds: 3), - behavior: SnackBarBehavior.floating, - ), - ); - } - - void _clearRoute() { - print('🧹 Limpando rota...'); - setState(() { - _destination = null; - _polylines.clear(); - _markers.removeWhere((marker) => marker.markerId.value == 'destination'); - }); - _showSnackBar('Rota limpa. Busque um novo destino.', AppColors.coral); - } - - void _centerOnCurrentLocation() { - if (_currentLocation != null) { - print('🎯 Centralizando na localização atual'); - mapController.animateCamera( - CameraUpdate.newCameraPosition( - CameraPosition(target: _currentLocation!, zoom: 15.0), - ), - ); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: AppColors.background, - body: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - AppColors.backgroundGrey.withOpacity(0.3), - AppColors.background, - ], - ), - ), - child: Stack( - children: [ - // Back button - Positioned( - top: 50, - left: 20, - child: Container( - decoration: BoxDecoration( - color: AppColors.backgroundGrey.withOpacity(0.3), - borderRadius: BorderRadius.circular(12), - border: Border.all(color: Colors.white.withOpacity(0.1)), - ), - child: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.white), - onPressed: () { - Navigator.pop(context); - }, - tooltip: 'Voltar', - ), - ), - ), - - Column( - children: [ - const SizedBox(height: 100), - - // Search section - Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Buscar Destino:', - style: TextStyle( - color: AppColors.white, - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), - const SizedBox(height: 12), - - // Search bar - Container( - decoration: BoxDecoration( - color: AppColors.backgroundGrey.withOpacity(0.2), - borderRadius: BorderRadius.circular(15), - border: Border.all( - color: AppColors.backgroundGrey.withOpacity(0.3), - ), - ), - child: TextField( - controller: _searchController, - keyboardType: TextInputType.text, - textInputAction: TextInputAction.search, - enableSuggestions: true, - autocorrect: true, - decoration: InputDecoration( - hintText: - 'Ex: Parque Ibirapuera, Avenida Paulista...', - hintStyle: TextStyle( - color: AppColors.white.withOpacity(0.5), - ), - border: InputBorder.none, - contentPadding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 12, - ), - prefixIcon: const Icon( - Icons.search, - color: AppColors.white, - ), - suffixIcon: _isSearching - ? const Padding( - padding: EdgeInsets.all(12), - child: SizedBox( - width: 20, - height: 20, - child: CircularProgressIndicator( - color: AppColors.backgroundGrey, - strokeWidth: 2, - ), - ), - ) - : IconButton( - icon: const Icon( - Icons.send, - color: AppColors.backgroundGrey, - ), - onPressed: () => - _searchPlaces(_searchController.text), - ), - ), - style: const TextStyle(color: AppColors.white), - onChanged: (value) { - if (value.length >= 3) { - _searchPlaces(value); - } else { - setState(() { - _placeSuggestions = []; - }); - } - }, - onSubmitted: (value) { - if (_placeSuggestions.isNotEmpty) { - _selectPlace(_placeSuggestions.first); - } - }, - ), - ), - - // Suggestions dropdown - if (_placeSuggestions.isNotEmpty) - Container( - margin: const EdgeInsets.only(top: 8), - decoration: BoxDecoration( - color: AppColors.backgroundGrey.withOpacity(0.2), - borderRadius: BorderRadius.circular(15), - border: Border.all( - color: AppColors.backgroundGrey.withOpacity(0.3), - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: _placeSuggestions.take(5).map((place) { - return InkWell( - onTap: () => _selectPlace(place), - child: Container( - width: double.infinity, - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 12, - ), - child: Row( - children: [ - const Icon( - Icons.place, - color: AppColors.backgroundGrey, - size: 20, - ), - const SizedBox(width: 12), - Expanded( - child: Text( - place['description'], - style: const TextStyle( - color: AppColors.white, - fontSize: 14, - ), - ), - ), - ], - ), - ), - ); - }).toList(), - ), - ), - ], - ), - ), - - const SizedBox(height: 20), - - // Status bar - Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: Row( - children: [ - Text( - _isRequestingPermission - ? "Solicitando permissões..." - : _isGettingLocation - ? "Obtendo localização..." - : _destination != null - ? "Rota ativa" - : "Defina um destino", - style: const TextStyle( - color: AppColors.white, - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), - const Spacer(), - if (_isRequestingPermission) - Container( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 2, - ), - decoration: BoxDecoration( - color: AppColors.backgroundGrey.withOpacity(0.2), - borderRadius: BorderRadius.circular(10), - ), - child: const Text( - "PERMISSÃO", - style: TextStyle( - color: AppColors.backgroundGrey, - fontSize: 10, - fontWeight: FontWeight.bold, - ), - ), - ) - else if (_destination != null) - Container( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 2, - ), - decoration: BoxDecoration( - color: AppColors.backgroundGrey.withOpacity(0.2), - borderRadius: BorderRadius.circular(10), - ), - child: const Text( - "ATIVO", - style: TextStyle( - color: AppColors.backgroundGrey, - fontSize: 10, - fontWeight: FontWeight.bold, - ), - ), - ), - ], - ), - ), - - const SizedBox(height: 15), - - // Map - Expanded( - child: Container( - margin: const EdgeInsets.symmetric(horizontal: 20), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - border: Border.all( - color: AppColors.backgroundGrey.withOpacity(0.3), - ), - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(20), - child: Stack( - children: [ - GoogleMap( - onMapCreated: _onMapCreated, - initialCameraPosition: CameraPosition( - target: - _currentLocation ?? - const LatLng(-23.5505, -46.6333), - zoom: 15.0, - ), - markers: _markers, - polylines: _polylines, - mapType: MapType.normal, - myLocationEnabled: true, - myLocationButtonEnabled: false, - zoomControlsEnabled: false, - ), - - // Loading indicator - if (_isRequestingPermission || _isGettingLocation) - Container( - color: Colors.black54, - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const CircularProgressIndicator( - color: AppColors.backgroundGrey, - strokeWidth: 3, - ), - const SizedBox(height: 16), - Text( - _isRequestingPermission - ? 'Solicitando permissões de localização...' - : 'Obtendo sua localização...', - style: const TextStyle( - color: Colors.white, - fontSize: 16, - ), - ), - ], - ), - ), - ), - - // Map controls - Positioned( - top: 20, - right: 20, - child: Column( - children: [ - Container( - decoration: BoxDecoration( - color: AppColors.backgroundGrey.withOpacity( - 0.8, - ), - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: Colors.white.withOpacity(0.1), - ), - ), - child: Column( - children: [ - IconButton( - icon: const Icon( - Icons.add, - color: Colors.white, - ), - onPressed: () { - mapController.animateCamera( - CameraUpdate.zoomIn(), - ); - }, - tooltip: 'Aumentar zoom', - ), - IconButton( - icon: const Icon( - Icons.remove, - color: Colors.white, - ), - onPressed: () { - mapController.animateCamera( - CameraUpdate.zoomOut(), - ); - }, - tooltip: 'Diminuir zoom', - ), - IconButton( - icon: const Icon( - Icons.my_location, - color: Colors.white, - ), - onPressed: _centerOnCurrentLocation, - tooltip: 'Minha localização', - ), - IconButton( - icon: const Icon( - Icons.clear, - color: Colors.white, - ), - onPressed: _clearRoute, - tooltip: 'Limpar rota', - ), - ], - ), - ), - ], - ), - ), - ], - ), - ), - ), - ), - - const SizedBox(height: 20), - ], - ), - ], - ), - ), - ); - } -} diff --git a/lib/screens/inicial_screen.dart b/lib/screens/inicial_screen.dart index 4345b24..4fb82d2 100644 --- a/lib/screens/inicial_screen.dart +++ b/lib/screens/inicial_screen.dart @@ -5,101 +5,6 @@ import '../sheets/entrar_sheet.dart'; import '../sheets/registrar_sheet.dart'; import '../services/supabase_service.dart'; -class AnimatedButton extends StatefulWidget { - final String text; - final VoidCallback onPressed; - final Color backgroundColor; - final Color textColor; - - const AnimatedButton({ - super.key, - required this.text, - required this.onPressed, - required this.backgroundColor, - required this.textColor, - }); - - @override - State createState() => _AnimatedButtonState(); -} - -class _AnimatedButtonState extends State - with SingleTickerProviderStateMixin { - late AnimationController _controller; - late Animation _scaleAnimation; - late Animation _bounceAnimation; - - @override - void initState() { - super.initState(); - _controller = AnimationController( - duration: const Duration(milliseconds: 150), - vsync: this, - ); - _scaleAnimation = Tween( - begin: 1.0, - end: 0.92, - ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut)); - _bounceAnimation = Tween(begin: 0.0, end: -3.0).animate( - CurvedAnimation( - parent: _controller, - curve: const Interval(0.3, 0.8, curve: Curves.elasticOut), - ), - ); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - void _handleTap() { - _controller.forward().then((_) { - _controller.reverse().then((_) { - widget.onPressed(); - }); - }); - } - - @override - Widget build(BuildContext context) { - return AnimatedBuilder( - animation: _controller, - builder: (context, child) { - return Transform.translate( - offset: Offset(0, _bounceAnimation.value), - child: Transform.scale( - scale: _scaleAnimation.value, - child: SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - onPressed: _handleTap, - style: ElevatedButton.styleFrom( - backgroundColor: widget.backgroundColor, - foregroundColor: widget.textColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30), - ), - elevation: 5, - ), - child: Text( - widget.text, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - ), - ), - ), - ), - ), - ); - }, - ); - } -} - class InicialScreen extends StatelessWidget { const InicialScreen({super.key}); @@ -119,32 +24,64 @@ class InicialScreen extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - AnimatedButton( - text: AppStrings.registrar, - onPressed: () { - showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (context) => const RegistrarSheet(), - ); - }, - backgroundColor: AppColors.buttonColor, - textColor: AppColors.white, + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + onPressed: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (context) => const RegistrarSheet(), + ); + }, + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.buttonColor, + foregroundColor: AppColors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), + ), + elevation: 5, + ), + child: Text( + AppStrings.registrar, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ), ), const SizedBox(height: 16), - AnimatedButton( - text: AppStrings.entrar, - onPressed: () { - showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (context) => const EntrarSheet(), - ); - }, - backgroundColor: AppColors.buttonColor, - textColor: AppColors.white, + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + onPressed: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (context) => const EntrarSheet(), + ); + }, + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.buttonColor, + foregroundColor: AppColors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), + ), + elevation: 5, + ), + child: Text( + AppStrings.entrar, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ), ), ], ), diff --git a/lib/screens/logado_inicial_screen.dart b/lib/screens/logado_inicial_screen.dart deleted file mode 100644 index 744755a..0000000 --- a/lib/screens/logado_inicial_screen.dart +++ /dev/null @@ -1,652 +0,0 @@ -import 'dart:async'; -import 'package:flutter/material.dart'; -import '../constants/app_colors.dart'; -import '../services/supabase_service.dart'; -import '../screens/inicial_screen.dart'; -import '../screens/google_maps_screen.dart'; -import '../screens/setting_screen.dart'; - -/// Tela principal para usuários logados com estatísticas de corrida e menu. -class LogadoInicialScreen extends StatefulWidget { - const LogadoInicialScreen({super.key}); - - @override - State createState() => _LogadoInicialScreenState(); -} - -class _LogadoInicialScreenState 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. - - @override - void initState() { - super.initState(); - // Simular progresso inicial - _simulateProgress(); - } - - void _simulateProgress() { - Timer.periodic(const Duration(seconds: 3), (timer) { - if (mounted && progress < 1.0) { - setState(() { - progress = (progress + 0.1).clamp(0.0, 1.0); - currentDistance = targetDistance * progress; - }); - } else { - timer.cancel(); - } - }); - } - - /// 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) { - final user = SupabaseService.currentUser; - final userName = - user?.userMetadata?['name'] ?? user?.email?.split('@')[0] ?? 'Usuário'; - - return Scaffold( - backgroundColor: AppColors.background, - body: Stack( - children: [ - // Static dark gray triangles - Positioned( - top: -50, - left: -80, - child: CustomPaint( - size: const Size(160, 120), - painter: TrianglePainter( - color: Colors.grey.shade800.withOpacity(0.4), - ), - ), - ), - Positioned( - top: 20, - right: -60, - child: CustomPaint( - size: const Size(120, 90), - painter: TrianglePainter( - color: Colors.grey.shade800.withOpacity(0.3), - ), - ), - ), - Positioned( - top: 80, - left: 40, - child: CustomPaint( - size: const Size(140, 105), - painter: TrianglePainter( - color: Colors.grey.shade800.withOpacity(0.35), - ), - ), - ), - Positioned( - top: 120, - right: 80, - child: CustomPaint( - size: const Size(100, 75), - painter: TrianglePainter( - color: Colors.grey.shade800.withOpacity(0.25), - ), - ), - ), - Positioned( - top: 160, - left: -40, - child: CustomPaint( - size: const Size(130, 98), - painter: TrianglePainter( - color: Colors.grey.shade800.withOpacity(0.3), - ), - ), - ), - - // User info header - Positioned( - top: 40, - left: 20, - right: 20, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Olá, $userName!', - style: const TextStyle( - color: Colors.white, - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), - Text( - 'Bem-vindo de volta!', - style: TextStyle(color: Colors.white70, fontSize: 14), - ), - ], - ), - Container( - width: 50, - height: 50, - decoration: BoxDecoration( - color: AppColors.buttonColor, - borderRadius: BorderRadius.circular(25), - ), - child: const Icon( - Icons.person, - color: AppColors.white, - size: 24, - ), - ), - ], - ), - ), - - // 1. Indicador de progresso circular posicionado no topo central. - Align( - alignment: Alignment.topCenter, - child: Padding( - padding: const EdgeInsets.only(top: 140), - child: _buildCircularProgressIndicator(), - ), - ), - - // 2. Exibição da distância (ex: 0.0 KM | 8.0 KM). - Positioned( - top: 360, - 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 clicável. - Positioned( - top: 420, - 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 mapa clicável. - 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. - ), - // Overlay para indicar que é clicável - Container( - color: Colors.black.withOpacity(0.1), - child: const Center( - child: Icon( - Icons.touch_app, - color: Colors.white54, - size: 40, - ), - ), - ), - ], - ), - ), - ), - ), - ], - ), - ), - ), - - // 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', - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SettingsScreen(), - ), - ); - }, - ), - _buildMenuButton(Icons.group_outlined, 'Grupos'), - _buildMenuButton(Icons.access_time, 'Histórico'), - _buildMenuButton( - Icons.notifications_none, - 'Notificações', - showBadge: true, - ), - _buildMenuButton(Icons.logout, 'Sair', isLogout: true), - ], - ), - ), - - // 6. Botão de Bluetooth no canto superior direito. - Positioned( - top: 40, - right: 30, - child: GestureDetector( - onTap: () { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Bluetooth clicado!'), - duration: Duration(seconds: 1), - ), - ); - }, - 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. - Widget _buildMenuButton( - IconData icon, - String message, { - bool showBadge = false, - bool isLogout = false, - VoidCallback? onTap, - }) { - return GestureDetector( - onTap: - onTap ?? - () async { - if (isLogout) { - await SupabaseService.signOut(); - if (mounted) { - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (context) => const InicialScreen(), - ), - (route) => false, - ); - } - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('$message clicado!'), - duration: const Duration(seconds: 1), - ), - ); - } - }, - child: Stack( - children: [ - Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: isLogout ? Colors.red.shade600 : 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 os triângulos estáticos. -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; - } -} - -/// 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(CircularProgressPainter oldDelegate) => - oldDelegate.progress != progress; -} diff --git a/lib/screens/logado_screen.dart b/lib/screens/logado_screen.dart index 845ce82..a8358e4 100644 --- a/lib/screens/logado_screen.dart +++ b/lib/screens/logado_screen.dart @@ -1,24 +1,34 @@ import 'package:flutter/material.dart'; import '../constants/app_colors.dart'; -import '../screens/inicial_screen.dart'; +import '../constants/app_strings.dart'; +import '../screens/setting_screen.dart'; +import '../screens/bluetooth_connection_screen.dart'; +import '../screens/google_maps_screen.dart'; -class LogadoScreen extends StatelessWidget { +class LogadoScreen extends StatefulWidget { const LogadoScreen({super.key}); + @override + State createState() => _LogadoScreenState(); +} + +class _LogadoScreenState extends State { + double progress = 0.65; // Simulação de progresso + @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.background, body: Stack( children: [ - // Static dark gray triangles + // Background triangles Positioned( top: -50, left: -80, child: CustomPaint( size: const Size(160, 120), painter: TrianglePainter( - color: Colors.grey.shade800.withOpacity(0.4), + color: Colors.grey.shade800.withValues(alpha: 0.4), ), ), ), @@ -28,7 +38,7 @@ class LogadoScreen extends StatelessWidget { child: CustomPaint( size: const Size(120, 90), painter: TrianglePainter( - color: Colors.grey.shade800.withOpacity(0.3), + color: Colors.grey.shade800.withAlpha(76), ), ), ), @@ -38,7 +48,7 @@ class LogadoScreen extends StatelessWidget { child: CustomPaint( size: const Size(140, 105), painter: TrianglePainter( - color: Colors.grey.shade800.withOpacity(0.35), + color: Colors.grey.shade800.withAlpha(89), ), ), ), @@ -48,7 +58,7 @@ class LogadoScreen extends StatelessWidget { child: CustomPaint( size: const Size(100, 75), painter: TrianglePainter( - color: Colors.grey.shade800.withOpacity(0.25), + color: Colors.grey.shade800.withAlpha(51), ), ), ), @@ -58,7 +68,7 @@ class LogadoScreen extends StatelessWidget { child: CustomPaint( size: const Size(130, 98), painter: TrianglePainter( - color: Colors.grey.shade800.withOpacity(0.3), + color: Colors.grey.shade800.withAlpha(76), ), ), ), @@ -68,127 +78,329 @@ class LogadoScreen extends StatelessWidget { child: Padding( padding: const EdgeInsets.all(24.0), child: Column( - mainAxisAlignment: MainAxisAlignment.center, children: [ - const SizedBox(height: 80), // Space for triangles - // Success icon with animation - TweenAnimationBuilder( - duration: const Duration(milliseconds: 600), - tween: Tween(begin: 0.0, end: 1.0), - builder: (context, value, child) { - return Transform.scale( - scale: value, - child: Container( - width: 100, - height: 100, - decoration: BoxDecoration( - color: AppColors.buttonColor, - borderRadius: BorderRadius.circular(50), - boxShadow: [ - BoxShadow( - color: AppColors.buttonColor.withOpacity(0.3), - blurRadius: 20, - spreadRadius: 5, - ), - ], - ), - child: const Icon( - Icons.check, - size: 50, - color: AppColors.white, - ), - ), - ); - }, - ), - const SizedBox(height: 32), + const SizedBox(height: 60), - // Welcome message with fade in - TweenAnimationBuilder( - duration: const Duration(milliseconds: 800), - tween: Tween(begin: 0.0, end: 1.0), - builder: (context, value, child) { - return Opacity( - opacity: value, - child: const Text( - 'Login Realizado!', - style: TextStyle( - fontSize: 32, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - textAlign: TextAlign.center, - ), - ); - }, - ), - const SizedBox(height: 16), - - TweenAnimationBuilder( - duration: const Duration(milliseconds: 1000), - tween: Tween(begin: 0.0, end: 1.0), - builder: (context, value, child) { - return Opacity( - opacity: value, - child: const Text( - 'Você está autenticado com sucesso.', - style: TextStyle(fontSize: 18, color: Colors.white70), - textAlign: TextAlign.center, - ), - ); - }, - ), - const SizedBox(height: 48), - - // Back button with slide up animation - TweenAnimationBuilder( - duration: const Duration(milliseconds: 1200), - tween: Tween( - begin: Offset(0, 1), - end: Offset(0, 0), - ), - builder: (context, value, child) { - return Transform.translate( - offset: value * 50, - child: SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - onPressed: () { - Navigator.of(context).pushReplacement( - MaterialPageRoute( - builder: (context) => const InicialScreen(), - ), - ); - }, - style: ElevatedButton.styleFrom( - backgroundColor: AppColors.buttonColor, - foregroundColor: AppColors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30), - ), - elevation: 5, + // Header with user info + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Bem-vindo!', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.white, ), - child: const Text( - 'Voltar para Tela Inicial', + ), + Text( + 'Usuário Logado', + style: TextStyle( + fontSize: 16, + color: Colors.white.withValues(alpha: 0.7), + ), + ), + ], + ), + Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: AppColors.buttonColor, + borderRadius: BorderRadius.circular(25), + ), + child: const Icon( + Icons.person, + color: Colors.white, + size: 30, + ), + ), + ], + ), + + 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), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Meta Diária', style: TextStyle( fontSize: 18, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.bold, + color: Colors.white, ), ), + Text( + '${(progress * 100).toInt()}%', + style: const TextStyle( + fontSize: 16, + color: Colors.white, + ), + ), + ], + ), + 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: 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 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) { + return Container( + padding: const EdgeInsets.all(15), + decoration: BoxDecoration( + color: AppColors.backgroundGrey, + borderRadius: BorderRadius.circular(12), + ), + child: Column( + children: [ + Text( + value, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: color, + ), + ), + const SizedBox(height: 5), + Text( + title, + style: const TextStyle(fontSize: 12, color: Colors.white70), + ), + ], + ), + ); + } + + Widget _buildMenuButton( + IconData icon, + String label, { + bool showBadge = false, + bool isAvatar = false, + }) { + return GestureDetector( + onTap: () { + _handleMenuTap(label); + }, + child: Column( + mainAxisSize: MainAxisSize.min, + 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), + ), + ), + ), + ], + ), + ), + const SizedBox(height: 8), + Text( + label, + style: const TextStyle(fontSize: 12, color: Colors.white70), + ), + ], + ), + ); + } + + Widget _buildSmallActionButton(IconData icon, Color color) { + return GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const BluetoothConnectionScreen(), + ), + ); + }, + 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), + ), + ); + } + + 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 diff --git a/lib/screens/setting_screen.dart b/lib/screens/setting_screen.dart index 4f1fd5b..f855e9e 100644 --- a/lib/screens/setting_screen.dart +++ b/lib/screens/setting_screen.dart @@ -82,7 +82,7 @@ class _SettingsScreenState extends State { Text( userEmail, style: TextStyle( - color: Colors.white.withOpacity(0.7), + color: Colors.white.withValues(alpha: 0.7), fontSize: 14, ), ), @@ -127,12 +127,12 @@ class _SettingsScreenState extends State { title: 'Modo Noturno', trailing: Switch( value: _isNightMode, + activeThumbColor: AppColors.buttonColor, onChanged: (value) { setState(() { _isNightMode = value; }); }, - activeColor: AppColors.buttonColor, ), ), _buildDivider(), @@ -142,7 +142,7 @@ class _SettingsScreenState extends State { trailing: Text( _selectedLanguage, style: TextStyle( - color: Colors.white.withOpacity(0.7), + color: Colors.white.withValues(alpha: 0.7), fontSize: 16, ), ), @@ -174,7 +174,7 @@ class _SettingsScreenState extends State { _notificationsEnabled = value; }); }, - activeColor: AppColors.buttonColor, + activeThumbColor: AppColors.buttonColor, ), ), _buildDivider(), @@ -218,7 +218,7 @@ class _SettingsScreenState extends State { const SizedBox(height: 24), // Logout Button - Container( + SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () { @@ -270,7 +270,7 @@ class _SettingsScreenState extends State { Widget _buildDivider() { return Divider( - color: Colors.white.withOpacity(0.1), + color: AppColors.buttonColor.withValues(alpha: 0.2), height: 1, indent: 16, endIndent: 16, @@ -284,7 +284,7 @@ class _SettingsScreenState extends State { firstDate: DateTime(2020), lastDate: DateTime(2025), ).then((date) { - if (date != null) { + if (date != null && mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Data selecionada: ${date.toString().split(' ')[0]}'), @@ -326,17 +326,25 @@ class _SettingsScreenState extends State { } Widget _buildLanguageOption(String language) { - return RadioListTile( + return ListTile( title: Text(language, style: const TextStyle(color: Colors.white)), - value: language, - groupValue: _selectedLanguage, - onChanged: (value) { + trailing: Radio( + value: language, + groupValue: _selectedLanguage, + onChanged: (value) { + setState(() { + _selectedLanguage = value!; + }); + Navigator.pop(context); + }, + fillColor: WidgetStateProperty.all(AppColors.buttonColor), + ), + onTap: () { setState(() { - _selectedLanguage = value!; + _selectedLanguage = language; }); Navigator.pop(context); }, - activeColor: AppColors.buttonColor, ); } diff --git a/lib/sheets/entrar_sheet.dart b/lib/sheets/entrar_sheet.dart index ab09533..e5557d5 100644 --- a/lib/sheets/entrar_sheet.dart +++ b/lib/sheets/entrar_sheet.dart @@ -1,106 +1,7 @@ import 'package:flutter/material.dart'; import '../constants/app_colors.dart'; import '../services/supabase_service.dart'; -import '../screens/logado_inicial_screen.dart'; - -class AnimatedButton extends StatefulWidget { - final String text; - final VoidCallback onPressed; - final Color backgroundColor; - final Color textColor; - final bool isLoading; - - const AnimatedButton({ - super.key, - required this.text, - required this.onPressed, - required this.backgroundColor, - required this.textColor, - this.isLoading = false, - }); - - @override - State createState() => _AnimatedButtonState(); -} - -class _AnimatedButtonState extends State - with SingleTickerProviderStateMixin { - late AnimationController _controller; - late Animation _scaleAnimation; - late Animation _bounceAnimation; - - @override - void initState() { - super.initState(); - _controller = AnimationController( - duration: const Duration(milliseconds: 150), - vsync: this, - ); - _scaleAnimation = Tween( - begin: 1.0, - end: 0.92, - ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut)); - _bounceAnimation = Tween(begin: 0.0, end: -3.0).animate( - CurvedAnimation( - parent: _controller, - curve: const Interval(0.3, 0.8, curve: Curves.elasticOut), - ), - ); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - void _handleTap() { - _controller.forward().then((_) { - _controller.reverse().then((_) { - widget.onPressed(); - }); - }); - } - - @override - Widget build(BuildContext context) { - return AnimatedBuilder( - animation: _controller, - builder: (context, child) { - return Transform.translate( - offset: Offset(0, _bounceAnimation.value), - child: Transform.scale( - scale: _scaleAnimation.value, - child: SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - onPressed: widget.isLoading ? null : _handleTap, - style: ElevatedButton.styleFrom( - backgroundColor: widget.backgroundColor, - foregroundColor: widget.textColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30), - ), - elevation: 5, - ), - child: widget.isLoading - ? const CircularProgressIndicator(color: Colors.white) - : Text( - widget.text, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - ), - ), - ), - ), - ), - ); - }, - ); - } -} +import '../screens/logado_screen.dart'; class EntrarSheet extends StatefulWidget { const EntrarSheet({super.key}); @@ -137,11 +38,9 @@ class _EntrarSheetState extends State { ), ); - // Then navigate to LogadoInicialScreen + // Navigate to LogadoScreen Navigator.of(context).pushReplacement( - MaterialPageRoute( - builder: (context) => const LogadoInicialScreen(), - ), + MaterialPageRoute(builder: (context) => const LogadoScreen()), ); } } catch (e) { @@ -222,7 +121,7 @@ class _EntrarSheetState extends State { color: AppColors.backgroundGrey, boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.1), + color: Colors.black.withValues(alpha: 0.1), blurRadius: 10, offset: const Offset(0, -5), ), @@ -273,7 +172,7 @@ class _EntrarSheetState extends State { controller: _emailController, decoration: InputDecoration( filled: true, - fillColor: Colors.white.withOpacity(0.1), + fillColor: AppColors.white.withValues(alpha: 0.5), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, @@ -306,7 +205,7 @@ class _EntrarSheetState extends State { obscureText: true, decoration: InputDecoration( filled: true, - fillColor: Colors.white.withOpacity(0.1), + fillColor: AppColors.white.withValues(alpha: 0.5), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, @@ -328,12 +227,31 @@ class _EntrarSheetState extends State { const SizedBox(height: 32), // Login button - AnimatedButton( - text: 'Entrar', - onPressed: _handleLogin, - backgroundColor: AppColors.buttonColor, - textColor: AppColors.white, - isLoading: _isLoading, + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + onPressed: _isLoading ? null : _handleLogin, + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.buttonColor, + foregroundColor: AppColors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), + ), + elevation: 5, + ), + child: _isLoading + ? const CircularProgressIndicator( + color: Colors.white, + ) + : const Text( + 'Entrar', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ), ), const SizedBox(height: 20), diff --git a/lib/sheets/registrar_sheet.dart b/lib/sheets/registrar_sheet.dart index 6904eeb..3b79465 100644 --- a/lib/sheets/registrar_sheet.dart +++ b/lib/sheets/registrar_sheet.dart @@ -1,106 +1,7 @@ import 'package:flutter/material.dart'; -import '../constants/app_colors.dart'; import '../services/supabase_service.dart'; -import '../screens/logado_inicial_screen.dart'; - -class AnimatedButton extends StatefulWidget { - final String text; - final VoidCallback onPressed; - final Color backgroundColor; - final Color textColor; - final bool isLoading; - - const AnimatedButton({ - super.key, - required this.text, - required this.onPressed, - required this.backgroundColor, - required this.textColor, - this.isLoading = false, - }); - - @override - State createState() => _AnimatedButtonState(); -} - -class _AnimatedButtonState extends State - with SingleTickerProviderStateMixin { - late AnimationController _controller; - late Animation _scaleAnimation; - late Animation _bounceAnimation; - - @override - void initState() { - super.initState(); - _controller = AnimationController( - duration: const Duration(milliseconds: 150), - vsync: this, - ); - _scaleAnimation = Tween( - begin: 1.0, - end: 0.92, - ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut)); - _bounceAnimation = Tween(begin: 0.0, end: -3.0).animate( - CurvedAnimation( - parent: _controller, - curve: const Interval(0.3, 0.8, curve: Curves.elasticOut), - ), - ); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - void _handleTap() { - _controller.forward().then((_) { - _controller.reverse().then((_) { - widget.onPressed(); - }); - }); - } - - @override - Widget build(BuildContext context) { - return AnimatedBuilder( - animation: _controller, - builder: (context, child) { - return Transform.translate( - offset: Offset(0, _bounceAnimation.value), - child: Transform.scale( - scale: _scaleAnimation.value, - child: SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - onPressed: widget.isLoading ? null : _handleTap, - style: ElevatedButton.styleFrom( - backgroundColor: widget.backgroundColor, - foregroundColor: widget.textColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30), - ), - elevation: 5, - ), - child: widget.isLoading - ? const CircularProgressIndicator(color: Colors.white) - : Text( - widget.text, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - ), - ), - ), - ), - ), - ); - }, - ); - } -} +import '../constants/app_colors.dart'; +import '../screens/logado_screen.dart'; class RegistrarSheet extends StatefulWidget { const RegistrarSheet({super.key}); @@ -144,11 +45,9 @@ class _RegistrarSheetState extends State { ), ); - // Then navigate to LogadoInicialScreen + // Navigate to LogadoScreen Navigator.of(context).pushReplacement( - MaterialPageRoute( - builder: (context) => const LogadoInicialScreen(), - ), + MaterialPageRoute(builder: (context) => const LogadoScreen()), ); } } catch (e) { @@ -193,7 +92,7 @@ class _RegistrarSheetState extends State { color: AppColors.backgroundGrey, boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.1), + color: Colors.black.withValues(alpha: 0.1), blurRadius: 10, offset: const Offset(0, -5), ), @@ -254,7 +153,7 @@ class _RegistrarSheetState extends State { controller: _nameController, decoration: InputDecoration( filled: true, - fillColor: Colors.white.withOpacity(0.1), + fillColor: AppColors.white.withValues(alpha: 0.5), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, @@ -285,7 +184,7 @@ class _RegistrarSheetState extends State { controller: _emailController, decoration: InputDecoration( filled: true, - fillColor: Colors.white.withOpacity(0.1), + fillColor: AppColors.white.withValues(alpha: 0.5), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, @@ -317,7 +216,7 @@ class _RegistrarSheetState extends State { obscureText: true, decoration: InputDecoration( filled: true, - fillColor: Colors.white.withOpacity(0.1), + fillColor: AppColors.white.withValues(alpha: 0.5), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, @@ -349,7 +248,7 @@ class _RegistrarSheetState extends State { obscureText: true, decoration: InputDecoration( filled: true, - fillColor: Colors.white.withOpacity(0.1), + fillColor: AppColors.white.withValues(alpha: 0.5), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, @@ -371,12 +270,29 @@ class _RegistrarSheetState extends State { const SizedBox(height: 32), // Register button - AnimatedButton( - text: 'Registrar', - onPressed: _handleRegister, - backgroundColor: AppColors.buttonColor, - textColor: AppColors.white, - isLoading: _isLoading, + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + onPressed: _isLoading ? null : _handleRegister, + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.buttonColor, + foregroundColor: AppColors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), + ), + elevation: 5, + ), + child: _isLoading + ? const CircularProgressIndicator(color: Colors.white) + : const Text( + 'Registrar', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ), ), SizedBox(height: 40), ], diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 0115915..d732731 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,12 +6,14 @@ import FlutterMacOS import Foundation import app_links +import flutter_blue_plus_darwin import geolocator_apple import shared_preferences_foundation import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) + FlutterBluePlusPlugin.register(with: registry.registrar(forPlugin: "FlutterBluePlusPlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) diff --git a/pubspec.lock b/pubspec.lock index e38d86e..5ea83d4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -49,6 +49,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.7" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" async: dependency: transitive description: @@ -57,6 +65,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.13.0" + bluez: + dependency: transitive + description: + name: bluez + sha256: "61a7204381925896a374301498f2f5399e59827c6498ae1e924aaa598751b545" + url: "https://pub.dev" + source: hosted + version: "0.8.3" boolean_selector: dependency: transitive description: @@ -137,6 +153,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.3.1" + dbus: + dependency: transitive + description: + name: dbus + sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270 + url: "https://pub.dev" + source: hosted + version: "0.7.12" ed25519_edwards: dependency: transitive description: @@ -182,14 +206,70 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_blue_plus: + dependency: "direct main" + description: + name: flutter_blue_plus + sha256: "69a8c87c11fc792e8cf0f997d275484fbdb5143ac9f0ac4d424429700cb4e0ed" + url: "https://pub.dev" + source: hosted + version: "1.36.8" + flutter_blue_plus_android: + dependency: transitive + description: + name: flutter_blue_plus_android + sha256: "6f7fe7e69659c30af164a53730707edc16aa4d959e4c208f547b893d940f853d" + url: "https://pub.dev" + source: hosted + version: "7.0.4" + flutter_blue_plus_darwin: + dependency: transitive + description: + name: flutter_blue_plus_darwin + sha256: "682982862c1d964f4d54a3fb5fccc9e59a066422b93b7e22079aeecd9c0d38f8" + url: "https://pub.dev" + source: hosted + version: "7.0.3" + flutter_blue_plus_linux: + dependency: transitive + description: + name: flutter_blue_plus_linux + sha256: "56b0c45edd0a2eec8f85bd97a26ac3cd09447e10d0094fed55587bf0592e3347" + url: "https://pub.dev" + source: hosted + version: "7.0.3" + flutter_blue_plus_platform_interface: + dependency: transitive + description: + name: flutter_blue_plus_platform_interface + sha256: "84fbd180c50a40c92482f273a92069960805ce324e3673ad29c41d2faaa7c5c2" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + flutter_blue_plus_web: + dependency: transitive + description: + name: flutter_blue_plus_web + sha256: a1aceee753d171d24c0e0cdadb37895b5e9124862721f25f60bb758e20b72c99 + url: "https://pub.dev" + source: hosted + version: "7.0.2" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "2.0.3" + flutter_map: + dependency: "direct main" + description: + name: flutter_map + sha256: "87cc8349b8fa5dccda5af50018c7374b6645334a0d680931c1fe11bce88fa5bb" + url: "https://pub.dev" + source: hosted + version: "6.2.1" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -198,14 +278,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.33" - flutter_polyline_points: - dependency: "direct main" - description: - name: flutter_polyline_points - sha256: "3a1c8c30abee9fb0fbe44c70d5d1cedb10ef28ec7ea285c669f02b3e183483aa" - url: "https://pub.dev" - source: hosted - version: "2.1.0" flutter_test: dependency: "direct dev" description: flutter @@ -300,10 +372,10 @@ packages: dependency: transitive description: name: google_maps_flutter_android - sha256: "8b569c7abc52bc62d4502bf93847d487c0843f3e6a2a8e122b72e98843b2ab4c" + sha256: ba0947315ddc9107ecc8d95fa26eb3b87b4f27b221606ce72518314d99c7306c url: "https://pub.dev" source: hosted - version: "2.19.1" + version: "2.19.2" google_maps_flutter_ios: dependency: transitive description: @@ -361,7 +433,7 @@ packages: source: hosted version: "0.15.6" http: - dependency: "direct main" + dependency: transitive description: name: http sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" @@ -376,6 +448,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + intl: + dependency: transitive + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" jwt_decode: dependency: transitive description: @@ -384,6 +464,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.1" + latlong2: + dependency: "direct main" + description: + name: latlong2 + sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe" + url: "https://pub.dev" + source: hosted + version: "0.9.1" leak_tracker: dependency: transitive description: @@ -412,10 +500,26 @@ packages: dependency: transitive description: name: lints - sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "2.1.1" + lists: + dependency: transitive + description: + name: lists + sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + logger: + dependency: transitive + description: + name: logger + sha256: a7967e31b703831a893bbc3c3dd11db08126fe5f369b5c648a36f821979f5be3 + url: "https://pub.dev" + source: hosted + version: "2.6.2" logging: dependency: transitive description: @@ -456,6 +560,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.0" + mgrs_dart: + dependency: transitive + description: + name: mgrs_dart + sha256: fb89ae62f05fa0bb90f70c31fc870bcbcfd516c843fb554452ab3396f78586f7 + url: "https://pub.dev" + source: hosted + version: "2.0.0" mime: dependency: transitive description: @@ -584,6 +696,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.1" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" + url: "https://pub.dev" + source: hosted + version: "7.0.2" platform: dependency: transitive description: @@ -608,6 +728,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + polylabel: + dependency: transitive + description: + name: polylabel + sha256: "41b9099afb2aa6c1730bdd8a0fab1400d287694ec7615dd8516935fa3144214b" + url: "https://pub.dev" + source: hosted + version: "1.0.1" posix: dependency: transitive description: @@ -624,6 +752,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.6.0" + proj4dart: + dependency: transitive + description: + name: proj4dart + sha256: c8a659ac9b6864aa47c171e78d41bbe6f5e1d7bd790a5814249e6b68bc44324e + url: "https://pub.dev" + source: hosted + version: "2.1.0" pub_semver: dependency: transitive description: @@ -813,6 +949,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + unicode: + dependency: transitive + description: + name: unicode + sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1" + url: "https://pub.dev" + source: hosted + version: "0.3.1" url_launcher: dependency: transitive description: @@ -925,6 +1069,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + wkt_parser: + dependency: transitive + description: + name: wkt_parser + sha256: "8a555fc60de3116c00aad67891bcab20f81a958e4219cc106e3c037aa3937f13" + url: "https://pub.dev" + source: hosted + version: "2.0.0" xdg_directories: dependency: transitive description: @@ -933,6 +1085,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" + url: "https://pub.dev" + source: hosted + version: "6.6.1" yaml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c052993..540bafb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,8 +1,6 @@ -name: run_vision_pro +name: teste_projeto_turma description: "A new Flutter project." -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: 'none' # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 @@ -30,28 +28,20 @@ environment: dependencies: flutter: sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 + flutter_map: ^6.1.0 + latlong2: ^0.9.1 + google_maps_flutter: ^2.14.2 + geolocator: ^10.1.0 + flutter_blue_plus: ^1.31.0 + permission_handler: ^11.3.1 lottie: ^3.1.2 supabase_flutter: ^2.6.0 - google_maps_flutter: ^2.5.3 - geolocator: ^10.1.0 - flutter_polyline_points: ^2.0.0 - http: ^1.1.0 - permission_handler: ^11.0.1 dev_dependencies: flutter_test: sdk: flutter - - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^6.0.0 + flutter_lints: ^2.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/test/widget_test.dart b/test/widget_test.dart index 52f3d0c..c3b1442 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -8,23 +8,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:run_vision_pro/main.dart'; +import 'package:teste_projeto_turma/main.dart'; void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { + testWidgets('App smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. await tester.pumpWidget(const MyApp()); - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); + // Verify that app loads + expect(find.byType(MaterialApp), findsOneWidget); }); }