feat: Implement color and icon selection tabs in CategoryDialog for improved user experience

This commit is contained in:
2026-05-21 18:39:28 +02:00
parent a9d818dec4
commit 7e210871dd
+151 -55
View File
@@ -784,6 +784,7 @@ class _CategoryDialogState extends State<_CategoryDialog> {
late final TextEditingController _controller; late final TextEditingController _controller;
Color? _selectedColor; Color? _selectedColor;
IconData? _selectedIcon; IconData? _selectedIcon;
int _selectedSection = 0;
final List<Color> _palette = [ final List<Color> _palette = [
Colors.amber, Colors.amber,
@@ -920,9 +921,11 @@ class _CategoryDialogState extends State<_CategoryDialog> {
title: Text( title: Text(
widget.category == null ? 'Crear categoría' : 'Editar categoría', widget.category == null ? 'Crear categoría' : 'Editar categoría',
), ),
content: SingleChildScrollView( content: SizedBox(
width: 380,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
TextField( TextField(
controller: _controller, controller: _controller,
@@ -930,65 +933,119 @@ class _CategoryDialogState extends State<_CategoryDialog> {
hintText: 'Nombre de la categoría', hintText: 'Nombre de la categoría',
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 16),
Align( Container(
alignment: Alignment.centerLeft, decoration: BoxDecoration(
child: Text( color: Colors.white.withValues(alpha: 0.04),
'Color', borderRadius: BorderRadius.circular(14),
style: const TextStyle(fontSize: 14, color: Colors.white70), border: Border.all(color: Colors.white12),
), ),
), child: Column(
const SizedBox(height: 8), mainAxisSize: MainAxisSize.min,
Wrap( children: [
spacing: 8, Row(
children: _palette.map((Color color) { children: [
final bool isSelected = _selectedColor == color; Expanded(
return GestureDetector( child: _PickerTabButton(
onTap: () => setState(() => _selectedColor = color), label: 'Color',
child: Container( selected: _selectedSection == 0,
width: 36, borderRadius: const BorderRadius.only(
height: 36, topLeft: Radius.circular(13),
decoration: BoxDecoration( ),
color: color, onTap: () => setState(() => _selectedSection = 0),
borderRadius: BorderRadius.circular(6), ),
border: isSelected ),
? Border.all(color: Colors.white, width: 2) Expanded(
: null, 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<String>('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<String>('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,
),
),
),
),
),
);
}
}