import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../data/sources/ble/ble_provisioner.dart'; // Shared bottom sheet used by onboarding and the main sensor screens. // Flow: scan → select device → enter WiFi credentials → provision → place on map. class BleProvisionSheet extends ConsumerStatefulWidget { const BleProvisionSheet({super.key}); @override ConsumerState createState() => _BleProvisionSheetState(); } class _BleProvisionSheetState extends ConsumerState { final _provisioner = BleProvisioner(); final _ssidController = TextEditingController(); final _wifiPasswordController = TextEditingController(); BleScanResult? _selected; bool _provisioning = false; String? _error; @override void dispose() { _provisioner.dispose(); _ssidController.dispose(); _wifiPasswordController.dispose(); super.dispose(); } Future _provision() async { if (_selected == null) return; setState(() { _provisioning = true; _error = null; }); try { await _provisioner.provision( _selected!.deviceId, ssid: _ssidController.text.trim(), wifiPassword: _wifiPasswordController.text, ); // TODO: poll localiserd until sensor appears, then prompt placement on map. if (mounted) Navigator.of(context).pop(); } catch (e) { setState(() => _error = e.toString()); } finally { if (mounted) setState(() => _provisioning = false); } } @override Widget build(BuildContext context) { return DraggableScrollableSheet( expand: false, initialChildSize: 0.6, maxChildSize: 0.9, builder: (context, scrollController) => Padding( padding: EdgeInsets.fromLTRB( 24, 16, 24, MediaQuery.of(context).viewInsets.bottom + 24, ), child: ListView( controller: scrollController, children: [ Text('Add sensor', style: Theme.of(context).textTheme.titleLarge), const SizedBox(height: 16), // Scan results // TODO: StreamBuilder on _provisioner.scan() — show a list of // BleScanResult tiles; tapping one sets _selected. const Text('Nearby ESP32 devices'), const SizedBox(height: 8), const Placeholder(fallbackHeight: 120), const SizedBox(height: 24), if (_selected != null) ...[ Text('Selected: ${_selected!.name}'), const SizedBox(height: 16), TextField( controller: _ssidController, decoration: const InputDecoration( labelText: 'WiFi SSID', border: OutlineInputBorder(), ), ), const SizedBox(height: 12), TextField( controller: _wifiPasswordController, decoration: const InputDecoration( labelText: 'WiFi password', border: OutlineInputBorder(), ), obscureText: true, ), const SizedBox(height: 16), ], if (_error != null) Padding( padding: const EdgeInsets.only(bottom: 12), child: Text(_error!, style: TextStyle( color: Theme.of(context).colorScheme.error)), ), FilledButton( onPressed: (_selected == null || _provisioning) ? null : _provision, child: _provisioning ? const SizedBox.square( dimension: 20, child: CircularProgressIndicator(strokeWidth: 2)) : const Text('Provision & add'), ), ], ), ), ); } }