import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../app/error_message.dart'; import '../app/load_state.dart'; import '../models/ignis_device.dart'; import '../providers/providers.dart'; import '../widgets/load_error_view.dart'; /// Экран создания новой группы ламп. /// Загружает список устройств, позволяет выбрать нужные. class GroupEditScreen extends ConsumerStatefulWidget { const GroupEditScreen({super.key}); @override ConsumerState createState() => _GroupEditScreenState(); } class _GroupEditScreenState extends ConsumerState { final _idCtrl = TextEditingController(); final _nameCtrl = TextEditingController(); final Set _selectedMacs = {}; bool _saving = false; bool _rescanning = false; @override void initState() { super.initState(); _loadDevices(); } @override void dispose() { _idCtrl.dispose(); _nameCtrl.dispose(); super.dispose(); } Future _loadDevices() async { await ref.read(devicesProvider.notifier).load(); } /// Пересканировать сеть и перезагрузить устройства Future _rescan() async { setState(() => _rescanning = true); try { await ref.read(apiProvider).rescanNetwork(); // Подождать немного -- сканирование асинхронное await Future.delayed(const Duration(seconds: 3)); await ref.read(devicesProvider.notifier).load(); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Ошибка сканирования: ${describeLoadError(e)}'), ), ); } } finally { if (mounted) setState(() => _rescanning = false); } } @override Widget build(BuildContext context) { final devicesState = ref.watch(devicesProvider); final devices = devicesState.data; return Scaffold( appBar: AppBar( title: const Text('НОВАЯ ГРУППА'), actions: [ // Кнопка ресканирования сети IconButton( icon: _rescanning ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.refresh), tooltip: 'Пересканировать сеть', onPressed: _rescanning ? null : _rescan, ), ], ), body: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // ID группы TextField( controller: _idCtrl, decoration: const InputDecoration( labelText: 'ID группы (например "bedroom")', prefixIcon: Icon(Icons.tag), ), ), const SizedBox(height: 12), // Название группы TextField( controller: _nameCtrl, decoration: const InputDecoration( labelText: 'Название (например "Спальня")', prefixIcon: Icon(Icons.label), ), textCapitalization: TextCapitalization.sentences, ), const SizedBox(height: 16), // Заголовок списка устройств Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Устройства (${_selectedMacs.length} выбрано)', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white70, ), ), TextButton( onPressed: devices.isEmpty ? null : () { setState(() { if (_selectedMacs.length == devices.length) { _selectedMacs.clear(); } else { for (final d in devices) { _selectedMacs.add(d.groupMemberId); } } }); }, child: Text( _selectedMacs.length == devices.length ? 'Снять все' : 'Выбрать все', style: const TextStyle(fontSize: 12), ), ), ], ), // Список устройств Expanded(child: _buildDevices(devicesState, devices)), // Кнопка сохранения Padding( padding: EdgeInsets.only( bottom: MediaQuery.of(context).padding.bottom + 8, ), child: SizedBox( width: double.infinity, height: 48, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.deepOrange, foregroundColor: Colors.white, ), onPressed: _saving ? null : _save, child: _saving ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Text('СОЗДАТЬ ГРУППУ'), ), ), ), ], ), ), ); } Widget _buildDevices( LoadState> devicesState, List devices, ) { if ((devicesState.isIdle || devicesState.isLoading) && devices.isEmpty) { return const Center( child: CircularProgressIndicator(color: Colors.deepOrange), ); } if (devicesState.hasError && devices.isEmpty) { return LoadErrorView( title: 'Не удалось загрузить устройства', message: devicesState.errorMessage, icon: Icons.lightbulb_outline, onRetry: _loadDevices, ); } if (devices.isEmpty) { return const Center( child: Text( 'Устройства не найдены.\nПопробуйте пересканировать сеть.', textAlign: TextAlign.center, style: TextStyle(color: Colors.white38), ), ); } final hasStatusHeader = devicesState.isLoading || devicesState.hasError; final statusHeaderCount = hasStatusHeader ? 1 : 0; return ListView.builder( itemCount: devices.length + statusHeaderCount, itemBuilder: (context, index) { if (hasStatusHeader && index == 0) { if (devicesState.isLoading) { return const Padding( padding: EdgeInsets.only(bottom: 12), child: LinearProgressIndicator(color: Colors.deepOrange), ); } return LoadErrorBanner( title: 'Не удалось обновить устройства', message: devicesState.errorMessage, onRetry: _loadDevices, ); } final deviceIndex = index - statusHeaderCount; final d = devices[deviceIndex]; final selected = _selectedMacs.contains(d.groupMemberId); return CheckboxListTile( value: selected, activeColor: Colors.deepOrange, title: Text(d.name), subtitle: d.subtitle == null ? null : Text( d.subtitle!, style: const TextStyle(fontSize: 11, color: Colors.white38), ), onChanged: (v) { setState(() { if (v == true) { _selectedMacs.add(d.groupMemberId); } else { _selectedMacs.remove(d.groupMemberId); } }); }, ); }, ); } Future _save() async { final id = _idCtrl.text.trim(); final name = _nameCtrl.text.trim(); if (id.isEmpty || name.isEmpty) { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('Укажите ID и название'))); return; } if (_selectedMacs.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Выберите хотя бы одно устройство')), ); return; } setState(() => _saving = true); try { await ref.read(apiProvider).createGroup(id, name, _selectedMacs.toList()); // Обновить список групп await ref.read(groupsProvider.notifier).refresh(); if (mounted) Navigator.of(context).pop(); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Ошибка создания: ${describeLoadError(e)}')), ); } } if (mounted) setState(() => _saving = false); } }