Files
ReelShort/lib/core/widgets/empty_state.dart
T

129 lines
4.3 KiB
Dart

// Author: fruit.com.cn
import 'package:flutter/material.dart';
import '../theme/app_colors.dart';
const double _regularArtSize = 96;
const double _compactArtSize = 64;
const double _regularIconSize = 42;
const double _compactIconSize = 28;
const double _regularRadius = 28;
const double _compactRadius = 18;
const double _maxContentWidth = 280;
const double _actionMinHeight = 44;
const double _artOpacity = 0.11;
/// 统一空列表占位:柔和图标、标题、可选说明与操作。
class EmptyState extends StatelessWidget {
const EmptyState({
super.key,
required this.icon,
required this.title,
this.subtitle,
this.actionLabel,
this.onAction,
this.compact = false,
});
final IconData icon;
final String title;
final String? subtitle;
final String? actionLabel;
final VoidCallback? onAction;
final bool compact;
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final dark = Theme.of(context).brightness == Brightness.dark;
final iconColor = dark ? AppColors.blueSoft : AppColors.blue;
final artSize = compact ? _compactArtSize : _regularArtSize;
final iconSize = compact ? _compactIconSize : _regularIconSize;
final hasAction = actionLabel != null && onAction != null;
return Center(
child: ConstrainedBox(
key: const Key('empty-state-content'),
constraints: const BoxConstraints(maxWidth: _maxContentWidth),
child: Padding(
padding: EdgeInsets.all(compact ? 16 : 24),
child: Semantics(
container: true,
label: title,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ExcludeSemantics(
child: Container(
key: const Key('empty-state-art'),
width: artSize,
height: artSize,
alignment: Alignment.center,
decoration: BoxDecoration(
color: AppColors.blue.withValues(alpha: _artOpacity),
borderRadius: BorderRadius.circular(
compact ? _compactRadius : _regularRadius,
),
),
child: Icon(
icon,
key: const Key('empty-state-icon'),
size: iconSize,
color: iconColor,
),
),
),
SizedBox(height: compact ? 10 : 16),
ExcludeSemantics(
child: Text(
title,
key: const Key('empty-state-title'),
maxLines: 2,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style: TextStyle(
color: scheme.onSurface,
fontSize: compact ? 14 : 16,
fontWeight: FontWeight.w600,
height: 1.35,
),
),
),
if (subtitle != null) ...[
const SizedBox(height: 6),
ExcludeSemantics(
child: Text(
subtitle!,
maxLines: 2,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style: const TextStyle(
color: AppColors.muted,
fontSize: 13,
height: 1.4,
),
),
),
],
if (hasAction) ...[
const SizedBox(height: 10),
TextButton(
key: const Key('empty-state-action'),
onPressed: onAction,
style: TextButton.styleFrom(
foregroundColor: scheme.primary,
minimumSize: const Size(44, _actionMinHeight),
padding: const EdgeInsets.symmetric(horizontal: 14),
),
child: Text(actionLabel!),
),
],
],
),
),
),
),
);
}
}