import 'package:abawo_bt_app/controller/bluetooth.dart'; import 'package:abawo_bt_app/util/constants.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:abawo_bt_app/widgets/device_listitem.dart'; import 'package:abawo_bt_app/widgets/scanning_animation.dart'; // Import the new animation widget const Duration _scanDuration = Duration(seconds: 10); class ConnectDevicePage extends ConsumerStatefulWidget { const ConnectDevicePage({super.key}); @override ConsumerState createState() => _ConnectDevicePageState(); } class _ConnectDevicePageState extends ConsumerState with TickerProviderStateMixin { // Use TickerProviderStateMixin for multiple controllers if needed later, good practice bool _initialScanStarted = false; bool _showOnlyAbawoDevices = true; // State for filtering devices late AnimationController _progressController; // Controller for scan duration progress late AnimationController _waveAnimationController; // Controller for wave animation // Function to start scan safely after controller is ready void _startScanIfNeeded(BluetoothController controller) { // Use WidgetsBinding to schedule the scan start after the build phase WidgetsBinding.instance.addPostFrameCallback((_) { if (!_initialScanStarted && mounted) { controller.startScan(timeout: _scanDuration); _startScanProgressAnimation(); // Start scan duration progress animation _startWaveAnimation(); // Start the wave animation if (mounted) { setState(() { _initialScanStarted = true; }); } } }); } @override void initState() { super.initState(); // Initialize scan progress controller _progressController = AnimationController( vsync: this, duration: _scanDuration, )..addListener(() { // Trigger rebuild when animation value changes for the progress indicator if (mounted) { setState(() {}); } }); // Initialize wave animation controller _waveAnimationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 1500), // New duration: 1.5 seconds )..repeat(); // Make the wave animation repeat } @override void dispose() { // Stop animations before disposing _progressController.stop(); _waveAnimationController.stop(); _progressController.dispose(); _waveAnimationController.dispose(); super.dispose(); } // Helper method to start/reset scan progress animation void _startScanProgressAnimation() { if (mounted) { _progressController.reset(); _progressController.forward().whenCompleteOrCancel(() { // Optional: Add logic if needed when scan progress completes or cancels }); } } // Helper method to start the wave animation void _startWaveAnimation() { if (mounted && !_waveAnimationController.isAnimating) { _waveAnimationController.reset(); _waveAnimationController.repeat(); } } // Helper method to stop animations when scan finishes/cancels void _stopAnimations() { if (mounted) { if (_progressController.isAnimating) { _progressController.stop(); } if (_waveAnimationController.isAnimating) { _waveAnimationController.stop(); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Connect Device'), leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: () => context.go('/'), ), actions: [ Padding( padding: const EdgeInsets.only(right: 12.0), child: Row( children: [ const Text('abawo only'), // Label for the switch Switch( value: _showOnlyAbawoDevices, onChanged: (value) { if (mounted) { setState(() { _showOnlyAbawoDevices = value; }); } }, ), ], ), ) ], ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'Available Devices', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), const SizedBox(height: 20), // Use Consumer to react to bluetoothProvider changes Expanded( // Allow the Consumer content to expand child: Consumer(builder: (context, ref, child) { final btAsyncValue = ref.watch(bluetoothProvider); return btAsyncValue.when( loading: () => const Center( child: CircularProgressIndicator()), // Center loading indicator error: (err, stack) => Center( child: Text( 'Error loading Bluetooth: $err')), // Center error data: (controller) { // Start the initial scan once the controller is ready // Start initial scan and animation _startScanIfNeeded(controller); // Use StreamBuilder to watch the scanning state return StreamBuilder( stream: FlutterBluePlus.isScanning, initialData: false, // Default to not scanning before check builder: (context, snapshot) { final isScanning = snapshot.data ?? false; if (isScanning && _initialScanStarted) { _startWaveAnimation(); // Ensure wave animation is running // Show the new scanning wave animation with the progress indicator value return Center( // Pass the wave animation controller. The progress value will be added // to the ScanningWaveAnimation widget itself later. child: ScanningWaveAnimation( animation: _waveAnimationController, progressValue: _progressController .value, // Pass the scan progress ), ); } else if (!_initialScanStarted) { // Show placeholder or button to start initial scan if needed, or just empty space return const SizedBox( height: 50); // Placeholder before scan starts } else { // Scan finished, stop animations and show results _stopAnimations(); final results = controller.scanResults; // Filter results based on the toggle state final filteredResults = _showOnlyAbawoDevices ? results .where((device) => device .advertisementData.serviceUuids .contains(Guid(abawoServiceBtUUID))) .toList() : results; // Use Column + Expanded for ListView + Button layout return Column( children: [ Expanded( // Allow ListView to take available space child: filteredResults .isEmpty // Use filtered list check ? const Center( child: Text( 'No devices found.')) // Center empty text : ListView.builder( itemCount: filteredResults .length, // Use filtered list length itemBuilder: (context, index) { final device = filteredResults[ index]; // Use filtered list final isAbawoDevice = device .advertisementData.serviceUuids .contains( Guid(abawoServiceBtUUID)); final deviceName = device.device.advName.isEmpty ? 'Unknown Device' : device.device.advName; // Use the custom DeviceListItem widget return InkWell( // Wrap with InkWell for tap feedback onTap: () { if (!isAbawoDevice) { // Show a snackbar for non-Abawo devices ScaffoldMessenger.of(context) .showSnackBar(const SnackBar( content: Text( 'This app can only connect to abawo devices.'))); return; } // TODO: Implement connect logic // controller.connectToDevice(device.device); // Pass the BluetoothDevice // context.go('/control/${device.device.remoteId.str}'); print( 'Tapped on ${device.device.remoteId.str}'); }, child: DeviceListItem( deviceName: deviceName, deviceId: device.device.remoteId.str, isUnknownDevice: device.device.advName.isEmpty, ), ); } // End of itemBuilder ), ), Padding( // Add padding around the button padding: const EdgeInsets.all(16.0), child: ElevatedButton( onPressed: () { // Retry scan by calling startScan on the controller // Ensure _initialScanStarted is true so indicator shows if (mounted) { setState(() { _initialScanStarted = true; }); } controller.startScan( timeout: _scanDuration); _startScanProgressAnimation(); // Restart scan progress animation _startWaveAnimation(); // Ensure wave animation runs on retry }, child: const Text('Retry Scan'), ), ), ], ); } }, ); }, ); }), ), ], ), ), ); } }