From 7e210871dd8e8044b8c22e1184b4f223735ccc70 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 21 May 2026 18:39:28 +0200 Subject: [PATCH] feat: Implement color and icon selection tabs in CategoryDialog for improved user experience --- lib/screens/home_screen.dart | 206 +++++++++++++++++++++++++---------- 1 file changed, 151 insertions(+), 55 deletions(-) diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index d1b786e..5b5fcfa 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -784,6 +784,7 @@ class _CategoryDialogState extends State<_CategoryDialog> { late final TextEditingController _controller; Color? _selectedColor; IconData? _selectedIcon; + int _selectedSection = 0; final List _palette = [ Colors.amber, @@ -920,9 +921,11 @@ class _CategoryDialogState extends State<_CategoryDialog> { title: Text( widget.category == null ? 'Crear categoría' : 'Editar categoría', ), - content: SingleChildScrollView( + content: SizedBox( + width: 380, child: Column( mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ TextField( controller: _controller, @@ -930,65 +933,119 @@ class _CategoryDialogState extends State<_CategoryDialog> { hintText: 'Nombre de la categoría', ), ), - const SizedBox(height: 12), - Align( - alignment: Alignment.centerLeft, - child: Text( - 'Color', - style: const TextStyle(fontSize: 14, color: Colors.white70), + const SizedBox(height: 16), + Container( + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.04), + borderRadius: BorderRadius.circular(14), + border: Border.all(color: Colors.white12), ), - ), - const SizedBox(height: 8), - Wrap( - spacing: 8, - children: _palette.map((Color color) { - final bool isSelected = _selectedColor == color; - return GestureDetector( - onTap: () => setState(() => _selectedColor = color), - child: Container( - width: 36, - height: 36, - decoration: BoxDecoration( - color: color, - borderRadius: BorderRadius.circular(6), - border: isSelected - ? Border.all(color: Colors.white, width: 2) - : null, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + child: _PickerTabButton( + label: 'Color', + selected: _selectedSection == 0, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(13), + ), + onTap: () => setState(() => _selectedSection = 0), + ), + ), + Expanded( + child: _PickerTabButton( + label: 'Icono', + selected: _selectedSection == 1, + borderRadius: const BorderRadius.only( + topRight: Radius.circular(13), + ), + onTap: () => setState(() => _selectedSection = 1), + ), + ), + ], + ), + const Divider(height: 1, color: Colors.white12), + Padding( + padding: const EdgeInsets.all(12), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 180), + child: _selectedSection == 0 + ? Wrap( + key: const ValueKey('colors'), + spacing: 10, + runSpacing: 10, + children: _palette.map((Color color) { + final bool isSelected = + _selectedColor?.value == color.value; + return GestureDetector( + onTap: () => setState(() { + _selectedColor = color; + _selectedSection = 0; + }), + child: Container( + width: 42, + height: 42, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(10), + border: isSelected + ? Border.all( + color: Colors.white, + width: 2, + ) + : Border.all( + color: Colors.white12, + width: 1, + ), + ), + ), + ); + }).toList(), + ) + : Wrap( + key: const ValueKey('icons'), + spacing: 10, + runSpacing: 10, + children: _icons.map((IconData icon) { + final bool isSelected = _selectedIcon == icon; + return GestureDetector( + onTap: () => setState(() { + _selectedIcon = icon; + _selectedSection = 1; + }), + child: Container( + width: 42, + height: 42, + decoration: BoxDecoration( + color: isSelected + ? Colors.white10 + : Colors.transparent, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: isSelected + ? Colors.white + : Colors.white12, + width: 1, + ), + ), + child: Icon( + icon, + color: isSelected + ? Colors.white + : Colors.white70, + ), + ), + ); + }).toList(), + ), ), ), - ); - }).toList(), - ), - const SizedBox(height: 12), - Align( - alignment: Alignment.centerLeft, - child: Text( - 'Icono', - style: const TextStyle(fontSize: 14, color: Colors.white70), + ], ), ), - const SizedBox(height: 8), - Wrap( - spacing: 8, - children: _icons.map((IconData icon) { - final bool isSelected = _selectedIcon == icon; - return GestureDetector( - onTap: () => setState(() => _selectedIcon = icon), - child: Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: isSelected ? Colors.white10 : Colors.transparent, - borderRadius: BorderRadius.circular(6), - ), - child: Icon( - icon, - color: isSelected ? Colors.white : Colors.white70, - ), - ), - ); - }).toList(), - ), ], ), ), @@ -1010,3 +1067,42 @@ class _CategoryDialogState extends State<_CategoryDialog> { ); } } + +class _PickerTabButton extends StatelessWidget { + const _PickerTabButton({ + required this.label, + required this.selected, + required this.borderRadius, + required this.onTap, + }); + + final String label; + final bool selected; + final BorderRadius borderRadius; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return Material( + borderRadius: borderRadius, + clipBehavior: Clip.antiAlias, + color: selected ? Colors.white10 : Colors.transparent, + child: InkWell( + borderRadius: borderRadius, + onTap: onTap, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 14), + child: Center( + child: Text( + label, + style: TextStyle( + color: selected ? Colors.white : Colors.white54, + fontWeight: selected ? FontWeight.w600 : FontWeight.w400, + ), + ), + ), + ), + ), + ); + } +}