Carregar ficheiros para "lib"

This commit is contained in:
2026-02-26 16:45:47 +00:00
parent edba3e33c6
commit 67811db547
2 changed files with 352 additions and 0 deletions

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),
),
);
},
),
),
],
),
);
}
}

183
lib/bluetooth_screen.dart Normal file
View File

@@ -0,0 +1,183 @@
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 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) {
setState(() {
_scanResults = results;
});
}
});
_isScanningSubscription = FlutterBluePlus.isScanning.listen((state) {
if (mounted) {
setState(() {
_isScanning = state;
});
}
});
}
@override
void dispose() {
_scanResultsSubscription?.cancel();
_isScanningSubscription?.cancel();
super.dispose();
}
Future<void> startScan() async {
// 1. Pedir permissões
Map<Permission, PermissionStatus> statuses = await [
Permission.bluetoothScan,
Permission.bluetoothConnect,
Permission.location,
].request();
if (statuses[Permission.bluetoothScan]!.isGranted &&
statuses[Permission.bluetoothConnect]!.isGranted) {
try {
// 2. Iniciar Scan
await FlutterBluePlus.startScan(
timeout: const Duration(seconds: 15),
);
// Se não encontrar nada (ex: no emulador), avisa o usuário
Future.delayed(const Duration(seconds: 5), () {
if (mounted && _scanResults.isEmpty && _isScanning) {
_showSnackBar("Nenhum dispositivo encontrado (Verifique se o Bluetooth/GPS estão ligados)", Colors.orange);
}
});
} catch (e) {
_showSnackBar("Erro ao iniciar busca: $e", Colors.red);
}
} else {
_showSnackBar("Permissões de Bluetooth negadas", Colors.orange);
}
}
Future<void> connectToDevice(BluetoothDevice device) async {
try {
String name = device.platformName.isEmpty ? 'Dispositivo' : device.platformName;
_showSnackBar("Conectando a $name...", Colors.blue);
await device.connect();
_showSnackBar("Conectado com sucesso!", Colors.green);
} catch (e) {
_showSnackBar("Falha ao conectar: $e", Colors.red);
}
}
void _showSnackBar(String message, Color color) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: color,
duration: const Duration(seconds: 3),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Conexão Bluetooth'),
backgroundColor: Colors.black87,
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.blue),
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: 24, vertical: 12),
),
),
],
),
),
const SizedBox(height: 20),
Expanded(
child: _scanResults.isEmpty && !_isScanning
? const Center(
child: Text(
"Clique em Procurar para encontrar dispositivos.\n(Nota: Requer telemóvel físico para Bluetooth real)",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white54),
),
)
: ListView.builder(
itemCount: _scanResults.length,
itemBuilder: (context, index) {
final r = _scanResults[index];
final name = r.device.platformName.isEmpty
? "Dispositivo Desconhecido"
: r.device.platformName;
return Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
color: Colors.white10,
child: ListTile(
leading: const Icon(Icons.bluetooth, color: Colors.blue),
title: Text(
name,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
subtitle: Text(
r.device.remoteId.toString(),
style: const TextStyle(color: Colors.white70),
),
trailing: ElevatedButton(
onPressed: () => connectToDevice(r.device),
child: const Text("Conectar"),
),
),
);
},
),
),
],
),
);
}
}