import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../providers/folder_provider.dart'; import 'custom_button.dart'; /// 文件夹选择器弹窗 - 用于选择保存图片的目标文件夹 /// 支持文件夹列表显示、新建文件夹、最近使用排序等功能 class FolderSelectorDialog extends ConsumerStatefulWidget { /// 文件夹选择回调 final Function(String folderId) onFolderSelected; /// 当前选中的文件夹ID final String? currentFolderId; const FolderSelectorDialog({ Key? key, required this.onFolderSelected, this.currentFolderId, }) : super(key: key); @override ConsumerState createState() => _FolderSelectorDialogState(); } class _FolderSelectorDialogState extends ConsumerState { /// 搜索控制器 final TextEditingController _searchController = TextEditingController(); /// 搜索关键词 String searchKeyword = ''; @override void initState() { super.initState(); // 加载文件夹列表 WidgetsBinding.instance.addPostFrameCallback((_) { ref.read(folderProvider.notifier).loadFolders(); }); } @override void dispose() { _searchController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final colorScheme = theme.colorScheme; return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: Container( width: 480, height: 600, decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: theme.scaffoldBackgroundColor, ), child: Column( children: [ // 标题栏 _buildHeader(colorScheme), // 搜索框 _buildSearchBar(colorScheme), // 文件夹列表 Expanded( child: _buildFolderList(colorScheme), ), // 底部操作栏 _buildFooter(colorScheme), ], ), ), ); } /// 构建标题栏 Widget _buildHeader(ColorScheme colorScheme) { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: colorScheme.surface, borderRadius: const BorderRadius.only( topLeft: Radius.circular(16), topRight: Radius.circular(16), ), ), child: Row( children: [ Icon( Icons.folder, color: colorScheme.primary, size: 28, ), const SizedBox(width: 12), Text( '选择文件夹', style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, ), ), const Spacer(), IconButton( onPressed: () => Navigator.of(context).pop(), icon: const Icon(Icons.close), tooltip: '关闭', ), ], ), ); } /// 构建搜索框 Widget _buildSearchBar(ColorScheme colorScheme) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), child: TextField( controller: _searchController, decoration: InputDecoration( hintText: '搜索文件夹...', prefixIcon: const Icon(Icons.search), suffixIcon: searchKeyword.isNotEmpty ? IconButton( onPressed: () { _searchController.clear(); setState(() { searchKeyword = ''; }); }, icon: const Icon(Icons.clear), ) : null, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 12, ), ), onChanged: (value) { setState(() { searchKeyword = value; }); }, ), ); } /// 构建文件夹列表 Widget _buildFolderList(ColorScheme colorScheme) { final folderState = ref.watch(folderProvider); if (folderState.isLoading) { return const Center( child: CircularProgressIndicator(), ); } if (folderState.error != null) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.error_outline, size: 64, color: colorScheme.error, ), const SizedBox(height: 16), Text( '加载文件夹失败', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: colorScheme.error, ), ), const SizedBox(height: 16), CustomButton( text: '重试', onPressed: () { ref.read(folderProvider.notifier).loadFolders(); }, buttonType: ButtonType.outlined, ), ], ), ); } final folders = folderState.folders; if (folders.isEmpty) { return _buildEmptyState(colorScheme); } // 过滤文件夹 final filteredFolders = searchKeyword.isEmpty ? folders : folders.where((folder) => folder.name.toLowerCase().contains(searchKeyword.toLowerCase())).toList(); if (filteredFolders.isEmpty) { return _buildNoSearchResults(colorScheme); } return ListView.separated( padding: const EdgeInsets.symmetric(horizontal: 20), itemCount: filteredFolders.length, separatorBuilder: (context, index) => const SizedBox(height: 8), itemBuilder: (context, index) { final folder = filteredFolders[index]; final isSelected = folder.id == widget.currentFolderId; return _buildFolderItem(folder, isSelected, colorScheme); }, ); } /// 构建空状态 Widget _buildEmptyState(ColorScheme colorScheme) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.folder_outlined, size: 64, color: colorScheme.outline, ), const SizedBox(height: 16), Text( '还没有文件夹', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: colorScheme.outline, ), ), const SizedBox(height: 8), Text( '创建第一个文件夹来开始整理你的灵感', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: colorScheme.outline.withOpacity(0.7), ), ), const SizedBox(height: 24), CustomButton( text: '创建文件夹', onPressed: _showCreateFolderDialog, prefixIcon: Icons.add, buttonType: ButtonType.primary, ), ], ), ); } /// 构建无搜索结果状态 Widget _buildNoSearchResults(ColorScheme colorScheme) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.search_off, size: 64, color: colorScheme.outline, ), const SizedBox(height: 16), Text( '未找到匹配的文件夹', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: colorScheme.outline, ), ), const SizedBox(height: 8), Text( '尝试使用其他关键词搜索', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: colorScheme.outline.withOpacity(0.7), ), ), ], ), ); } /// 构建文件夹项 Widget _buildFolderItem( dynamic folder, bool isSelected, ColorScheme colorScheme, ) { return Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(12), onTap: () { widget.onFolderSelected(folder.id); Navigator.of(context).pop(); }, child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( color: isSelected ? colorScheme.primary : colorScheme.outline.withOpacity(0.2), width: isSelected ? 2 : 1, ), color: isSelected ? colorScheme.primary.withOpacity(0.1) : colorScheme.surface, ), child: Row( children: [ // 文件夹图标 Container( width: 48, height: 48, decoration: BoxDecoration( color: isSelected ? colorScheme.primary.withOpacity(0.2) : colorScheme.primaryContainer.withOpacity(0.5), borderRadius: BorderRadius.circular(12), ), child: Icon( _getFolderIcon(folder.icon), color: isSelected ? colorScheme.primary : colorScheme.primary, size: 24, ), ), const SizedBox(width: 16), // 文件夹信息 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( folder.name, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, color: isSelected ? colorScheme.primary : colorScheme.onSurface, ), ), const SizedBox(height: 4), Text( '${folder.imageCount} 张图片', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: colorScheme.onSurface.withOpacity(0.6), ), ), ], ), ), // 选择标记 if (isSelected) Icon( Icons.check_circle, color: colorScheme.primary, size: 24, ), ], ), ), ), ); } /// 构建底部操作栏 Widget _buildFooter(ColorScheme colorScheme) { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: colorScheme.surface, borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(16), bottomRight: Radius.circular(16), ), ), child: Row( children: [ // 创建新文件夹按钮 Expanded( child: CustomButton( text: '新建文件夹', onPressed: _showCreateFolderDialog, prefixIcon: Icons.add, buttonType: ButtonType.outlined, ), ), const SizedBox(width: 16), // 取消按钮 CustomButton( text: '取消', onPressed: () => Navigator.of(context).pop(), buttonType: ButtonType.text, ), ], ), ); } /// 显示创建文件夹对话框 void _showCreateFolderDialog() { showDialog( context: context, builder: (context) => CreateFolderDialog( onFolderCreated: (folderId) { Navigator.of(context).pop(); // 关闭创建对话框 widget.onFolderSelected(folderId); Navigator.of(context).pop(); // 关闭选择器对话框 }, ), ); } /// 获取文件夹图标 IconData _getFolderIcon(String? iconCode) { // 默认文件夹图标 if (iconCode == null || iconCode.isEmpty) { return Icons.folder; } // 尝试解析Material Icons名称 try { // 这里需要根据实际的图标存储方式来实现 // 暂时返回默认图标 return Icons.folder; } catch (e) { return Icons.folder; } } } /// 创建文件夹对话框 class CreateFolderDialog extends ConsumerStatefulWidget { /// 文件夹创建成功回调 final Function(String folderId) onFolderCreated; const CreateFolderDialog({ Key? key, required this.onFolderCreated, }) : super(key: key); @override ConsumerState createState() => _CreateFolderDialogState(); } class _CreateFolderDialogState extends ConsumerState { /// 文件夹名称控制器 final TextEditingController _nameController = TextEditingController(); /// 选中的图标 String selectedIcon = 'folder'; /// 表单键 final GlobalKey _formKey = GlobalKey(); /// 是否正在创建 bool isCreating = false; @override void dispose() { _nameController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final colorScheme = theme.colorScheme; return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: Container( width: 400, padding: const EdgeInsets.all(24), decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: theme.scaffoldBackgroundColor, ), child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // 标题 Text( '新建文件夹', style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 24), // 文件夹名称输入 TextFormField( controller: _nameController, decoration: const InputDecoration( labelText: '文件夹名称', hintText: '输入文件夹名称', border: OutlineInputBorder(), prefixIcon: Icon(Icons.folder), ), validator: (value) { if (value == null || value.trim().isEmpty) { return '请输入文件夹名称'; } if (value.trim().length > 20) { return '文件夹名称不能超过20个字符'; } return null; }, textInputAction: TextInputAction.done, onFieldSubmitted: (_) => _createFolder(), ), const SizedBox(height: 20), // 图标选择 Text( '选择图标', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 12), _buildIconSelector(colorScheme), const SizedBox(height: 24), // 操作按钮 Row( mainAxisAlignment: MainAxisAlignment.end, children: [ // 取消按钮 CustomButton( text: '取消', onPressed: () => Navigator.of(context).pop(), buttonType: ButtonType.text, ), const SizedBox(width: 16), // 创建按钮 CustomButton( text: '创建', onPressed: isCreating ? null : _createFolder, buttonType: ButtonType.primary, isLoading: isCreating, ), ], ), ], ), ), ), ); } /// 构建图标选择器 Widget _buildIconSelector(ColorScheme colorScheme) { final icons = [ 'folder', 'bookmark', 'favorite', 'star', 'work', 'home', 'travel_explore', 'restaurant', 'shopping_bag', 'sports_esports', ]; return Wrap( spacing: 12, runSpacing: 12, children: icons.map((icon) { final isSelected = selectedIcon == icon; return GestureDetector( onTap: () { setState(() { selectedIcon = icon; }); }, child: Container( width: 48, height: 48, decoration: BoxDecoration( color: isSelected ? colorScheme.primary.withOpacity(0.2) : colorScheme.surface, borderRadius: BorderRadius.circular(12), border: Border.all( color: isSelected ? colorScheme.primary : colorScheme.outline.withOpacity(0.2), width: isSelected ? 2 : 1, ), ), child: Icon( _getIconData(icon), color: isSelected ? colorScheme.primary : colorScheme.onSurface, size: 24, ), ), ); }).toList(), ); } /// 创建文件夹 Future _createFolder() async { if (!_formKey.currentState!.validate()) { return; } setState(() { isCreating = true; }); try { final folderId = await ref.read(folderProvider.notifier).createFolder( name: _nameController.text.trim(), icon: selectedIcon, ); if (folderId != null) { widget.onFolderCreated(folderId); } else { _showErrorSnackBar('创建文件夹失败'); } } catch (e) { _showErrorSnackBar('创建文件夹失败:${e.toString()}'); } finally { if (mounted) { setState(() { isCreating = false; }); } } } /// 显示错误提示 void _showErrorSnackBar(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: Theme.of(context).colorScheme.error, ), ); } /// 获取图标数据 IconData _getIconData(String iconName) { switch (iconName) { case 'folder': return Icons.folder; case 'bookmark': return Icons.bookmark; case 'favorite': return Icons.favorite; case 'star': return Icons.star; case 'work': return Icons.work; case 'home': return Icons.home; case 'travel_explore': return Icons.travel_explore; case 'restaurant': return Icons.restaurant; case 'shopping_bag': return Icons.shopping_bag; case 'sports_esports': return Icons.sports_esports; default: return Icons.folder; } } }