import 'package:drift/drift.dart' show Value; import 'package:flutter/material.dart'; import '../database/database.dart' as db; // Import database with prefix 'db' import '../utils/category_utils.dart'; // Import CategoryUtils class AddCategoryDialog extends StatefulWidget { final db.AppDatabase database; const AddCategoryDialog({Key? key, required this.database}) : super(key: key); @override State createState() => _AddCategoryDialogState(); } class _AddCategoryDialogState extends State { final _formKey = GlobalKey(); final _nameController = TextEditingController(); // State for selected icon String _selectedIconName = 'label_outline'; // Default icon name (string) IconData _selectedIconData = Icons.label_outline; // Default icon data // State for selected color Color _selectedColor = CategoryUtils.availableColors[9]; // Default to green @override void initState() { super.initState(); // Initialize icon data based on the default name _selectedIconData = CategoryUtils.getIconFromString(_selectedIconName); } @override void dispose() { _nameController.dispose(); super.dispose(); } // --- Function to show the icon picker dialog --- Future _showIconPicker() async { final Map availableIcons = CategoryUtils.getAvailableIcons(); final List iconNames = availableIcons.keys.toList(); final List iconDatas = availableIcons.values.toList(); print("Number of available icons: ${availableIcons.length}"); // Debug print final String? chosenIconName = await showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('Выберите иконку'), contentPadding: const EdgeInsets.all(10.0), // Adjust padding content: SizedBox( // Constrain the size of the dialog content width: double.maxFinite, // Use maximum width available height: 300, // <--- ЗАДАЕМ ВЫСОТУ ДЛЯ ОБЛАСТИ ПРОКРУТКИ child: GridView.builder( // shrinkWrap: true, // <--- УБИРАЕМ SHRINKWRAP itemCount: iconNames.length, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 5, // Adjust number of columns crossAxisSpacing: 10.0, mainAxisSpacing: 10.0, ), itemBuilder: (context, index) { final bool isSelected = _selectedIconName == iconNames[index]; return InkWell( onTap: () { Navigator.pop(context, iconNames[index]); // Return the selected icon name (String) }, borderRadius: BorderRadius.circular(8.0), // Ripple effect matches border child: Container( decoration: BoxDecoration( border: Border.all( color: isSelected ? Theme.of(context).colorScheme.primary // Highlight selected : Colors.grey.withOpacity(0.3), // Subtle border for all width: isSelected ? 2.0 : 1.0, // Thicker border if selected ), borderRadius: BorderRadius.circular(8.0), color: isSelected ? Theme.of(context).colorScheme.primary.withOpacity(0.1) : null, ), child: Icon( iconDatas[index], size: 30.0, // Adjust icon size color: Theme.of(context).brightness == Brightness.dark ? Colors.white70 : Colors.black87, ), ), ); }, ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context, null), // Close without selection child: const Text('Отмена'), ), ], ); }, ); // Update state if an icon was chosen if (chosenIconName != null) { setState(() { _selectedIconName = chosenIconName; _selectedIconData = CategoryUtils.getIconFromString(chosenIconName); }); } } // --- End of Icon Picker Function --- Future _saveCategory() async { if (_formKey.currentState!.validate()) { final name = _nameController.text; // Use the selected icon name final icon = _selectedIconName; // Use the selected color value final color = _selectedColor.value; final newCategoryCompanion = db.CategoriesCompanion( name: Value(name), icon: Value(icon), // Use selected icon name color: Value(color), // Use selected color value ); try { // TODO: Add check for duplicate category name before inserting (more robustly) final newCategoryId = await widget.database.addCategory(newCategoryCompanion); final newCategory = await widget.database.getCategoryById(newCategoryId); if (mounted) { Navigator.pop(context, newCategory); // Return the newly created CategoryDb object } } catch (e) { print('Error adding category: $e'); // Handle potential duplicate name error from database (depends on DB constraints) String errorMessage = 'Произошла ошибка при добавлении категории.'; if (e.toString().toLowerCase().contains('unique constraint failed')) { errorMessage = 'Категория с таким именем уже существует.'; } if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(errorMessage), backgroundColor: Colors.red, ), ); } } } } @override Widget build(BuildContext context) { final theme = Theme.of(context); // Determine icon color based on the selected background color's brightness final iconColor = ThemeData.estimateBrightnessForColor(_selectedColor) == Brightness.dark ? Colors.white70 : Colors.black87; return AlertDialog( title: const Text('Создать категорию'), content: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, // Align children to the start children: [ // --- Icon and Name Row --- Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ // --- Icon Picker --- Padding( padding: const EdgeInsets.only(right: 16.0, bottom: 10.0), // Add padding child: InkWell( onTap: _showIconPicker, // Show picker on tap customBorder: const CircleBorder(), // Make ripple circular child: CircleAvatar( radius: 24, backgroundColor: _selectedColor, // Use selected color for background child: Icon( _selectedIconData, // Display selected icon color: iconColor, // Adjust icon color based on background size: 26, ), ), ), ), // --- Name Field --- Expanded( child: TextFormField( controller: _nameController, decoration: const InputDecoration( labelText: 'Название категории', ), validator: (value) { if (value == null || value.isEmpty) { return 'Пожалуйста, введите название'; } // Basic length check if (value.length > 50) { return 'Название слишком длинное'; } return null; }, textCapitalization: TextCapitalization.sentences, ), ), ], ), const SizedBox(height: 20), // Space before color picker // --- Color Picker --- const Text('Выберите цвет:', style: TextStyle(fontSize: 16)), const SizedBox(height: 8), Wrap( // Use Wrap for horizontal layout with wrapping spacing: 8.0, // Horizontal space between circles runSpacing: 8.0, // Vertical space between rows children: CategoryUtils.availableColors.map((color) { final bool isSelected = _selectedColor == color; return InkWell( onTap: () { setState(() { _selectedColor = color; // Update selected color on tap }); }, customBorder: const CircleBorder(), child: Container( width: 32, // Size of the color circle height: 32, decoration: BoxDecoration( color: color, shape: BoxShape.circle, border: Border.all( color: isSelected ? theme.colorScheme.onSurface // Highlight selected : theme.dividerColor, // Border for others width: isSelected ? 3.0 : 1.0, ), boxShadow: isSelected ? [ BoxShadow( color: Colors.black.withOpacity(0.3), blurRadius: 3, offset: const Offset(0, 1), ) ] : null, ), ), ); }).toList(), ), const SizedBox(height: 16), // Add some space at the bottom ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context, null), // Return null if cancelled child: const Text('Отмена'), ), ElevatedButton( onPressed: _saveCategory, child: const Text('Сохранить'), ), ], ); } }