BLE Scan implemented
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 12s
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 12s
This commit is contained in:
@@ -1,128 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||||
import 'package:lasertag_app/l10n/app_localizations.dart';
|
||||
import 'constants.dart';
|
||||
|
||||
class LeaderSelectionScreen extends StatefulWidget {
|
||||
const LeaderSelectionScreen({super.key});
|
||||
|
||||
@override
|
||||
State<LeaderSelectionScreen> createState() => _LeaderSelectionScreenState();
|
||||
}
|
||||
|
||||
class _LeaderSelectionScreenState extends State<LeaderSelectionScreen> {
|
||||
// Map, um Geräte und ihren erkannten Typ zu speichern
|
||||
Map<BluetoothDevice, int> discoveredDevices = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_startDiscovery();
|
||||
}
|
||||
|
||||
void _startDiscovery() async {
|
||||
FlutterBluePlus.scanResults.listen((results) {
|
||||
for (ScanResult r in results) {
|
||||
// 1. Prüfen, ob Manufacturer Data für unsere Test-ID (0xFFFF / 65535) da ist
|
||||
final mfgData = r.advertisementData.manufacturerData[65535];
|
||||
|
||||
if (mfgData != null && mfgData.isNotEmpty) {
|
||||
int detectedType = mfgData[0]; // Das Typ-Byte vom nRF52
|
||||
|
||||
// 2. Nur hinzufügen, wenn es wirklich ein Leader (0x01) ist
|
||||
if (detectedType == LasertagUUIDs.typeLeader) {
|
||||
if (!discoveredDevices.containsKey(r.device)) {
|
||||
setState(() {
|
||||
discoveredDevices[r.device] = detectedType;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// 2. Warten, bis der Bluetooth-Adapter wirklich "on" ist
|
||||
await FlutterBluePlus.adapterState
|
||||
.where((s) => s == BluetoothAdapterState.on)
|
||||
.first;
|
||||
|
||||
// 3. Den Scan tatsächlich starten
|
||||
try {
|
||||
// Filtert nach der Service-UUID ...1000 aus ble_mgmt.c
|
||||
await FlutterBluePlus.startScan(
|
||||
withServices: [Guid(LasertagUUIDs.provService)],
|
||||
timeout: const Duration(seconds: 15),
|
||||
);
|
||||
} catch (e) {
|
||||
debugPrint("Scan-Fehler: $e");
|
||||
}
|
||||
} // <--- Hier endet _startDiscovery korrekt
|
||||
|
||||
// Hilfsfunktion zur Übersetzung des Typs
|
||||
String _getLocalizedTypeName(BuildContext context, int type) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
switch (type) {
|
||||
case LasertagUUIDs.typeLeader:
|
||||
return l10n.typeLeader;
|
||||
case LasertagUUIDs.typeWeapon:
|
||||
return l10n.typeWeapon;
|
||||
case LasertagUUIDs.typeVest:
|
||||
return l10n.typeVest;
|
||||
case LasertagUUIDs.typeBeacon:
|
||||
return l10n.typeBeacon;
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(l10n.selectLeader)),
|
||||
body: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(l10n.searching),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: discoveredDevices.entries.map((entry) {
|
||||
final device = entry.key;
|
||||
final type = entry.value;
|
||||
return ListTile(
|
||||
leading: Icon(
|
||||
Icons.hub,
|
||||
// Holt sich die Akzentfarbe direkt vom Betriebssystem via Theme-Context
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
size: 32,
|
||||
),
|
||||
title: Text(
|
||||
device.platformName.isEmpty
|
||||
? "Lasertag Device"
|
||||
: device.platformName,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
subtitle: Text(
|
||||
"${_getLocalizedTypeName(context, type)} (${device.remoteId})",
|
||||
// Nutzt eine dezentere Farbe für den Untertitel
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
onTap: () => _onLeaderSelected(device),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onLeaderSelected(BluetoothDevice device) {
|
||||
debugPrint("Leader ausgewählt: ${device.platformName}");
|
||||
// Hier öffnen wir als nächstes den Config-Dialog
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart'; // Neu
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:lasertag_app/l10n/app_localizations.dart';
|
||||
import 'ui/screens/device_selection_screen.dart';
|
||||
import 'providers/device_provider.dart'; // Neu
|
||||
|
||||
void main() {
|
||||
runApp(const LasertagApp());
|
||||
runApp(
|
||||
// Den Provider hier für die ganze App bereitstellen
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => DeviceProvider(),
|
||||
child: const LasertagApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class LasertagApp extends StatelessWidget {
|
||||
@@ -13,24 +21,13 @@ class LasertagApp extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Der Builder holt die Systemfarben (Accent Color)
|
||||
return DynamicColorBuilder(
|
||||
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
|
||||
// --- Lokalisierung ---
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: const [Locale('de')],
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
locale: const Locale('de'),
|
||||
|
||||
// --- Theme-Logik ---
|
||||
// Falls das OS Farben liefert, nutzen wir diese, sonst ein Standard-Blau
|
||||
theme: ThemeData(
|
||||
colorScheme: lightDynamic ?? ColorScheme.fromSeed(seedColor: Colors.blue),
|
||||
useMaterial3: true,
|
||||
@@ -42,9 +39,8 @@ class LasertagApp extends StatelessWidget {
|
||||
),
|
||||
useMaterial3: true,
|
||||
),
|
||||
themeMode: ThemeMode.system, // Folgt macOS/Android Hell/Dunkel Modus
|
||||
|
||||
home: DeviceSelectionScreen(),
|
||||
themeMode: ThemeMode.system,
|
||||
home: const DeviceSelectionScreen(),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,28 +1,39 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../models/device_model.dart';
|
||||
import '../../providers/device_provider.dart';
|
||||
import '../../constants.dart';
|
||||
import 'package:lasertag_app/l10n/app_localizations.dart';
|
||||
|
||||
class DeviceSelectionScreen extends StatelessWidget {
|
||||
class DeviceSelectionScreen extends StatefulWidget {
|
||||
const DeviceSelectionScreen({super.key});
|
||||
|
||||
@override
|
||||
State<DeviceSelectionScreen> createState() => _DeviceSelectionScreenState();
|
||||
}
|
||||
|
||||
class _DeviceSelectionScreenState extends State<DeviceSelectionScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Scan beim Starten genau einmal triggern
|
||||
// "listen: false" ist wichtig in initState
|
||||
Provider.of<DeviceProvider>(context, listen: false).startScan();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final provider = DeviceProvider(); // Später via "Provider"-Paket
|
||||
// Hier "lauscht" der Screen jetzt auf Änderungen im Provider
|
||||
final provider = context.watch<DeviceProvider>();
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
|
||||
provider.startScan(); // Scan starten beim Laden des Bildschirms
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(l10n.appTitle)),
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
// SEKTION 1: SPIEL-LEITER
|
||||
_buildHeader(context, l10n.listTypeLeader),
|
||||
_buildDeviceList(context, provider.leaders , isLeader: true),
|
||||
_buildDeviceList(context, provider.leaders, isLeader: true),
|
||||
|
||||
// SEKTION 2: AUSRÜSTUNG (Waffen, Westen, etc.)
|
||||
_buildHeader(context, l10n.listTypeEquipment),
|
||||
_buildDeviceList(context, provider.peripherals, isLeader: false),
|
||||
],
|
||||
@@ -64,7 +75,7 @@ class DeviceSelectionScreen extends StatelessWidget {
|
||||
device.name,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
subtitle: Text("ID: ${device.id}"),
|
||||
subtitle: Text("BTLE-ID: ${device.id}"),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
debugPrint("${device.name} ausgewählt für Konfiguration");
|
||||
|
||||
@@ -325,6 +325,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nested
|
||||
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -349,6 +357,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.3"
|
||||
provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: provider
|
||||
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5+1"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -14,6 +14,7 @@ dependencies:
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
intl: ^0.20.2
|
||||
provider: ^6.1.5+1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user