feat: working connection, conn setting, and gear ratio setting for universal shifters

This commit is contained in:
2026-02-22 23:05:12 +01:00
parent f92d6d04f5
commit dcb1e6596e
93 changed files with 10538 additions and 668 deletions

View File

@ -1,4 +1,9 @@
import 'package:abawo_bt_app/controller/bluetooth.dart';
import 'package:abawo_bt_app/database/database.dart';
import 'package:abawo_bt_app/model/bluetooth_device_model.dart';
import 'package:abawo_bt_app/widgets/device_listitem.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
class HomePage extends StatelessWidget {
@ -53,10 +58,7 @@ class HomePage extends StatelessWidget {
borderRadius: BorderRadius.circular(8),
),
child: const Center(
child: Text(
'No devices connected yet',
style: TextStyle(color: Colors.grey),
),
child: DevicesList(),
),
),
],
@ -73,3 +75,148 @@ class HomePage extends StatelessWidget {
);
}
}
class DevicesList extends ConsumerStatefulWidget {
const DevicesList({super.key});
@override
ConsumerState<DevicesList> createState() => _DevicesListState();
}
class _DevicesListState extends ConsumerState<DevicesList> {
String? _connectingDeviceId; // ID of device currently being connected
Future<void> _removeDevice(ConnectedDevice device) async {
final shouldRemove = await showDialog<bool>(
context: context,
builder: (dialogContext) => AlertDialog(
title: const Text('Remove device?'),
content:
Text('Do you want to remove ${device.deviceName} from the app?'),
actions: [
TextButton(
onPressed: () => Navigator.of(dialogContext).pop(false),
child: const Text('Cancel'),
),
FilledButton(
onPressed: () => Navigator.of(dialogContext).pop(true),
child: const Text('Remove'),
),
],
),
);
if (shouldRemove != true || !mounted) {
return;
}
final result = await ref
.read(nConnectedDevicesProvider.notifier)
.deleteConnectedDevice(device.id);
if (!mounted) {
return;
}
if (result.isErr()) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to remove device: ${result.unwrapErr()}')),
);
return;
}
if (_connectingDeviceId == device.deviceAddress) {
setState(() {
_connectingDeviceId = null;
});
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('${device.deviceName} removed from the app.')),
);
}
@override
Widget build(BuildContext context) {
final asyncDevices = ref.watch(nConnectedDevicesProvider);
return asyncDevices.when(
loading: () => const Center(
child: CircularProgressIndicator(),
),
error: (error, stackTrace) => Center(
child: Text(
'Error loading devices: ${error.toString()}',
style: TextStyle(color: Colors.red),
textAlign: TextAlign.center,
),
),
data: (devices) {
if (devices.isEmpty) {
return const Center(
child: Text(
'No devices connected yet',
style: TextStyle(color: Colors.grey),
),
);
}
return ListView.builder(
itemCount: devices.length,
shrinkWrap: true,
itemBuilder: (context, index) {
final device = devices[index];
return InkWell(
onTap: () async {
if (_connectingDeviceId != null) return;
setState(() {
_connectingDeviceId = device.deviceAddress;
});
try {
final controller = await ref.read(bluetoothProvider.future);
final result = await controller.connectById(
device.deviceAddress,
timeout: const Duration(seconds: 10),
);
if (result.isOk()) {
context.go('/device/${device.deviceAddress}');
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Connection failed. Is the device turned on and in range?'),
duration: const Duration(seconds: 3),
),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: ${e.toString()}')),
);
} finally {
setState(() {
_connectingDeviceId = null;
});
}
},
child: DeviceListItem(
deviceName: device.deviceName,
deviceId: device.deviceAddress,
type: deviceTypeFromString(device.deviceType),
isConnecting: device.deviceAddress == _connectingDeviceId,
trailing: IconButton(
tooltip: 'Remove device',
icon: const Icon(Icons.delete_outline),
onPressed: () => _removeDevice(device),
),
),
);
},
);
},
);
}
}