Compare commits

...

6 Commits

Author SHA1 Message Date
05f54e6a37 Uso correto de app_colors.dart 2026-03-05 16:56:25 +00:00
85b00f6763 Merge remote-tracking branch 'origin/main' 2026-03-05 16:41:22 +00:00
eae4566182 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	android/app/src/main/AndroidManifest.xml
#	lib/main.dart
#	lib/screens/google_map_screen.dart
#	macos/Flutter/GeneratedPluginRegistrant.swift
#	pubspec.lock
#	pubspec.yaml
#	windows/flutter/generated_plugin_registrant.cc
#	windows/flutter/generated_plugins.cmake
2026-03-05 16:27:19 +00:00
e20de5f992 Mudança na tela bluetooth 2026-02-26 17:35:05 +00:00
67811db547 Carregar ficheiros para "lib" 2026-02-26 16:45:47 +00:00
edba3e33c6 Tela conexão bluetooth 2026-02-26 16:39:27 +00:00
7 changed files with 586 additions and 64 deletions

View File

@@ -1,14 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required for Bluetooth -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
@@ -27,6 +18,10 @@
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data <meta-data
android:name="io.flutter.embedding.android.NormalTheme" android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" android:resource="@style/NormalTheme"
@@ -36,13 +31,21 @@
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
<!-- TODO: Replace with your actual Google Maps API Key -->
<meta-data android:name="com.google.android.geo.API_KEY" <meta-data android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyCk84rxmF044cxKLABf55rEKHDqOcyoV5k"/> android:value="AIzaSyCk84rxmF044cxKLABf55rEKHDqOcyoV5k"/>
</application> </application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries> <queries>
<intent> <intent>
<action android:name="android.intent.action.PROCESS_TEXT"/> <action android:name="android.intent.action.PROCESS_TEXT"/>

View File

@@ -0,0 +1,169 @@
import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:permission_handler/permission_handler.dart';
import 'dart:async';
class BluetoothConnectionScreen extends StatefulWidget {
const BluetoothConnectionScreen({super.key});
@override
State<BluetoothConnectionScreen> createState() => _BluetoothConnectionScreenState();
}
class _BluetoothConnectionScreenState extends State<BluetoothConnectionScreen> {
List<ScanResult> scanResults = [];
bool isScanning = false;
StreamSubscription? scanSubscription;
StreamSubscription? isScanningSubscription;
@override
void initState() {
super.initState();
// Monitorar se o sistema está escaneando
isScanningSubscription = FlutterBluePlus.isScanning.listen((scanning) {
if (mounted) {
setState(() {
isScanning = scanning;
});
}
});
// Ouvir os resultados do scan globalmente
scanSubscription = FlutterBluePlus.scanResults.listen((results) {
if (mounted) {
setState(() {
scanResults = results;
});
}
});
}
@override
void dispose() {
scanSubscription?.cancel();
isScanningSubscription?.cancel();
super.dispose();
}
Future<void> startScan() async {
// 1. Pedir permissões (necessário no Android)
Map<Permission, PermissionStatus> statuses = await [
Permission.bluetoothScan,
Permission.bluetoothConnect,
Permission.location,
].request();
if (statuses[Permission.bluetoothScan]!.isGranted &&
statuses[Permission.bluetoothConnect]!.isGranted &&
statuses[Permission.location]!.isGranted) {
setState(() {
scanResults.clear();
});
// 2. Iniciar o scan
try {
await FlutterBluePlus.startScan(timeout: const Duration(seconds: 15));
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Erro ao buscar: $e')),
);
}
}
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Permissões negadas')),
);
}
}
}
Future<void> connectToDevice(BluetoothDevice device) async {
try {
await device.connect();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Conectado a ${device.platformName.isEmpty ? 'Dispositivo' : device.platformName}')),
);
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Falha ao conectar: $e')),
);
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Conexão Bluetooth'),
backgroundColor: Colors.grey[850],
foregroundColor: Colors.white,
),
backgroundColor: Colors.grey[900],
body: Column(
children: [
const SizedBox(height: 20),
Center(
child: Column(
children: [
const Icon(
Icons.bluetooth,
size: 80,
color: Colors.white,
),
const SizedBox(height: 10),
ElevatedButton.icon(
onPressed: isScanning ? null : startScan,
icon: isScanning
? const SizedBox(width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.black))
: const Icon(Icons.search),
label: Text(isScanning ? 'Buscando...' : 'Procurar Dispositivos'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
textStyle: const TextStyle(fontSize: 18),
),
),
],
),
),
const SizedBox(height: 20),
Expanded(
child: scanResults.isEmpty && !isScanning
? const Center(
child: Text(
"Nenhum dispositivo encontrado.\nClique em procurar.",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white70),
),
)
: ListView.builder(
itemCount: scanResults.length,
itemBuilder: (context, index) {
final device = scanResults[index].device;
final name = device.platformName.isEmpty ? 'Desconhecido' : device.platformName;
return Card(
color: Colors.white10,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: ListTile(
title: Text(name, style: const TextStyle(color: Colors.white)),
subtitle: Text(device.remoteId.toString(), style: const TextStyle(color: Colors.white70)),
trailing: const Icon(Icons.link, color: Colors.blue),
onTap: () => connectToDevice(device),
),
);
},
),
),
],
),
);
}
}

