import 'package:flutter/material.dart'; import 'package:notas/theme/app_palette.dart'; import 'package:notas/widgets/category_style.dart'; class CategoryDraft { const CategoryDraft({ required this.name, required this.colorValue, required this.iconCodePoint, }); final String name; final int colorValue; final int iconCodePoint; } Future showCategoryEditorDialog({ required BuildContext context, required String title, required String confirmLabel, String? initialName, int? initialColorValue, int? initialIconCodePoint, }) async { final TextEditingController controller = TextEditingController( text: initialName ?? '', ); final List colorOptions = CategoryStyle.colorsOf(context); final List iconOptions = CategoryStyle.icons; final int fallbackColorValue = initialColorValue ?? colorOptions.first.toARGB32(); final int fallbackIconCodePoint = initialIconCodePoint ?? iconOptions.first.codePoint; try { final CategoryDraft? result = await showDialog( context: context, builder: (BuildContext dialogContext) { final AppPalette palette = Theme.of(dialogContext).extension() ?? AppPalette.fromBrightness(Theme.of(dialogContext).brightness); final List dialogColorOptions = CategoryStyle.colorsOf( dialogContext, ); final List dialogIconOptions = CategoryStyle.icons; int selectedColorValue = fallbackColorValue; int selectedIconCodePoint = fallbackIconCodePoint; return StatefulBuilder( builder: (BuildContext context, StateSetter setDialogState) { final Color previewColor = Color(selectedColorValue); return AlertDialog( backgroundColor: palette.surfaceElevated, title: Text(title), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( controller: controller, autofocus: true, textInputAction: TextInputAction.done, decoration: const InputDecoration( hintText: 'Nombre de la categoría', ), onSubmitted: (String value) { final String name = value.trim(); if (name.isEmpty) { return; } Navigator.of(dialogContext).pop( CategoryDraft( name: name, colorValue: selectedColorValue, iconCodePoint: selectedIconCodePoint, ), ); }, ), const SizedBox(height: 20), Text( 'Color', style: TextStyle( color: palette.textSecondary, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 10), Wrap( spacing: 10, runSpacing: 10, children: [ for (final Color color in dialogColorOptions) InkWell( borderRadius: BorderRadius.circular(999), onTap: () { setDialogState(() { selectedColorValue = color.toARGB32(); }); }, child: AnimatedContainer( duration: const Duration(milliseconds: 160), width: 42, height: 42, decoration: BoxDecoration( shape: BoxShape.circle, color: color, border: Border.all( color: selectedColorValue == color.toARGB32() ? palette.textPrimary : palette.border, width: selectedColorValue == color.toARGB32() ? 2.5 : 1, ), boxShadow: [ BoxShadow( color: color.withValues(alpha: 0.0), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: selectedColorValue == color.toARGB32() ? Icon( Icons.check, size: 18, color: color.computeLuminance() > 0.5 ? Colors.black : Colors.white, ) : null, ), ), ], ), const SizedBox(height: 20), Text( 'Icono', style: TextStyle( color: palette.textSecondary, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 10), Wrap( spacing: 8, runSpacing: 8, children: [ for (final IconData icon in dialogIconOptions) InkWell( borderRadius: BorderRadius.circular(14), onTap: () { setDialogState(() { selectedIconCodePoint = icon.codePoint; }); }, child: AnimatedContainer( duration: const Duration(milliseconds: 160), width: 50, height: 50, decoration: BoxDecoration( color: selectedIconCodePoint == icon.codePoint ? previewColor.withValues(alpha: 0.14) : palette.fill, borderRadius: BorderRadius.circular(14), border: Border.all( color: selectedIconCodePoint == icon.codePoint ? previewColor : palette.border, width: selectedIconCodePoint == icon.codePoint ? 2 : 1, ), ), child: Icon( icon, color: selectedIconCodePoint == icon.codePoint ? previewColor : palette.textSecondary, ), ), ), ], ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.of(dialogContext).pop(), child: const Text('Cancelar'), ), FilledButton( onPressed: () { final String name = controller.text.trim(); if (name.isEmpty) { return; } Navigator.of(dialogContext).pop( CategoryDraft( name: name, colorValue: selectedColorValue, iconCodePoint: selectedIconCodePoint, ), ); }, child: Text(confirmLabel), ), ], ); }, ); }, ); if (result == null || result.name.trim().isEmpty) { return null; } return result; } finally { controller.dispose(); } }