import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:lunchpick/core/constants/app_colors.dart'; import 'package:lunchpick/core/utils/category_mapper.dart'; import 'package:lunchpick/presentation/providers/restaurant_provider.dart'; class CategorySelector extends ConsumerWidget { final String? selectedCategory; final Function(String?) onCategorySelected; final bool showAllOption; final bool multiSelect; final List? selectedCategories; final Function(List)? onMultipleSelected; const CategorySelector({ super.key, this.selectedCategory, required this.onCategorySelected, this.showAllOption = true, this.multiSelect = false, this.selectedCategories, this.onMultipleSelected, }); @override Widget build(BuildContext context, WidgetRef ref) { final isDark = Theme.of(context).brightness == Brightness.dark; final categoriesAsync = ref.watch(categoriesProvider); return categoriesAsync.when( data: (categories) { return SizedBox( height: 50, child: ListView( scrollDirection: Axis.horizontal, children: [ if (showAllOption && !multiSelect) ...[ _buildCategoryChip( context: context, label: '전체', icon: Icons.restaurant_menu, color: isDark ? AppColors.darkPrimary : AppColors.lightPrimary, isSelected: selectedCategory == null, onTap: () => onCategorySelected(null), ), const SizedBox(width: 8), ], ...categories.map((category) { final isSelected = multiSelect ? selectedCategories?.contains(category) ?? false : selectedCategory == category; return Padding( padding: const EdgeInsets.only(right: 8), child: _buildCategoryChip( context: context, label: CategoryMapper.getDisplayName(category), icon: CategoryMapper.getIcon(category), color: CategoryMapper.getColor(category), isSelected: isSelected, onTap: () { if (multiSelect) { _handleMultiSelect(category); } else { onCategorySelected(category); } }, ), ); }).toList(), ], ), ); }, loading: () => const SizedBox( height: 50, child: Center( child: CircularProgressIndicator(), ), ), error: (error, stack) => const SizedBox( height: 50, child: Center( child: Text('카테고리를 불러올 수 없습니다'), ), ), ); } void _handleMultiSelect(String category) { if (onMultipleSelected == null || selectedCategories == null) return; final List updatedCategories = List.from(selectedCategories!); if (updatedCategories.contains(category)) { updatedCategories.remove(category); } else { updatedCategories.add(category); } onMultipleSelected!(updatedCategories); } Widget _buildCategoryChip({ required BuildContext context, required String label, required IconData icon, required Color color, required bool isSelected, required VoidCallback onTap, }) { final isDark = Theme.of(context).brightness == Brightness.dark; return Material( color: Colors.transparent, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(20), child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: isSelected ? color.withOpacity(0.2) : isDark ? AppColors.darkSurface : AppColors.lightBackground, borderRadius: BorderRadius.circular(20), border: Border.all( color: isSelected ? color : Colors.transparent, width: 2, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( icon, size: 20, color: isSelected ? color : isDark ? AppColors.darkTextSecondary : AppColors.lightTextSecondary, ), const SizedBox(width: 6), Text( label, style: TextStyle( color: isSelected ? color : isDark ? AppColors.darkText : AppColors.lightText, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), ], ), ), ), ); } } /// 카테고리 선택 다이얼로그 class CategorySelectionDialog extends ConsumerWidget { final List selectedCategories; final String title; final String? subtitle; const CategorySelectionDialog({ super.key, required this.selectedCategories, this.title = '카테고리 선택', this.subtitle, }); @override Widget build(BuildContext context, WidgetRef ref) { final isDark = Theme.of(context).brightness == Brightness.dark; final categoriesAsync = ref.watch(categoriesProvider); return AlertDialog( backgroundColor: isDark ? AppColors.darkSurface : AppColors.lightSurface, title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title), if (subtitle != null) ...[ const SizedBox(height: 4), Text( subtitle!, style: TextStyle( fontSize: 14, color: isDark ? AppColors.darkTextSecondary : AppColors.lightTextSecondary, ), ), ], ], ), content: categoriesAsync.when( data: (categories) => SizedBox( width: double.maxFinite, child: GridView.builder( shrinkWrap: true, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, childAspectRatio: 1.2, crossAxisSpacing: 8, mainAxisSpacing: 8, ), itemCount: categories.length, itemBuilder: (context, index) { final category = categories[index]; final isSelected = selectedCategories.contains(category); return _CategoryGridItem( category: category, isSelected: isSelected, onTap: () { final updatedCategories = List.from(selectedCategories); if (isSelected) { updatedCategories.remove(category); } else { updatedCategories.add(category); } Navigator.pop(context, updatedCategories); }, ); }, ), ), loading: () => const Center( child: CircularProgressIndicator(), ), error: (error, stack) => Center( child: Text('카테고리를 불러올 수 없습니다: $error'), ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text( '취소', style: TextStyle( color: isDark ? AppColors.darkTextSecondary : AppColors.lightTextSecondary, ), ), ), TextButton( onPressed: () => Navigator.pop(context, selectedCategories), child: const Text('확인'), ), ], ); } } class _CategoryGridItem extends StatelessWidget { final String category; final bool isSelected; final VoidCallback onTap; const _CategoryGridItem({ required this.category, required this.isSelected, required this.onTap, }); @override Widget build(BuildContext context) { final isDark = Theme.of(context).brightness == Brightness.dark; final color = CategoryMapper.getColor(category); final icon = CategoryMapper.getIcon(category); final displayName = CategoryMapper.getDisplayName(category); return Material( color: Colors.transparent, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: isSelected ? color.withOpacity(0.2) : isDark ? AppColors.darkCard : AppColors.lightCard, borderRadius: BorderRadius.circular(12), border: Border.all( color: isSelected ? color : Colors.transparent, width: 2, ), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon, size: 28, color: isSelected ? color : isDark ? AppColors.darkTextSecondary : AppColors.lightTextSecondary, ), const SizedBox(height: 4), Text( displayName, style: TextStyle( fontSize: 12, color: isSelected ? color : isDark ? AppColors.darkText : AppColors.lightText, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), textAlign: TextAlign.center, maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ), ), ); } }