335
lib/bluetooth_screen.dart Normal file
View File

@@ -0,0 +1,335 @@
import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:permission_handler/permission_handler.dart';
import 'dart:async';
import 'constants/app_colors.dart';
import 'constants/app_strings.dart';
class BluetoothScreen extends StatefulWidget {
const BluetoothScreen({super.key});
@override
State<BluetoothScreen> createState() => _BluetoothScreenState();
}
class _BluetoothScreenState extends State<BluetoothScreen> {
List<ScanResult> _scanResults = [];
bool _isScanning = false;
StreamSubscription? _scanResultsSubscription;
StreamSubscription? _isScanningSubscription;
@override
void initState() {
super.initState();
_scanResultsSubscription = FlutterBluePlus.scanResults.listen((results) {
if (mounted) {
List<ScanResult> sortedResults = List.from(results);
sortedResults.sort((a, b) {
bool aHasName = _hasRealName(a);
bool bHasName = _hasRealName(b);
if (aHasName && !bHasName) return -1;
if (!aHasName && bHasName) return 1;
return 0;
});
setState(() {
_scanResults = sortedResults;
});
}
});
_isScanningSubscription = FlutterBluePlus.isScanning.listen((state) {
if (mounted) {
setState(() {
_isScanning = state;
});
}
});
}
bool _hasRealName(ScanResult r) {
return r.advertisementData.advName.isNotEmpty || r.device.platformName.isNotEmpty;
}
@override
void dispose() {
_scanResultsSubscription?.cancel();
_isScanningSubscription?.cancel();
super.dispose();
}
Future<void> startScan() async {
Map<Permission, PermissionStatus> statuses = await [
Permission.bluetoothScan,
Permission.bluetoothConnect,
Permission.location,
].request();
if (statuses[Permission.bluetoothScan]!.isGranted &&
statuses[Permission.bluetoothConnect]!.isGranted) {
try {
_scanResults.clear();
await FlutterBluePlus.startScan(
timeout: const Duration(seconds: 15),
androidUsesFineLocation: true,
continuousUpdates: true,
);
Future.delayed(const Duration(seconds: 8), () {
if (mounted && _scanResults.isEmpty && _isScanning) {
_showSnackBar(AppStrings.noDevicesFound, AppColors.coral);
}
});
} catch (e) {
_showSnackBar("${AppStrings.scanError}$e", AppColors.error);
}
} else {
_showSnackBar(AppStrings.permissionsDenied, AppColors.coral);
}
}
Future<void> stopScan() async {
try {
await FlutterBluePlus.stopScan();
} catch (e) {
_showSnackBar("${AppStrings.stopScanError}$e", AppColors.error);
}
}
Future<void> connectToDevice(BluetoothDevice device) async {
try {
String name = device.platformName.isNotEmpty ? device.platformName : AppStrings.defaultDeviceName;
_showSnackBar("${AppStrings.connectingTo}$name...", AppColors.white.withValues(alpha: 0.7));
await device.connect();
_showSnackBar(AppStrings.connectedSuccess, AppColors.success);
} catch (e) {
_showSnackBar("${AppStrings.connectFail}$e", AppColors.error);
}
}
String _getDeviceName(ScanResult r) {
if (r.advertisementData.advName.isNotEmpty) {
return r.advertisementData.advName;
} else if (r.device.platformName.isNotEmpty) {
return r.device.platformName;
} else {
String id = r.device.remoteId.toString();
return "${AppStrings.deviceIdPrefix}${id.length > 5 ? id.substring(id.length - 5) : id}]";
}
}
void _showSnackBar(String message, Color color) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: color,
duration: const Duration(seconds: 3),
behavior: SnackBarBehavior.floating,
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(AppStrings.bluetoothTitle, style: TextStyle(fontWeight: FontWeight.bold)),
centerTitle: true,
backgroundColor: AppColors.transparent,
elevation: 0,
foregroundColor: AppColors.white,
),
extendBodyBehindAppBar: true,
backgroundColor: AppColors.background,
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
AppColors.coral.withValues(alpha: 0.1),
AppColors.background,
],
),
),
child: Column(
children: [
const SizedBox(height: 100),
Center(
child: Column(
children: [
Container(
padding: const EdgeInsets.all(25),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _isScanning
? AppColors.coral.withValues(alpha: 0.1)
: AppColors.backgroundGrey.withValues(alpha: 0.3),
border: Border.all(
color: _isScanning ? AppColors.coral : AppColors.backgroundGrey,
width: 2,
),
),
child: Icon(
_isScanning ? Icons.bluetooth_searching : Icons.bluetooth,
size: 60,
color: _isScanning ? AppColors.coral : AppColors.white.withValues(alpha: 0.7),
),
),
const SizedBox(height: 30),
SizedBox(
width: 220,
height: 50,
child: ElevatedButton(
onPressed: _isScanning ? stopScan : startScan,
style: ElevatedButton.styleFrom(
backgroundColor: _isScanning ? AppColors.backgroundGrey : AppColors.white,
foregroundColor: _isScanning ? AppColors.white : AppColors.background,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
elevation: 0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (_isScanning)
const Padding(
padding: EdgeInsets.only(right: 10),
child: SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
color: AppColors.white,
),
),
),
Text(
_isScanning ? AppStrings.stopSearch : AppStrings.startSearch,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
letterSpacing: 1.2,
),
),
],
),
),
),
],
),
),
const SizedBox(height: 40),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Row(
children: [
Text(
_isScanning ? AppStrings.searching : AppStrings.nearbyDevices,
style: const TextStyle(
color: AppColors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
const Spacer(),
if (_isScanning)
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: AppColors.coral.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(10),
),
child: const Text(
AppStrings.active,
style: TextStyle(color: AppColors.coral, fontSize: 10, fontWeight: FontWeight.bold),
),
),
],
),
),
const SizedBox(height: 15),
Expanded(
child: _scanResults.isEmpty && !_isScanning
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.bluetooth_disabled, size: 40, color: AppColors.white.withValues(alpha: 0.1)),
const SizedBox(height: 16),
Text(
AppStrings.startSearchInstruction,
style: TextStyle(color: AppColors.white.withValues(alpha: 0.3)),
),
],
),
)
: ListView.builder(
padding: const EdgeInsets.only(top: 0, bottom: 20),
itemCount: _scanResults.length,
itemBuilder: (context, index) {
final r = _scanResults[index];
final name = _getDeviceName(r);
final isUnknown = name.startsWith(AppStrings.deviceIdPrefix);
return Container(
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 6),
decoration: BoxDecoration(
color: AppColors.backgroundGrey.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(15),
),
child: ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
leading: CircleAvatar(
backgroundColor: AppColors.backgroundGrey.withValues(alpha: 0.3),
child: Icon(
isUnknown ? Icons.devices_other : Icons.bluetooth,
color: isUnknown ? AppColors.white.withValues(alpha: 0.4) : AppColors.white,
size: 20
),
),
title: Text(
name,
style: TextStyle(
color: isUnknown ? AppColors.white.withValues(alpha: 0.5) : AppColors.white,
fontWeight: FontWeight.w600,
fontSize: 15,
),
),
subtitle: Text(
r.device.remoteId.toString(),
style: TextStyle(color: AppColors.white.withValues(alpha: 0.3), fontSize: 11),
),
trailing: ElevatedButton(
onPressed: () => connectToDevice(r.device),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.coral,
foregroundColor: AppColors.white,
padding: const EdgeInsets.symmetric(horizontal: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
elevation: 0,
),
child: const Text(AppStrings.connect, style: TextStyle(fontSize: 11, fontWeight: FontWeight.bold)),
),
),
);
},
),
),
],
),
),
);
}
}

View File

@@ -11,4 +11,10 @@ class AppColors {
// Neutral colors // Neutral colors
static const Color backgroundGrey = Color.fromARGB(255, 89, 89, 89); 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;
} }

View File

@@ -14,24 +14,33 @@ class AppStrings {
// Bluetooth Screen // Bluetooth Screen
static const String bluetoothTitle = "DISPOSITIVOS"; static const String bluetoothTitle = "DISPOSITIVOS";
static const String bluetoothConnect = "CONECTAR BLUETOOTH"; static const String bluetoothConnect = "CONECTAR BLUETOOTH";
static const String statusSearching = "STATUS: BUSCANDO..."; 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 statusReady = "STATUS: PRONTO";
static const String statusConnected = "STATUS: CONECTADO"; 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 foundDevices = "Encontrados";
static const String oneDevice = "1 Dispositivo"; static const String oneDevice = "1 Dispositivo";
static const String permissionDenied = "Permissões de Bluetooth negadas."; static const String permissionsDenied = "Permissões de Bluetooth negadas.";
static const String turnOnBluetooth = "Ligue o Bluetooth para buscar dispositivos."; static const String turnOnBluetooth = "Ligue o Bluetooth para buscar dispositivos.";
static const String scanError = "Erro ao iniciar scan: "; static const String scanError = "Erro ao iniciar scan: ";
static const String stopScanError = "Erro ao parar scan: "; static const String stopScanError = "Erro ao parar scan: ";
static const String defaultDeviceName = "Dispositivo";
static const String connectingTo = "Conectando a "; static const String connectingTo = "Conectando a ";
static const String connectionSuccess = "Conectado com sucesso!"; static const String connectedSuccess = "Conectado com sucesso!";
static const String connectionError = "Erro ao conectar: "; static const String connectFail = "Falha ao conectar: ";
static const String deviceIdPrefix = "Disp. [";
static const String unknownDevice = "Dispositivo Desconhecido"; static const String unknownDevice = "Dispositivo Desconhecido";
// Map Screen // Map Screen
static const String mapTitleTracking = "TRACKING ATIVO"; static const String mapTitleTracking = "TRACKING ATIVO";
static const String mapTitlePlanning = "PLANEJAR TRAJETO"; static const String mapTitlePlanning = "PLANEJAR TRAJETO";
static const String mapTitleRunning = "TOUHOU VIVA"; static const String mapTitleRunning = "CORRIDA";
static const String mapPace = "RITMO"; static const String mapPace = "RITMO";
static const String mapRoute = "TRAJETO"; static const String mapRoute = "TRAJETO";
static const String kmhUnit = "KM/H"; static const String kmhUnit = "KM/H";

View File

@@ -55,7 +55,7 @@ class _RunningScreenState extends State<RunningScreen>
shape: BoxShape.circle, shape: BoxShape.circle,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: AppColors.white.withOpacity(0.05), color: AppColors.white.withValues(alpha: 0.05),
blurRadius: 30, blurRadius: 30,
spreadRadius: 10, spreadRadius: 10,
), ),
@@ -74,7 +74,7 @@ class _RunningScreenState extends State<RunningScreen>
progress: value, progress: value,
strokeWidth: 14, strokeWidth: 14,
progressColor: AppColors.white, progressColor: AppColors.white,
backgroundColor: AppColors.white.withOpacity(0.15), backgroundColor: AppColors.white.withValues(alpha: 0.15),
), ),
); );
}, },
@@ -95,14 +95,14 @@ class _RunningScreenState extends State<RunningScreen>
style: const TextStyle( style: const TextStyle(
fontSize: 42, fontSize: 42,
fontWeight: FontWeight.w900, fontWeight: FontWeight.w900,
color: Colors.white, color: AppColors.white,
letterSpacing: -1, letterSpacing: -1,
), ),
), ),
Text( Text(
AppStrings.complete, AppStrings.complete,
style: TextStyle( style: TextStyle(
color: Colors.white.withOpacity(0.5), color: AppColors.white.withValues(alpha: 0.5),
fontSize: 11, fontSize: 11,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
letterSpacing: 1.5, letterSpacing: 1.5,
@@ -140,9 +140,9 @@ class _RunningScreenState extends State<RunningScreen>
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 22, vertical: 10), padding: const EdgeInsets.symmetric(horizontal: 22, vertical: 10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white.withOpacity(0.08), color: AppColors.white.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(25), borderRadius: BorderRadius.circular(25),
border: Border.all(color: Colors.white.withOpacity(0.1)), border: Border.all(color: AppColors.white.withValues(alpha: 0.1)),
), ),
child: Text( child: Text(
"${currentDistance.toStringAsFixed(1)} ${AppStrings.kmUnit} | ${targetDistance.toStringAsFixed(1)} ${AppStrings.kmUnit}", "${currentDistance.toStringAsFixed(1)} ${AppStrings.kmUnit} | ${targetDistance.toStringAsFixed(1)} ${AppStrings.kmUnit}",
@@ -169,7 +169,7 @@ class _RunningScreenState extends State<RunningScreen>
borderRadius: BorderRadius.circular(30), borderRadius: BorderRadius.circular(30),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.3), color: AppColors.black.withValues(alpha: 0.3),
blurRadius: 20, blurRadius: 20,
offset: const Offset(0, 10), offset: const Offset(0, 10),
), ),
@@ -186,9 +186,9 @@ class _RunningScreenState extends State<RunningScreen>
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
_buildStatItem(Icons.directions_run_rounded, "3219", AppStrings.steps), _buildStatItem(Icons.directions_run_rounded, "3219", AppStrings.steps),
Divider(color: Colors.white.withOpacity(0.1), height: 1), Divider(color: AppColors.white.withValues(alpha: 0.1), height: 1),
_buildStatItem(Icons.favorite_rounded, "98", AppStrings.bpm), _buildStatItem(Icons.favorite_rounded, "98", AppStrings.bpm),
Divider(color: Colors.white.withOpacity(0.1), height: 1), Divider(color: AppColors.white.withValues(alpha: 0.1), height: 1),
_buildStatItem(Icons.local_fire_department_rounded, "480", AppStrings.kcal), _buildStatItem(Icons.local_fire_department_rounded, "480", AppStrings.kcal),
], ],
), ),
@@ -222,8 +222,8 @@ class _RunningScreenState extends State<RunningScreen>
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
colors: [ colors: [
Colors.transparent, AppColors.transparent,
Colors.black.withOpacity(0.4), AppColors.black.withValues(alpha: 0.4),
], ],
), ),
), ),
@@ -234,10 +234,10 @@ class _RunningScreenState extends State<RunningScreen>
child: Container( child: Container(
padding: const EdgeInsets.all(6), padding: const EdgeInsets.all(6),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.background.withOpacity(0.8), color: AppColors.background.withValues(alpha: 0.8),
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: const Icon(Icons.fullscreen_rounded, color: Colors.white, size: 20), child: const Icon(Icons.fullscreen_rounded, color: AppColors.white, size: 20),
), ),
), ),
const Positioned( const Positioned(
@@ -246,7 +246,7 @@ class _RunningScreenState extends State<RunningScreen>
child: Text( child: Text(
AppStrings.mapPreview, AppStrings.mapPreview,
style: TextStyle( style: TextStyle(
color: Colors.white, color: AppColors.white,
fontSize: 10, fontSize: 10,
fontWeight: FontWeight.w900, fontWeight: FontWeight.w900,
letterSpacing: 1, letterSpacing: 1,
@@ -274,7 +274,7 @@ class _RunningScreenState extends State<RunningScreen>
child: LinearProgressIndicator( child: LinearProgressIndicator(
value: progress, value: progress,
minHeight: 8, minHeight: 8,
backgroundColor: Colors.white.withOpacity(0.1), backgroundColor: AppColors.white.withValues(alpha: 0.1),
valueColor: const AlwaysStoppedAnimation<Color>(AppColors.white), valueColor: const AlwaysStoppedAnimation<Color>(AppColors.white),
), ),
), ),
@@ -301,18 +301,18 @@ class _RunningScreenState extends State<RunningScreen>
Positioned( Positioned(
top: 60, top: 60,
right: 25, right: 25,
child: _buildSmallActionButton(Icons.bluetooth, Colors.red), child: _buildSmallActionButton(Icons.bluetooth, AppColors.error),
), ),
], ],
), ),
); );
} }
/// Item de estatística com design refinado. /// Item de estatística with design refinado.
Widget _buildStatItem(IconData icon, String value, String label) { Widget _buildStatItem(IconData icon, String value, String label) {
return Row( return Row(
children: [ children: [
Icon(icon, color: AppColors.coral.withOpacity(0.8), size: 22), Icon(icon, color: AppColors.coral.withValues(alpha: 0.8), size: 22),
const SizedBox(width: 12), const SizedBox(width: 12),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -321,7 +321,7 @@ class _RunningScreenState extends State<RunningScreen>
Text( Text(
value, value,
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: AppColors.white,
fontSize: 19, fontSize: 19,
fontWeight: FontWeight.w900, fontWeight: FontWeight.w900,
), ),
@@ -329,7 +329,7 @@ class _RunningScreenState extends State<RunningScreen>
Text( Text(
label, label,
style: TextStyle( style: TextStyle(
color: Colors.white.withOpacity(0.4), color: AppColors.white.withValues(alpha: 0.4),
fontSize: 9, fontSize: 9,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
letterSpacing: 0.5, letterSpacing: 0.5,
@@ -373,9 +373,9 @@ class _RunningScreenState extends State<RunningScreen>
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.backgroundGrey, color: AppColors.backgroundGrey,
borderRadius: BorderRadius.circular(18), borderRadius: BorderRadius.circular(18),
border: Border.all(color: Colors.white.withOpacity(0.05)), border: Border.all(color: AppColors.white.withValues(alpha: 0.05)),
), ),
child: Icon(icon, color: Colors.white.withOpacity(0.9), size: 24), child: Icon(icon, color: AppColors.white.withValues(alpha: 0.9), size: 24),
), ),
if (showBadge) if (showBadge)
Positioned( Positioned(
@@ -411,11 +411,11 @@ class _RunningScreenState extends State<RunningScreen>
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.backgroundGrey, color: AppColors.backgroundGrey,
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all(color: Colors.white.withOpacity(0.05)), border: Border.all(color: AppColors.white.withValues(alpha: 0.05)),
), ),
child: Stack( child: Stack(
children: [ children: [
Icon(icon, color: Colors.white, size: 20), Icon(icon, color: AppColors.white, size: 20),
Positioned( Positioned(
right: 0, right: 0,
top: 0, top: 0,
@@ -441,7 +441,7 @@ class MapPainter extends CustomPainter {
@override @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
final paintPath = Paint() final paintPath = Paint()
..color = AppColors.coral.withOpacity(0.5) ..color = AppColors.coral.withValues(alpha: 0.5)
..strokeWidth = 3 ..strokeWidth = 3
..style = PaintingStyle.stroke ..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round; ..strokeCap = StrokeCap.round;
@@ -452,7 +452,7 @@ class MapPainter extends CustomPainter {
path.quadraticBezierTo(size.width * 0.7, size.height * 0.1, size.width * 0.9, size.height * 0.3); path.quadraticBezierTo(size.width * 0.7, size.height * 0.1, size.width * 0.9, size.height * 0.3);
final paintRoad = Paint() final paintRoad = Paint()
..color = Colors.white.withOpacity(0.1) ..color = AppColors.white.withValues(alpha: 0.1)
..strokeWidth = 10 ..strokeWidth = 10
..style = PaintingStyle.stroke; ..style = PaintingStyle.stroke;
@@ -465,7 +465,7 @@ class MapPainter extends CustomPainter {
final markerPaint = Paint()..color = AppColors.coral; final markerPaint = Paint()..color = AppColors.coral;
canvas.drawCircle(Offset(size.width * 0.5, size.height * 0.5), 5, markerPaint); canvas.drawCircle(Offset(size.width * 0.5, size.height * 0.5), 5, markerPaint);
canvas.drawCircle(Offset(size.width * 0.5, size.height * 0.5), 8, Paint()..color = AppColors.coral.withOpacity(0.3)); canvas.drawCircle(Offset(size.width * 0.5, size.height * 0.5), 8, Paint()..color = AppColors.coral.withValues(alpha: 0.3));
} }
@override @override

View File

@@ -63,8 +63,8 @@ class _BluetoothConnectionScreenState extends State<BluetoothConnectionScreen> {
} else { } else {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( SnackBar(
content: Text(AppStrings.permissionDenied), content: Text(AppStrings.permissionsDenied),
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
), ),
); );
@@ -80,7 +80,7 @@ class _BluetoothConnectionScreenState extends State<BluetoothConnectionScreen> {
if (await FlutterBluePlus.adapterState.first != BluetoothAdapterState.on) { if (await FlutterBluePlus.adapterState.first != BluetoothAdapterState.on) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( SnackBar(
content: Text(AppStrings.turnOnBluetooth), content: Text(AppStrings.turnOnBluetooth),
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
), ),
@@ -135,9 +135,9 @@ class _BluetoothConnectionScreenState extends State<BluetoothConnectionScreen> {
}); });
FlutterBluePlus.stopScan(); FlutterBluePlus.stopScan();
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( SnackBar(
content: Text(AppStrings.connectionSuccess), content: Text(AppStrings.connectedSuccess),
backgroundColor: Colors.green, backgroundColor: AppColors.success,
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
), ),
); );
@@ -146,8 +146,8 @@ class _BluetoothConnectionScreenState extends State<BluetoothConnectionScreen> {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('${AppStrings.connectionError}$e'), content: Text('${AppStrings.connectFail}$e'),
backgroundColor: Colors.red, backgroundColor: AppColors.error,
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
), ),
); );
@@ -198,10 +198,10 @@ class _BluetoothConnectionScreenState extends State<BluetoothConnectionScreen> {
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.backgroundGrey, color: AppColors.backgroundGrey,
borderRadius: BorderRadius.circular(30), borderRadius: BorderRadius.circular(30),
border: Border.all(color: Colors.white.withOpacity(0.05)), border: Border.all(color: Colors.white.withValues(alpha: 0.05)),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.3), color: Colors.black.withValues(alpha: 0.3),
blurRadius: 20, blurRadius: 20,
offset: const Offset(0, 10), offset: const Offset(0, 10),
), ),
@@ -216,7 +216,7 @@ class _BluetoothConnectionScreenState extends State<BluetoothConnectionScreen> {
Text( Text(
_connectedDevice != null _connectedDevice != null
? AppStrings.statusConnected ? AppStrings.statusConnected
: (_isScanning ? AppStrings.statusSearching : AppStrings.statusReady), : (_isScanning ? AppStrings.searching : AppStrings.statusReady),
style: TextStyle( style: TextStyle(
color: _connectedDevice != null ? Colors.greenAccent : (_isScanning ? AppColors.coral : Colors.white54), color: _connectedDevice != null ? Colors.greenAccent : (_isScanning ? AppColors.coral : Colors.white54),
fontSize: 10, fontSize: 10,
@@ -243,10 +243,10 @@ class _BluetoothConnectionScreenState extends State<BluetoothConnectionScreen> {
child: Container( child: Container(
padding: const EdgeInsets.all(15), padding: const EdgeInsets.all(15),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _isScanning ? Colors.red.withOpacity(0.1) : AppColors.coral.withOpacity(0.1), color: _isScanning ? Colors.red.withValues(alpha: 0.1) : AppColors.coral.withValues(alpha: 0.1),
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all( border: Border.all(
color: _isScanning ? Colors.red.withOpacity(0.5) : AppColors.coral.withOpacity(0.5), color: _isScanning ? Colors.red.withValues(alpha: 0.5) : AppColors.coral.withValues(alpha: 0.5),
width: 2, width: 2,
), ),
), ),
@@ -263,10 +263,10 @@ class _BluetoothConnectionScreenState extends State<BluetoothConnectionScreen> {
child: Container( child: Container(
padding: const EdgeInsets.all(15), padding: const EdgeInsets.all(15),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.red.withOpacity(0.1), color: Colors.red.withValues(alpha: 0.1),
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all( border: Border.all(
color: Colors.red.withOpacity(0.5), color: Colors.red.withValues(alpha: 0.5),
width: 2, width: 2,
), ),
), ),
@@ -297,21 +297,21 @@ class _BluetoothConnectionScreenState extends State<BluetoothConnectionScreen> {
child: Container( child: Container(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.backgroundGrey.withOpacity(0.4), color: AppColors.backgroundGrey.withValues(alpha: 0.4),
borderRadius: BorderRadius.circular(25), borderRadius: BorderRadius.circular(25),
border: Border.all(color: Colors.white.withOpacity(0.03)), border: Border.all(color: Colors.white.withValues(alpha: 0.03)),
), ),
child: Row( child: Row(
children: [ children: [
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.background.withOpacity(0.8), color: AppColors.background.withValues(alpha: 0.8),
borderRadius: BorderRadius.circular(15), borderRadius: BorderRadius.circular(15),
), ),
child: Icon( child: Icon(
Icons.bluetooth_audio_rounded, Icons.bluetooth_audio_rounded,
color: _connectedDevice != null ? Colors.greenAccent : AppColors.coral.withOpacity(0.8), color: _connectedDevice != null ? Colors.greenAccent : AppColors.coral.withValues(alpha: 0.8),
size: 24 size: 24
), ),
), ),
@@ -357,13 +357,13 @@ class _BluetoothConnectionScreenState extends State<BluetoothConnectionScreen> {
], ],
), ),
if (_isScanning) if (_isScanning)
Positioned( const Positioned(
bottom: 0, bottom: 0,
left: 0, left: 0,
right: 0, right: 0,
child: LinearProgressIndicator( child: LinearProgressIndicator(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
valueColor: const AlwaysStoppedAnimation<Color>(AppColors.coral), valueColor: AlwaysStoppedAnimation<Color>(AppColors.coral),
minHeight: 2, minHeight: 2,
), ),
), ),