Compare commits
6 Commits
0069dba953
...
05f54e6a37
| Author | SHA1 | Date | |
|---|---|---|---|
| 05f54e6a37 | |||
| 85b00f6763 | |||
| eae4566182 | |||
| e20de5f992 | |||
| 67811db547 | |||
| edba3e33c6 |
@@ -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"/>
|
||||||
|
|||||||
169
lib/bluetooth_connection_screen.dart
Normal file
169
lib/bluetooth_connection_screen.dart
Normal 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
335
lib/bluetooth_screen.dart
Normal 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)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -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";
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user