refactor(app): 迁移 4 个页面到共享 EmptyStateWidget + ErrorStateWidget
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled

迁移统计:
- discover_page: _buildError → ErrorStateWidget, _buildEmptyHint → EmptyStateWidget
- sticker_library_page: 错误 + 空列表 → 共享组件
- class_page: 错误/班级列表空/日记墙空/话题空 → 共享组件 (4 处)
- calendar_page: CalendarError → ErrorStateWidget

统一体验: 所有页面空状态使用一致的 icon + title + subtitle + CTA 布局
This commit is contained in:
iven
2026-06-07 13:42:56 +08:00
parent 2f96f9a4f4
commit 346c751cbb
4 changed files with 45 additions and 119 deletions

View File

@@ -9,6 +9,7 @@ import 'package:nuanji_app/core/theme/app_radius.dart';
import 'package:nuanji_app/core/utils/mood_utils.dart';
import 'package:nuanji_app/data/models/journal_entry.dart';
import 'package:nuanji_app/data/repositories/journal_repository.dart';
import '../../../widgets/error_state_widget.dart';
import '../bloc/calendar_bloc.dart';
/// 日历页面 — 月视图(心情色彩) + 周视图 + 时间轴
@@ -41,21 +42,11 @@ class _CalendarView extends StatelessWidget {
}
if (state is CalendarError) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.error_outline, size: 48, color: colorScheme.error),
const SizedBox(height: 16),
Text(state.message, style: theme.textTheme.bodyLarge),
const SizedBox(height: 16),
FilledButton.tonal(
onPressed: () => context.read<CalendarBloc>()
.add(CalendarMonthChanged(DateTime.now())),
child: const Text('重试'),
),
],
),
return ErrorStateWidget(
message: state.message,
onRetry: () => context.read<CalendarBloc>()
.add(CalendarMonthChanged(DateTime.now())),
icon: Icons.error_outline,
);
}

View File

