import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/theme/app_colors.dart'; import '../profile/profile_provider.dart'; import '../profile/profile_service.dart'; const int _totalSteps = 6; const List _stepAccentColors = [ Color(0xFF5856D6), // Goal — iOS purple Color(0xFF007AFF), // Gender — iOS blue Color(0xFFFF9500), // DOB — iOS orange Color(0xFF34C759), // Height + Weight — iOS green Color(0xFFFF2D55), // Activity — iOS pink Color(0xFFFF6B00), // Calories — deep orange ]; const List _stepIcons = [ Icons.emoji_events_outlined, Icons.person_outline, Icons.cake_outlined, Icons.monitor_weight_outlined, Icons.directions_run, Icons.local_fire_department, ]; class OnboardingScreen extends ConsumerStatefulWidget { const OnboardingScreen({super.key}); @override ConsumerState createState() => _OnboardingScreenState(); } class _OnboardingScreenState extends ConsumerState { final _pageController = PageController(); int _currentStep = 0; // Collected data — new order: goal first String? _goal; String? _gender; DateTime? _selectedDob; final _heightController = TextEditingController(); final _weightController = TextEditingController(); String? _activity; int _dailyCalories = 2000; bool _saving = false; @override void dispose() { _pageController.dispose(); _heightController.dispose(); _weightController.dispose(); super.dispose(); } // Mifflin-St Jeor formula — mirrors backend calories.go int _calculateCalories() { final heightValue = int.tryParse(_heightController.text); final weightValue = double.tryParse(_weightController.text); if (heightValue == null || weightValue == null || _selectedDob == null || _gender == null || _activity == null || _goal == null) { return 2000; } final now = DateTime.now(); int age = now.year - _selectedDob!.year; if (now.month < _selectedDob!.month || (now.month == _selectedDob!.month && now.day < _selectedDob!.day)) { age--; } final double bmr = _gender == 'male' ? 10 * weightValue + 6.25 * heightValue - 5 * age + 5 : 10 * weightValue + 6.25 * heightValue - 5 * age - 161; const activityMultipliers = { 'low': 1.375, 'moderate': 1.55, 'high': 1.725, }; const goalAdjustments = { 'lose': -500.0, 'maintain': 0.0, 'gain': 300.0, }; final tdee = bmr * (activityMultipliers[_activity] ?? 1.55); final adjusted = tdee + (goalAdjustments[_goal] ?? 0); return adjusted.round().clamp(1000, 5000); } bool _canAdvance() { switch (_currentStep) { case 0: // Goal return _goal != null; case 1: // Gender return _gender != null; case 2: // DOB return _selectedDob != null; case 3: // Height + Weight final heightValue = int.tryParse(_heightController.text); final weightValue = double.tryParse(_weightController.text); return heightValue != null && heightValue >= 100 && heightValue <= 250 && weightValue != null && weightValue >= 30 && weightValue <= 300; case 4: // Activity return _activity != null; case 5: // Calories return true; default: return false; } } void _nextStep() { if (!_canAdvance()) return; // Entering calorie review — pre-calculate based on collected data if (_currentStep == 4) { setState(() => _dailyCalories = _calculateCalories()); } if (_currentStep < _totalSteps - 1) { _pageController.nextPage( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ); } else { _finish(); } } void _prevStep() { if (_currentStep > 0) { _pageController.previousPage( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ); } } Future _finish() async { setState(() => _saving = true); final dob = _selectedDob!; final dobString = '${dob.year.toString().padLeft(4, '0')}-' '${dob.month.toString().padLeft(2, '0')}-' '${dob.day.toString().padLeft(2, '0')}'; final profileRequest = UpdateProfileRequest( heightCm: int.tryParse(_heightController.text), weightKg: double.tryParse(_weightController.text), dateOfBirth: dobString, gender: _gender, goal: _goal, activity: _activity, dailyCalories: _dailyCalories, ); final success = await ref.read(profileProvider.notifier).update(profileRequest); if (!mounted) return; setState(() => _saving = false); if (success) { context.go('/home'); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Не удалось сохранить. Попробуйте ещё раз.')), ); } } @override Widget build(BuildContext context) { final accentColor = _stepAccentColors[_currentStep]; return Scaffold( backgroundColor: Colors.white, body: SafeArea( child: Column( children: [ _ProgressHeader( currentStep: _currentStep, accentColor: accentColor, onBack: _currentStep > 0 ? _prevStep : null, ), Expanded( child: PageView( controller: _pageController, physics: const NeverScrollableScrollPhysics(), onPageChanged: (index) => setState(() => _currentStep = index), children: [ _StepPage( accentColor: _stepAccentColors[0], icon: _stepIcons[0], child: _GoalStepContent( selected: _goal, accentColor: _stepAccentColors[0], onChanged: (value) => setState(() => _goal = value), ), ), _StepPage( accentColor: _stepAccentColors[1], icon: _stepIcons[1], child: _GenderStepContent( selected: _gender, accentColor: _stepAccentColors[1], onChanged: (value) => setState(() => _gender = value), ), ), _StepPage( accentColor: _stepAccentColors[2], icon: _stepIcons[2], child: _DobStepContent( selectedDob: _selectedDob, accentColor: _stepAccentColors[2], onChanged: (value) => setState(() => _selectedDob = value), ), ), _StepPage( accentColor: _stepAccentColors[3], icon: _stepIcons[3], child: _HeightWeightStepContent( heightController: _heightController, weightController: _weightController, accentColor: _stepAccentColors[3], onChanged: () => setState(() {}), ), ), _StepPage( accentColor: _stepAccentColors[4], icon: _stepIcons[4], child: _ActivityStepContent( selected: _activity, accentColor: _stepAccentColors[4], onChanged: (value) => setState(() => _activity = value), ), ), _StepPage( accentColor: _stepAccentColors[5], icon: _stepIcons[5], child: _CalorieStepContent( calories: _dailyCalories, accentColor: _stepAccentColors[5], onChanged: (value) => setState(() => _dailyCalories = value), ), ), ], ), ), _ContinueButton( isLastStep: _currentStep == _totalSteps - 1, enabled: _canAdvance() && !_saving, saving: _saving, accentColor: accentColor, onPressed: _nextStep, ), ], ), ), ); } } // ── Progress header ──────────────────────────────────────────── class _ProgressHeader extends StatelessWidget { final int currentStep; final Color accentColor; final VoidCallback? onBack; const _ProgressHeader({ required this.currentStep, required this.accentColor, this.onBack, }); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.fromLTRB(8, 8, 16, 0), child: Row( children: [ SizedBox( width: 48, child: onBack != null ? IconButton( icon: const Icon(Icons.arrow_back), onPressed: onBack, ) : const SizedBox.shrink(), ), Expanded( child: ClipRRect( borderRadius: BorderRadius.circular(4), child: LinearProgressIndicator( value: (currentStep + 1) / _totalSteps, minHeight: 6, backgroundColor: AppColors.separator, valueColor: AlwaysStoppedAnimation(accentColor), ), ), ), const SizedBox(width: 48), ], ), ); } } // ── Continue button ──────────────────────────────────────────── class _ContinueButton extends StatelessWidget { final bool isLastStep; final bool enabled; final bool saving; final Color accentColor; final VoidCallback onPressed; const _ContinueButton({ required this.isLastStep, required this.enabled, required this.saving, required this.accentColor, required this.onPressed, }); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.fromLTRB(24, 12, 24, 24), child: SizedBox( width: double.infinity, child: FilledButton( style: FilledButton.styleFrom(backgroundColor: accentColor), onPressed: enabled ? onPressed : null, child: saving ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : Text(isLastStep ? 'Готово' : 'Продолжить'), ), ), ); } } // ── Step page: hero + content ────────────────────────────────── class _StepPage extends StatelessWidget { final Color accentColor; final IconData icon; final Widget child; const _StepPage({ required this.accentColor, required this.icon, required this.child, }); @override Widget build(BuildContext context) { return Column( children: [ Expanded( flex: 2, child: _StepHero(accentColor: accentColor, icon: icon), ), Expanded( flex: 3, child: _StepContent(child: child), ), ], ); } } // ── Step hero — colored illustration area ────────────────────── class _StepHero extends StatelessWidget { final Color accentColor; final IconData icon; const _StepHero({required this.accentColor, required this.icon}); @override Widget build(BuildContext context) { return Container( width: double.infinity, color: accentColor.withValues(alpha: 0.12), child: Center( child: SizedBox( width: 180, height: 180, child: Stack( alignment: Alignment.center, children: [ Container( width: 180, height: 180, decoration: BoxDecoration( shape: BoxShape.circle, color: accentColor.withValues(alpha: 0.12), ), ), Container( width: 130, height: 130, decoration: BoxDecoration( shape: BoxShape.circle, color: accentColor.withValues(alpha: 0.18), ), ), Container( width: 90, height: 90, decoration: BoxDecoration( shape: BoxShape.circle, color: accentColor.withValues(alpha: 0.25), ), ), Icon(icon, size: 48, color: accentColor), ], ), ), ), ); } } // ── Step content — white card with rounded top corners ───────── class _StepContent extends StatelessWidget { final Widget child; const _StepContent({required this.child}); @override Widget build(BuildContext context) { return Container( width: double.infinity, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(28)), ), padding: const EdgeInsets.fromLTRB(24, 28, 24, 16), child: SingleChildScrollView(child: child), ); } } // ── Shared selectable card ────────────────────────────────────── class _SelectableCard extends StatelessWidget { final bool selected; final Color accentColor; final VoidCallback onTap; final Widget child; final EdgeInsets? padding; const _SelectableCard({ required this.selected, required this.accentColor, required this.onTap, required this.child, this.padding, }); @override Widget build(BuildContext context) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(16), child: AnimatedContainer( duration: const Duration(milliseconds: 150), padding: padding ?? const EdgeInsets.symmetric(horizontal: 16, vertical: 16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), border: Border.all( color: selected ? accentColor : AppColors.separator, width: selected ? 2 : 1, ), color: selected ? accentColor.withValues(alpha: 0.08) : Colors.transparent, ), child: child, ), ); } } // ── Step 1: Goal ─────────────────────────────────────────────── class _GoalStepContent extends StatelessWidget { final String? selected; final Color accentColor; final ValueChanged onChanged; const _GoalStepContent({ required this.selected, required this.accentColor, required this.onChanged, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Ваша цель', style: theme.textTheme.headlineSmall), const SizedBox(height: 4), Text( 'Что вы хотите достичь?', style: theme.textTheme.bodyMedium ?.copyWith(color: AppColors.textSecondary), ), const SizedBox(height: 20), _GoalCard( icon: Icons.trending_down, label: 'Похудеть', selected: selected == 'lose', accentColor: accentColor, onTap: () => onChanged('lose'), ), const SizedBox(height: 12), _GoalCard( icon: Icons.balance, label: 'Поддержать вес', selected: selected == 'maintain', accentColor: accentColor, onTap: () => onChanged('maintain'), ), const SizedBox(height: 12), _GoalCard( icon: Icons.trending_up, label: 'Набрать массу', selected: selected == 'gain', accentColor: accentColor, onTap: () => onChanged('gain'), ), ], ); } } class _GoalCard extends StatelessWidget { final IconData icon; final String label; final bool selected; final Color accentColor; final VoidCallback onTap; const _GoalCard({ required this.icon, required this.label, required this.selected, required this.accentColor, required this.onTap, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); return _SelectableCard( selected: selected, accentColor: accentColor, onTap: onTap, child: Row( children: [ Icon( icon, size: 28, color: selected ? accentColor : AppColors.textSecondary, ), const SizedBox(width: 16), Expanded( child: Text( label, style: theme.textTheme.bodyLarge?.copyWith( color: selected ? accentColor : AppColors.textPrimary, fontWeight: selected ? FontWeight.w600 : FontWeight.normal, ), ), ), Icon( Icons.arrow_forward_ios, size: 16, color: selected ? accentColor : AppColors.textSecondary, ), ], ), ); } } // ── Step 2: Gender ───────────────────────────────────────────── class _GenderStepContent extends StatelessWidget { final String? selected; final Color accentColor; final ValueChanged onChanged; const _GenderStepContent({ required this.selected, required this.accentColor, required this.onChanged, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Ваш пол', style: theme.textTheme.headlineSmall), const SizedBox(height: 4), Text( 'Учитывается при расчёте BMR', style: theme.textTheme.bodyMedium ?.copyWith(color: AppColors.textSecondary), ), const SizedBox(height: 20), Row( children: [ Expanded( child: _GenderCard( label: 'Мужской', icon: Icons.person_outline, selected: selected == 'male', accentColor: accentColor, onTap: () => onChanged('male'), ), ), const SizedBox(width: 12), Expanded( child: _GenderCard( label: 'Женский', icon: Icons.person_2_outlined, selected: selected == 'female', accentColor: accentColor, onTap: () => onChanged('female'), ), ), ], ), ], ); } } class _GenderCard extends StatelessWidget { final String label; final IconData icon; final bool selected; final Color accentColor; final VoidCallback onTap; const _GenderCard({ required this.label, required this.icon, required this.selected, required this.accentColor, required this.onTap, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); return _SelectableCard( selected: selected, accentColor: accentColor, onTap: onTap, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 28), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon, size: 36, color: selected ? accentColor : AppColors.textSecondary, ), const SizedBox(height: 10), Text( label, style: theme.textTheme.bodyLarge?.copyWith( color: selected ? accentColor : AppColors.textPrimary, fontWeight: selected ? FontWeight.w600 : FontWeight.normal, ), ), ], ), ); } } // ── Step 3: Date of birth ────────────────────────────────────── class _DobStepContent extends StatelessWidget { final DateTime? selectedDob; final Color accentColor; final ValueChanged onChanged; const _DobStepContent({ required this.selectedDob, required this.accentColor, required this.onChanged, }); Future _pickDate(BuildContext context) async { final now = DateTime.now(); final initialDate = selectedDob ?? DateTime(now.year - 25, now.month, now.day); final pickedDate = await showDatePicker( context: context, initialDate: initialDate, firstDate: DateTime(now.year - 120), lastDate: DateTime(now.year - 10, now.month, now.day), ); if (pickedDate != null) onChanged(pickedDate); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final displayText = selectedDob != null ? '${selectedDob!.day.toString().padLeft(2, '0')}.' '${selectedDob!.month.toString().padLeft(2, '0')}.' '${selectedDob!.year}' : 'Выбрать дату'; final isSelected = selectedDob != null; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Дата рождения', style: theme.textTheme.headlineSmall), const SizedBox(height: 4), Text( 'Используется для расчёта нормы калорий', style: theme.textTheme.bodyMedium ?.copyWith(color: AppColors.textSecondary), ), const SizedBox(height: 20), InkWell( onTap: () => _pickDate(context), borderRadius: BorderRadius.circular(16), child: AnimatedContainer( duration: const Duration(milliseconds: 150), padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 22), decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), border: Border.all( color: isSelected ? accentColor : AppColors.separator, width: isSelected ? 2 : 1, ), color: isSelected ? accentColor.withValues(alpha: 0.08) : Colors.transparent, ), child: Row( children: [ Icon( Icons.calendar_today_outlined, color: isSelected ? accentColor : AppColors.textSecondary, ), const SizedBox(width: 16), Text( displayText, style: theme.textTheme.bodyLarge?.copyWith( color: isSelected ? accentColor : AppColors.textSecondary, fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, ), ), ], ), ), ), ], ); } } // ── Step 4: Height + Weight ──────────────────────────────────── class _HeightWeightStepContent extends StatelessWidget { final TextEditingController heightController; final TextEditingController weightController; final Color accentColor; final VoidCallback onChanged; const _HeightWeightStepContent({ required this.heightController, required this.weightController, required this.accentColor, required this.onChanged, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Рост и вес', style: theme.textTheme.headlineSmall), const SizedBox(height: 4), Text( 'Введите ваши параметры', style: theme.textTheme.bodyMedium ?.copyWith(color: AppColors.textSecondary), ), const SizedBox(height: 20), Row( children: [ Expanded( child: _MeasurementTile( controller: heightController, unit: 'см', accentColor: accentColor, keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter.digitsOnly, ], onChanged: (_) => onChanged(), ), ), const SizedBox(width: 16), Expanded( child: _MeasurementTile( controller: weightController, unit: 'кг', accentColor: accentColor, keyboardType: const TextInputType.numberWithOptions( decimal: true), inputFormatters: [ FilteringTextInputFormatter.allow( RegExp(r'[0-9.]')), ], onChanged: (_) => onChanged(), ), ), ], ), ], ); } } class _MeasurementTile extends StatefulWidget { final TextEditingController controller; final String unit; final Color accentColor; final TextInputType keyboardType; final List inputFormatters; final ValueChanged onChanged; const _MeasurementTile({ required this.controller, required this.unit, required this.accentColor, required this.keyboardType, required this.inputFormatters, required this.onChanged, }); @override State<_MeasurementTile> createState() => _MeasurementTileState(); } class _MeasurementTileState extends State<_MeasurementTile> { late final FocusNode _focusNode; bool _hasFocus = false; @override void initState() { super.initState(); _focusNode = FocusNode(); _focusNode.addListener( () => setState(() => _hasFocus = _focusNode.hasFocus)); } @override void dispose() { _focusNode.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final hasValue = widget.controller.text.isNotEmpty; final isActive = _hasFocus || hasValue; return InkWell( onTap: () => _focusNode.requestFocus(), borderRadius: BorderRadius.circular(16), child: AnimatedContainer( duration: const Duration(milliseconds: 150), padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 20), decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), border: Border.all( color: isActive ? widget.accentColor : AppColors.separator, width: isActive ? 2 : 1, ), color: isActive ? widget.accentColor.withValues(alpha: 0.06) : Colors.transparent, ), child: Column( children: [ TextField( controller: widget.controller, focusNode: _focusNode, keyboardType: widget.keyboardType, inputFormatters: widget.inputFormatters, textAlign: TextAlign.center, style: theme.textTheme.headlineLarge?.copyWith( color: isActive ? widget.accentColor : AppColors.textPrimary, fontWeight: FontWeight.w600, ), decoration: InputDecoration( border: InputBorder.none, isDense: true, contentPadding: EdgeInsets.zero, hintText: '–', hintStyle: theme.textTheme.headlineLarge?.copyWith( color: AppColors.separator, fontWeight: FontWeight.w300, ), ), onChanged: widget.onChanged, ), const SizedBox(height: 4), Text( widget.unit, style: theme.textTheme.bodyMedium?.copyWith( color: isActive ? widget.accentColor : AppColors.textSecondary, ), ), ], ), ), ); } } // ── Step 5: Activity ─────────────────────────────────────────── class _ActivityStepContent extends StatelessWidget { final String? selected; final Color accentColor; final ValueChanged onChanged; const _ActivityStepContent({ required this.selected, required this.accentColor, required this.onChanged, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Уровень активности', style: theme.textTheme.headlineSmall), const SizedBox(height: 4), Text( 'Насколько активен ваш образ жизни?', style: theme.textTheme.bodyMedium ?.copyWith(color: AppColors.textSecondary), ), const SizedBox(height: 20), _ActivityCard( icon: Icons.directions_walk, label: 'Низкая', description: 'Сидячая работа, мало движения', selected: selected == 'low', accentColor: accentColor, onTap: () => onChanged('low'), ), const SizedBox(height: 12), _ActivityCard( icon: Icons.directions_run, label: 'Средняя', description: 'Тренировки 3–5 раз в неделю', selected: selected == 'moderate', accentColor: accentColor, onTap: () => onChanged('moderate'), ), const SizedBox(height: 12), _ActivityCard( icon: Icons.fitness_center, label: 'Высокая', description: 'Интенсивные тренировки каждый день', selected: selected == 'high', accentColor: accentColor, onTap: () => onChanged('high'), ), ], ); } } class _ActivityCard extends StatelessWidget { final IconData icon; final String label; final String description; final bool selected; final Color accentColor; final VoidCallback onTap; const _ActivityCard({ required this.icon, required this.label, required this.description, required this.selected, required this.accentColor, required this.onTap, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); return _SelectableCard( selected: selected, accentColor: accentColor, onTap: onTap, child: Row( children: [ Icon( icon, size: 28, color: selected ? accentColor : AppColors.textSecondary, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: theme.textTheme.bodyLarge?.copyWith( color: selected ? accentColor : AppColors.textPrimary, fontWeight: selected ? FontWeight.w600 : FontWeight.normal, ), ), const SizedBox(height: 2), Text( description, style: theme.textTheme.bodySmall ?.copyWith(color: AppColors.textSecondary), ), ], ), ), if (selected) Icon(Icons.check_circle, color: accentColor), ], ), ); } } // ── Step 6: Calorie review ───────────────────────────────────── class _CalorieStepContent extends StatelessWidget { final int calories; final Color accentColor; final ValueChanged onChanged; const _CalorieStepContent({ required this.calories, required this.accentColor, required this.onChanged, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Норма калорий', style: theme.textTheme.headlineSmall), const SizedBox(height: 4), Text( 'Рассчитана по формуле Миффлина-Сан Жеора. Вы можете скорректировать значение.', style: theme.textTheme.bodyMedium ?.copyWith(color: AppColors.textSecondary), ), const SizedBox(height: 32), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _AdjustButton( icon: Icons.remove, accentColor: accentColor, onPressed: calories > 1000 ? () => onChanged(calories - 50) : null, ), const SizedBox(width: 24), Column( children: [ Text( '$calories', style: theme.textTheme.displaySmall?.copyWith( color: accentColor, fontWeight: FontWeight.w700, ), ), Text( 'ккал / день', style: theme.textTheme.bodyMedium ?.copyWith(color: AppColors.textSecondary), ), ], ), const SizedBox(width: 24), _AdjustButton( icon: Icons.add, accentColor: accentColor, onPressed: calories < 5000 ? () => onChanged(calories + 50) : null, ), ], ), const SizedBox(height: 20), Center( child: Text( 'Шаг: 50 ккал', style: theme.textTheme.bodySmall ?.copyWith(color: AppColors.textSecondary), ), ), ], ); } } class _AdjustButton extends StatelessWidget { final IconData icon; final Color accentColor; final VoidCallback? onPressed; const _AdjustButton({ required this.icon, required this.accentColor, this.onPressed, }); @override Widget build(BuildContext context) { return IconButton.outlined( onPressed: onPressed, icon: Icon( icon, color: onPressed != null ? accentColor : AppColors.separator, ), iconSize: 28, padding: const EdgeInsets.all(12), style: IconButton.styleFrom( side: BorderSide( color: onPressed != null ? accentColor : AppColors.separator, ), ), ); } }