import 'package:flutter/material.dart'; /// 空状态组件库 - 提供多种样式的空状态页面展示 /// 包括无数据、无结果、错误等不同场景的空状态显示 /// 基础空状态组件 - 通用的空状态显示组件 /// [icon] 空状态图标,使用Material Icons /// [title] 空状态标题文本 /// [message] 空状态描述文本 /// [actionText] 操作按钮文本,null时不显示按钮 /// [onAction] 操作按钮点击回调,null时按钮不可点击 /// [iconSize] 图标大小,null时使用默认大小 /// [iconColor] 图标颜色,null时使用主题色彩 class EmptyState extends StatelessWidget { /// 空状态图标,使用Material Icons final IconData icon; /// 空状态标题文本 final String title; /// 空状态描述文本 final String? message; /// 操作按钮文本,null时不显示按钮 final String? actionText; /// 操作按钮点击回调,null时按钮不可点击 final VoidCallback? onAction; /// 图标大小,null时使用默认大小 final double? iconSize; /// 图标颜色,null时使用主题色彩 final Color? iconColor; /// 自定义图标组件,优先级高于icon参数 final Widget? customIcon; /// 自定义操作组件,优先级高于actionText参数 final Widget? customAction; /// 主要内容垂直间距 final double spacing; /// 主要内容内边距 final EdgeInsetsGeometry? padding; /// 是否显示边框装饰 final bool showBorder; /// 边框圆角半径 final double borderRadius; /// 背景颜色,null时使用透明背景 final Color? backgroundColor; const EmptyState({ super.key, required this.icon, required this.title, this.message, this.actionText, this.onAction, this.iconSize, this.iconColor, this.customIcon, this.customAction, this.spacing = 16, this.padding, this.showBorder = false, this.borderRadius = 12, this.backgroundColor, }); /// 创建无数据空状态 - 用于列表、网格等无数据场景 factory EmptyState.noData({ String? title, String? message, String? actionText, VoidCallback? onAction, IconData? icon, }) { return EmptyState( icon: icon ?? Icons.inbox_outlined, title: title ?? '暂无数据', message: message ?? '当前没有可显示的数据', actionText: actionText, onAction: onAction, iconColor: Colors.grey, ); } /// 创建无搜索结果空状态 - 用于搜索无结果场景 factory EmptyState.noSearchResults({ String? searchQuery, VoidCallback? onClearSearch, }) { return EmptyState( icon: Icons.search_off_outlined, title: '未找到相关结果', message: searchQuery != null ? '未找到与"$searchQuery"相关的内容' : '未找到相关内容,请尝试其他关键词', actionText: onClearSearch != null ? '清除搜索' : null, onAction: onClearSearch, iconColor: Colors.grey, ); } /// 创建网络错误空状态 - 用于网络连接失败场景 factory EmptyState.networkError({ VoidCallback? onRetry, }) { return EmptyState( icon: Icons.wifi_off_outlined, title: '网络连接失败', message: '请检查网络连接后重试', actionText: '重新加载', onAction: onRetry, iconColor: Colors.orange, ); } /// 创建服务器错误空状态 - 用于服务器异常场景 factory EmptyState.serverError({ VoidCallback? onRetry, }) { return EmptyState( icon: Icons.cloud_off_outlined, title: '服务器异常', message: '服务器暂时无法访问,请稍后重试', actionText: '重新加载', onAction: onRetry, iconColor: Colors.red, ); } /// 创建权限不足空状态 - 用于无权限访问场景 factory EmptyState.permissionDenied({ VoidCallback? onRequestPermission, }) { return EmptyState( icon: Icons.lock_outline, title: '权限不足', message: '您没有访问此内容的权限', actionText: onRequestPermission != null ? '申请权限' : null, onAction: onRequestPermission, iconColor: Colors.orange, ); } /// 创建收藏为空状态 - 用于收藏列表为空场景 factory EmptyState.emptyFavorites({ VoidCallback? onBrowse, }) { return EmptyState( icon: Icons.favorite_border_outlined, title: '暂无收藏', message: '您还没有收藏任何内容', actionText: onBrowse != null ? '去浏览' : null, onAction: onBrowse, iconColor: Colors.pink, ); } /// 创建购物车为空状态 - 用于购物车为空场景 factory EmptyState.emptyCart({ VoidCallback? onShop, }) { return EmptyState( icon: Icons.shopping_cart_outlined, title: '购物车是空的', message: '您还没有添加任何商品', actionText: onShop != null ? '去购物' : null, onAction: onShop, iconColor: Colors.blue, ); } /// 创建历史记录为空状态 - 用于历史记录为空场景 factory EmptyState.emptyHistory({ VoidCallback? onBrowse, }) { return EmptyState( icon: Icons.history_outlined, title: '暂无历史记录', message: '您还没有任何操作记录', actionText: onBrowse != null ? '去浏览' : null, onAction: onBrowse, iconColor: Colors.grey, ); } /// 创建文件为空状态 - 用于文件列表为空场景 factory EmptyState.emptyFiles({ VoidCallback? onUpload, }) { return EmptyState( icon: Icons.folder_open_outlined, title: '文件夹是空的', message: '此文件夹中还没有任何文件', actionText: onUpload != null ? '上传文件' : null, onAction: onUpload, iconColor: Colors.blue, ); } /// 创建图片为空状态 - 用于图片列表为空场景 factory EmptyState.emptyImages({ VoidCallback? onAddImages, }) { return EmptyState( icon: Icons.photo_library_outlined, title: '暂无图片', message: '您还没有添加任何图片', actionText: onAddImages != null ? '添加图片' : null, onAction: onAddImages, iconColor: Colors.purple, ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); Widget content = Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ // 图标部分 if (customIcon != null) ...[ customIcon!, SizedBox(height: spacing), ] else ...[ Icon( icon, size: iconSize ?? 64, color: iconColor ?? theme.colorScheme.onSurface.withOpacity(0.4), ), SizedBox(height: spacing), ], // 标题部分 Text( title, style: theme.textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w600, color: theme.colorScheme.onSurface.withOpacity(0.8), ), textAlign: TextAlign.center, ), // 描述文本部分 if (message != null) ...[ SizedBox(height: spacing * 0.5), Text( message!, style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.6), ), textAlign: TextAlign.center, ), ], // 操作按钮部分 if (customAction != null) ...[ SizedBox(height: spacing * 1.5), customAction!, ] else if (actionText != null) ...[ SizedBox(height: spacing * 1.5), ElevatedButton.icon( onPressed: onAction, icon: const Icon(Icons.refresh, size: 18), label: Text(actionText!), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), ), ], ], ); // 添加内边距 content = Padding( padding: padding ?? const EdgeInsets.all(24), child: content, ); // 添加边框装饰 if (showBorder) { content = Container( decoration: BoxDecoration( color: backgroundColor ?? theme.colorScheme.surface.withOpacity(0.5), border: Border.all( color: theme.colorScheme.outline.withOpacity(0.2), width: 1, ), borderRadius: BorderRadius.circular(borderRadius), ), child: content, ); } else if (backgroundColor != null) { content = Container( decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular(borderRadius), ), child: content, ); } return content; } } /// 空状态页面 - 全屏空状态显示,包含图标、文本和操作按钮 /// [icon] 空状态图标 /// [title] 空状态标题 /// [message] 空状态描述文本 /// [actionText] 操作按钮文本 /// [onAction] 操作按钮点击回调 /// [showAppBar] 是否显示应用栏 /// [appBarTitle] 应用栏标题 class EmptyStatePage extends StatelessWidget { /// 空状态图标 final IconData icon; /// 空状态标题 final String title; /// 空状态描述文本 final String? message; /// 操作按钮文本 final String? actionText; /// 操作按钮点击回调 final VoidCallback? onAction; /// 是否显示应用栏 final bool showAppBar; /// 应用栏标题 final String? appBarTitle; /// 自定义空状态组件,优先级高于其他参数 final Widget? customEmptyState; /// 背景颜色,null时使用主题背景色 final Color? backgroundColor; const EmptyStatePage({ super.key, required this.icon, required this.title, this.message, this.actionText, this.onAction, this.showAppBar = false, this.appBarTitle, this.customEmptyState, this.backgroundColor, }); /// 创建无数据空状态页面 factory EmptyStatePage.noData({ String? title, String? message, String? actionText, VoidCallback? onAction, bool showAppBar = false, String? appBarTitle, }) { return EmptyStatePage( icon: Icons.inbox_outlined, title: title ?? '暂无数据', message: message ?? '当前没有可显示的数据', actionText: actionText, onAction: onAction, showAppBar: showAppBar, appBarTitle: appBarTitle ?? '无数据', ); } /// 创建无搜索结果空状态页面 factory EmptyStatePage.noSearchResults({ String? searchQuery, VoidCallback? onClearSearch, bool showAppBar = false, }) { return EmptyStatePage( icon: Icons.search_off_outlined, title: '未找到相关结果', message: searchQuery != null ? '未找到与"$searchQuery"相关的内容' : '未找到相关内容,请尝试其他关键词', actionText: onClearSearch != null ? '清除搜索' : null, onAction: onClearSearch, showAppBar: showAppBar, appBarTitle: '搜索结果', ); } /// 创建网络错误空状态页面 factory EmptyStatePage.networkError({ VoidCallback? onRetry, bool showAppBar = false, }) { return EmptyStatePage( icon: Icons.wifi_off_outlined, title: '网络连接失败', message: '请检查网络连接后重试', actionText: '重新加载', onAction: onRetry, showAppBar: showAppBar, appBarTitle: '网络错误', ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( backgroundColor: backgroundColor ?? theme.colorScheme.background, appBar: showAppBar ? AppBar( title: Text(appBarTitle ?? title), centerTitle: true, ) : null, body: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(24), child: customEmptyState ?? EmptyState( icon: icon, title: title, message: message, actionText: actionText, onAction: onAction, showBorder: false, ), ), ), ); } } /// 空状态构建器 - 根据条件显示空状态或正常内容 /// [isEmpty] 是否为空状态 /// [emptyState] 空状态组件 /// [child] 正常内容组件 class EmptyStateBuilder extends StatelessWidget { /// 是否为空状态 final bool isEmpty; /// 空状态组件 final Widget emptyState; /// 正常内容组件 final Widget child; const EmptyStateBuilder({ super.key, required this.isEmpty, required this.emptyState, required this.child, }); @override Widget build(BuildContext context) { if (isEmpty) { return emptyState; } return child; } } /// 空状态包装器 - 简化版本,自动构建空状态 /// [isEmpty] 是否为空状态 /// [icon] 空状态图标 /// [title] 空状态标题 /// [message] 空状态描述 /// [actionText] 操作按钮文本 /// [onAction] 操作按钮点击回调 /// [child] 正常内容组件 class EmptyStateWrapper extends StatelessWidget { /// 是否为空状态 final bool isEmpty; /// 空状态图标 final IconData icon; /// 空状态标题 final String title; /// 空状态描述 final String? message; /// 操作按钮文本 final String? actionText; /// 操作按钮点击回调 final VoidCallback? onAction; /// 正常内容组件 final Widget child; /// 自定义空状态组件,优先级高于其他参数 final Widget? customEmptyState; const EmptyStateWrapper({ super.key, required this.isEmpty, required this.icon, required this.title, required this.child, this.message, this.actionText, this.onAction, this.customEmptyState, }); @override Widget build(BuildContext context) { if (isEmpty) { return customEmptyState ?? EmptyState( icon: icon, title: title, message: message, actionText: actionText, onAction: onAction, ); } return child; } }