@@ -9,6 +9,8 @@ import 'package:nuanji_app/data/models/journal_entry.dart';
import 'package:nuanji_app/data/models/school_class.dart';
import 'package:nuanji_app/data/repositories/class_repository.dart';
import 'package:nuanji_app/data/repositories/journal_repository.dart';
import '../../../widgets/empty_state_widget.dart';
import '../../../widgets/error_state_widget.dart';
import '../../auth/bloc/auth_bloc.dart';
import '../bloc/class_bloc.dart';
import '../widgets/comment_bottom_sheet.dart';
@@ -46,7 +48,10 @@ class _ClassView extends StatelessWidget {
if (state is ClassError) {
return Scaffold(
appBar: AppBar(title: const Text('班级')),
body: Center(child: Text(state.message)),
body: ErrorStateWidget(
message: state.message,
onRetry: () => context.read<ClassBloc>().add(const ClassLoadMyClasses()),
),
);
}
@@ -93,22 +98,11 @@ class _ClassListView extends StatelessWidget {
}
Widget _buildEmptyState(BuildContext context, ColorScheme colorScheme) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.groups_outlined, size: 64, color: colorScheme.onSurface.withValues(alpha: 0.2)),
const SizedBox(height: 16),
Text('还没有加入任何班级', style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurface.withValues(alpha: 0.5),
)),
const SizedBox(height: 24),
FilledButton.tonal(
onPressed: () => context.go('/class-code'),
child: const Text('输入班级码加入'),
),
],
),
return EmptyStateWidget(
icon: Icons.group_add_rounded,
title: '还没有加入班级',
actionLabel: '通过班级码加入',
onAction: () => context.go('/class-code'),
);
}
}
@@ -250,21 +244,11 @@ class _DiaryWallTab extends StatelessWidget {
}
if (state.diaryWall.isEmpty) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.auto_stories_outlined, size: 48, color: colorScheme.onSurface.withValues(alpha: 0.2)),
const SizedBox(height: 12),
Text('日记墙还是空的', style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurface.withValues(alpha: 0.5),
)),
const SizedBox(height: 8),
Text('分享你的日记到班级吧!', style: theme.textTheme.bodySmall?.copyWith(
color: colorScheme.onSurface.withValues(alpha: 0.3),
)),
],
),
return const EmptyStateWidget(
icon: Icons.auto_stories_rounded,
title: '日记墙还是空的',
subtitle: '分享你的日记到这里吧',
iconSize: 48,
);
}
@@ -441,10 +425,9 @@ class _TopicsTab extends StatelessWidget {
final colorScheme = theme.colorScheme;
if (topics.isEmpty) {
return Center(
child: Text('暂无主题布置', style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurface.withValues(alpha: 0.5),
)),
return const EmptyStateWidget(
icon: Icons.assignment_outlined,
title: '暂无主题布置',
);
}

View File

@@ -19,6 +19,8 @@ import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_radius.dart';
import '../../../core/theme/app_shadows.dart';
import '../../../core/theme/app_typography.dart';
import '../../../widgets/empty_state_widget.dart';
import '../../../widgets/error_state_widget.dart';
import '../bloc/discover_bloc.dart';
import '../models/discover_models.dart';
@@ -126,66 +128,19 @@ class DiscoverPage extends StatelessWidget {
/// 错误状态
Widget _buildError(BuildContext context, String message) {
return Column(
children: [
const _LoadingSkeleton(height: 140),
const SizedBox(height: DesignTokens.spacing24),
Container(
width: double.infinity,
padding: const EdgeInsets.all(DesignTokens.spacing16),
decoration: BoxDecoration(
color: AppColors.rose.withValues(alpha: 0.1),
borderRadius: AppRadius.mdBorder,
),
child: Column(
children: [
Icon(Icons.cloud_off_rounded,
size: 32, color: AppColors.rose),
const SizedBox(height: DesignTokens.spacing8),
Text(message,
style: TextStyle(
fontSize: 13,
color: Theme.of(context).colorScheme.onSurfaceVariant),
textAlign: TextAlign.center),
const SizedBox(height: DesignTokens.spacing12),
TextButton.icon(
onPressed: () => context
.read<DiscoverBloc>()
.add(const DiscoverLoadData()),
icon: const Icon(Icons.refresh_rounded, size: 18),
label: const Text('重试'),
),
],
),
),
],
return ErrorStateWidget(
message: message,
onRetry: () =>
context.read<DiscoverBloc>().add(const DiscoverLoadData()),
);
}
/// 空数据提示
Widget _buildEmptyHint(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: DesignTokens.spacing32),
child: Center(
child: Column(
children: [
const Text('', style: TextStyle(fontSize: 40)),
const SizedBox(height: DesignTokens.spacing12),
Text('还没有发现内容',
style: TextStyle(
fontSize: 15,
color: Theme.of(context).colorScheme.onSurfaceVariant,
)),
const SizedBox(height: 4),
Text('写下你的第一篇日记,出现在这里吧!',
style: TextStyle(
fontSize: 13,
color: Theme.of(context).colorScheme.onSurfaceVariant
.withValues(alpha: 0.7),
)),
],
),
),
return const EmptyStateWidget(
icon: Icons.explore_rounded,
title: '还没有发现内容',
subtitle: '试试写一篇日记分享给大家吧',
);
}

View File

@@ -5,6 +5,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:nuanji_app/core/theme/app_colors.dart';
import 'package:nuanji_app/core/theme/app_radius.dart';
import 'package:nuanji_app/data/remote/api_client.dart';
import '../../../widgets/empty_state_widget.dart';
import '../../../widgets/error_state_widget.dart';
import '../bloc/sticker_bloc.dart';
/// 贴纸库页面 — 分类浏览贴纸包
@@ -64,18 +66,10 @@ class _StickerLibraryPageState extends State<StickerLibraryPage> {
}
if (state.errorMessage != null) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, size: 48, color: colorScheme.error),
const SizedBox(height: 16),
FilledButton.tonal(
onPressed: _bloc.load,
child: const Text('重试'),
),
],
),
return ErrorStateWidget(
message: state.errorMessage ?? '加载失败',
onRetry: _bloc.load,
icon: Icons.error_outline,
);
}
@@ -169,7 +163,10 @@ class _StickerLibraryPageState extends State<StickerLibraryPage> {
// ---- 贴纸包网格 ----
Expanded(
child: state.filteredPacks.isEmpty
? const Center(child: Text('暂无贴纸包'))
? const EmptyStateWidget(
icon: Icons.sticky_note_2_outlined,
title: '暂无贴纸包',
)
: GridView.builder(
padding: const EdgeInsets.all(16),
gridDelegate: