This commit is contained in:
2026-03-21 01:30:29 +03:00
parent 902d5953a7
commit f73d6788a2
+178 -77
View File
@@ -47,8 +47,24 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
bool _editingPrimary = true; bool _editingPrimary = true;
Color _tempPrimary = CardColorService.defaultPrimary; Color _tempPrimary = CardColorService.defaultPrimary;
Color _tempSecondary = CardColorService.defaultSecondary; Color _tempSecondary = CardColorService.defaultSecondary;
HSVColor _tempPrimaryHSV = HSVColor.fromColor(CardColorService.defaultPrimary);
HSVColor _tempSecondaryHSV = HSVColor.fromColor(CardColorService.defaultSecondary);
double _cardBottomY = 300; double _cardBottomY = 300;
HSVColor get _currentHSV => _editingPrimary ? _tempPrimaryHSV : _tempSecondaryHSV;
void _onHSVChanged(HSVColor hsv) {
setState(() {
if (_editingPrimary) {
_tempPrimaryHSV = hsv;
_tempPrimary = hsv.toColor();
} else {
_tempSecondaryHSV = hsv;
_tempSecondary = hsv.toColor();
}
});
}
Border? _themeBorder(BuildContext context) { Border? _themeBorder(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark; final isDark = Theme.of(context).brightness == Brightness.dark;
return isDark ? null : Border.all(color: const Color(0xFFDDDDEE), width: 1); return isDark ? null : Border.all(color: const Color(0xFFDDDDEE), width: 1);
@@ -58,6 +74,8 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
final colors = ref.read(cardColorsProvider); final colors = ref.read(cardColorsProvider);
_tempPrimary = colors.primary; _tempPrimary = colors.primary;
_tempSecondary = colors.secondary; _tempSecondary = colors.secondary;
_tempPrimaryHSV = HSVColor.fromColor(colors.primary);
_tempSecondaryHSV = HSVColor.fromColor(colors.secondary);
// Calculate actual card bottom: status bar + appbar + top padding + card height // Calculate actual card bottom: status bar + appbar + top padding + card height
final statusBar = MediaQuery.of(context).padding.top; final statusBar = MediaQuery.of(context).padding.top;
@@ -261,6 +279,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
left: 20, left: 20,
right: 20, right: 20,
top: _cardBottomY + 16, top: _cardBottomY + 16,
bottom: MediaQuery.of(context).padding.bottom + 16,
child: GestureDetector( child: GestureDetector(
onTap: () {}, // prevent dismiss onTap: () {}, // prevent dismiss
child: _buildColorPanel(context), child: _buildColorPanel(context),
@@ -271,88 +290,161 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
} }
Widget _buildColorPanel(BuildContext context) { Widget _buildColorPanel(BuildContext context) {
return Container( final maxHeight = MediaQuery.of(context).size.height - _cardBottomY - 32 - MediaQuery.of(context).padding.bottom;
padding: const EdgeInsets.all(20), final isDark = Theme.of(context).brightness == Brightness.dark;
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface, return ConstrainedBox(
borderRadius: BorderRadius.circular(20), constraints: BoxConstraints(
boxShadow: [ maxHeight: maxHeight,
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 24,
offset: const Offset(0, 8),
),
],
), ),
child: Column( child: SingleChildScrollView(
mainAxisSize: MainAxisSize.min, physics: const ClampingScrollPhysics(),
crossAxisAlignment: CrossAxisAlignment.start, child: Container(
children: [ padding: const EdgeInsets.fromLTRB(20, 16, 20, 20),
// Toggle between primary/secondary decoration: BoxDecoration(
Row( color: Theme.of(context).colorScheme.surface,
children: [ borderRadius: BorderRadius.circular(20),
_PanelTab( boxShadow: [
label: 'Primary', BoxShadow(
isSelected: _editingPrimary, color: Colors.black.withOpacity(0.3),
color: _tempPrimary, blurRadius: 24,
onTap: () => setState(() => _editingPrimary = true), offset: const Offset(0, 8),
),
const SizedBox(width: 12),
_PanelTab(
label: 'Secondary',
isSelected: !_editingPrimary,
color: _tempSecondary,
onTap: () => setState(() => _editingPrimary = false),
), ),
], ],
), ),
const SizedBox(height: 16), child: Column(
// HSV Color Picker mainAxisSize: MainAxisSize.min,
SizedBox( crossAxisAlignment: CrossAxisAlignment.start,
height: 200, children: [
child: ColorPicker( // TOP ROW: tabs + close button
pickerColor: _editingPrimary ? _tempPrimary : _tempSecondary, Row(
onColorChanged: (color) { children: [
setState(() { _PanelTab(
if (_editingPrimary) { label: 'Primary',
_tempPrimary = color; isSelected: _editingPrimary,
} else { color: _tempPrimary,
_tempSecondary = color; onTap: () => setState(() => _editingPrimary = true),
} ),
}); const SizedBox(width: 10),
}, _PanelTab(
colorPickerWidth: MediaQuery.of(context).size.width - 80, label: 'Secondary',
pickerAreaHeightPercent: 0.7, isSelected: !_editingPrimary,
enableAlpha: false, color: _tempSecondary,
displayThumbColor: true, onTap: () => setState(() => _editingPrimary = false),
labelTypes: const [], ),
pickerAreaBorderRadius: BorderRadius.circular(12), const Spacer(),
), // CLOSE BUTTON
), GestureDetector(
const SizedBox(height: 16), onTap: () => setState(() => _editingCard = false),
// Confirm button child: Container(
SizedBox( width: 32,
width: double.infinity, height: 32,
child: ElevatedButton( decoration: BoxDecoration(
onPressed: () { color: Theme.of(context).colorScheme.onSurface.withOpacity(0.08),
ref.read(cardColorsProvider.notifier).save(_tempPrimary, _tempSecondary); shape: BoxShape.circle,
setState(() => _editingCard = false); ),
}, child: Icon(
style: ElevatedButton.styleFrom( Icons.close_rounded,
backgroundColor: const Color(0xFF7C6DED), size: 18,
foregroundColor: Colors.white, color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
padding: const EdgeInsets.symmetric(vertical: 14), ),
shape: RoundedRectangleBorder( ),
borderRadius: BorderRadius.circular(14), ),
],
),
const SizedBox(height: 16),
// 2D spectrum area — drag to pick saturation + value
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: SizedBox(
height: 180,
width: double.infinity,
child: ColorPickerArea(
_currentHSV,
_onHSVChanged,
PaletteType.hsvWithHue,
),
), ),
), ),
child: const Text( const SizedBox(height: 12),
'Apply', // Hue rainbow slider — drag to change hue
style: TextStyle(fontWeight: FontWeight.w700, fontSize: 15), ClipRRect(
borderRadius: BorderRadius.circular(6),
child: SizedBox(
height: 24,
child: ColorPickerSlider(
TrackType.hue,
_currentHSV,
_onHSVChanged,
displayThumbColor: true,
),
),
), ),
), const SizedBox(height: 12),
// Color preview row
Row(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: _tempPrimary,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isDark ? Colors.white24 : Colors.black12,
),
),
),
const SizedBox(width: 8),
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: _tempSecondary,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isDark ? Colors.white24 : Colors.black12,
),
),
),
const SizedBox(width: 12),
Expanded(
child: Text(
'#${(_editingPrimary ? _tempPrimary : _tempSecondary).value.toRadixString(16).substring(2).toUpperCase()}',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5),
fontFamily: 'monospace',
),
),
),
],
),
const SizedBox(height: 16),
// APPLY BUTTON
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
ref.read(cardColorsProvider.notifier).save(_tempPrimary, _tempSecondary);
setState(() => _editingCard = false);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF7C6DED),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
),
child: const Text(
'Apply',
style: TextStyle(fontWeight: FontWeight.w700, fontSize: 15),
),
),
),
],
), ),
], ),
), ),
); );
} }
@@ -373,6 +465,12 @@ class _PanelTab extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final unselectedBorder = isDark ? Colors.white24 : const Color(0xFFCCCCDD);
final unselectedText = isDark
? Colors.white60
: Theme.of(context).colorScheme.onSurface.withOpacity(0.5);
return GestureDetector( return GestureDetector(
onTap: onTap, onTap: onTap,
child: AnimatedContainer( child: AnimatedContainer(
@@ -382,7 +480,7 @@ class _PanelTab extends StatelessWidget {
color: isSelected ? color.withOpacity(0.15) : Colors.transparent, color: isSelected ? color.withOpacity(0.15) : Colors.transparent,
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
border: Border.all( border: Border.all(
color: isSelected ? color : Colors.white24, color: isSelected ? color : unselectedBorder,
width: 1.5, width: 1.5,
), ),
), ),
@@ -395,7 +493,10 @@ class _PanelTab extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
color: color, color: color,
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all(color: Colors.white30, width: 1), border: Border.all(
color: isDark ? Colors.white30 : Colors.black12,
width: 1,
),
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
@@ -404,7 +505,7 @@ class _PanelTab extends StatelessWidget {
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
color: isSelected ? color : Colors.white60, color: isSelected ? color : unselectedText,
), ),
), ),
], ],