feat(flutter): 统一空列表占位组件

This commit is contained in:
Zhou Weidong committed 2026-10-17 17:44:44 +08:00
1 parent 19babb8f4a
commit 6d08330388
24 files changed
+394 -63

No files matched your search

+1
View File
@@ -11,6 +11,7 @@ import '../../core/data/services/catalog_controller.dart';
import '../../core/data/services/category_controller.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_theme.dart';
import '../../core/widgets/empty_state.dart';
import '../../core/widgets/remote_image.dart';
import '../../l10n/app_localizations.dart';
import 'discover_binding.dart';
@@ -44,14 +44,19 @@ class _HomeDramaGrid extends StatelessWidget {
final dramas = categories.visibleDramas;
if (dramas.isEmpty) {
final missingCategory = categories.selectedSectionMissing;
return SliverPadding(
padding: const EdgeInsets.fromLTRB(16, 6, 16, 24),
sliver: SliverToBoxAdapter(
child: Text(
categories.selectedSectionMissing
child: EmptyState(
key: const Key('home-all-dramas-empty'),
icon: missingCategory
? Icons.grid_view_rounded
: Icons.search_off_rounded,
title: missingCategory
? l10n.noCategoryDramas
: l10n.noSearchResults,
style: const TextStyle(color: AppColors.muted),
subtitle: l10n.emptyHint,
),
),
);
+5 -7
View File
@@ -15,6 +15,7 @@ import '../../core/platform/haptics.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_theme.dart';
import '../../core/widgets/auth_gate.dart';
import '../../core/widgets/empty_state.dart';
import '../../l10n/app_localizations.dart';
import 'downloads_binding.dart';
@@ -144,14 +145,11 @@ class _DownloadsBody extends StatelessWidget {
],
),
if (downloads.licenses.isEmpty)
Padding(
EmptyState(
key: const Key('downloads-empty'),
padding: const EdgeInsets.symmetric(vertical: 34),
child: Text(
l10n.noDownloads,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white70),
),
icon: Icons.download_outlined,
title: l10n.noDownloads,
subtitle: l10n.emptyHint,
)
else
for (final license in downloads.licenses) ...[
+8 -22
View File
@@ -17,6 +17,7 @@ import '../../core/theme/app_theme.dart';
import '../../core/widgets/remote_image.dart';
import '../../core/widgets/auth_gate.dart';
import '../../core/widgets/download_action.dart';
import '../../core/widgets/empty_state.dart';
import '../../l10n/app_localizations.dart';
import '../interaction/widgets/report_dialog.dart';
import 'drama_detail_binding.dart';
@@ -114,28 +115,13 @@ class _DramaDetailPageState extends State<DramaDetailPage> {
foregroundColor: Colors.white,
title: Text(localizedDramaTitle(l10n, drama)),
),
body: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.movie_filter_outlined,
size: 48,
color: Colors.white38,
),
const SizedBox(height: 12),
Text(
l10n.noEpisodesYet,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white70),
),
const SizedBox(height: 16),
FilledButton(onPressed: _load, child: Text(l10n.retry)),
],
),
),
body: EmptyState(
key: const Key('drama-detail-empty'),
icon: Icons.video_library_outlined,
title: l10n.noEpisodesYet,
subtitle: l10n.emptyHint,
actionLabel: l10n.retry,
onAction: _load,
),
);
}
+5 -5
View File
@@ -12,6 +12,7 @@ import '../../core/formatting/error_text.dart';
import '../../core/platform/haptics.dart';
import '../../core/theme/app_theme.dart';
import '../../core/widgets/auth_gate.dart';
import '../../core/widgets/empty_state.dart';
import '../../core/widgets/library_drama_tile.dart';
import '../../l10n/app_localizations.dart';
import '../drama_detail/drama_detail_view.dart';
@@ -101,12 +102,11 @@ class _FavoritesBody extends StatelessWidget {
);
}
if (favorites.cards.isEmpty) {
return Center(
return EmptyState(
key: const Key('favorites-empty'),
child: Text(
l10n.noFavorites,
style: const TextStyle(color: Colors.white70),
),
icon: Icons.favorite_border_rounded,
title: l10n.noFavorites,
subtitle: l10n.emptyHint,
);
}
return RefreshIndicator(
+22 -2
View File
@@ -14,6 +14,7 @@ import '../../core/platform/haptics.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_theme.dart';
import '../../core/widgets/auth_gate.dart';
import '../../core/widgets/empty_state.dart';
import '../../l10n/app_localizations.dart';
import '../discover/discover_view.dart';
import '../mine/mine_view.dart';
@@ -46,14 +47,20 @@ class HomePage extends StatelessWidget {
body: Center(child: CircularProgressIndicator()),
);
}
if (catalog.error != null || catalog.dramas.isEmpty) {
if (catalog.error != null) {
return Scaffold(
backgroundColor: Colors.black,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(catalog.error == null ? l10n.noStories : l10n.catalogError),
const Icon(
Icons.error_outline_rounded,
color: Colors.white70,
size: 40,
),
const SizedBox(height: 12),
Text(l10n.catalogError),
const SizedBox(height: 12),
FilledButton(onPressed: catalog.load, child: Text(l10n.retry)),
if (kDebugMode) ...[
@@ -68,6 +75,19 @@ class HomePage extends StatelessWidget {
),
);
}
if (catalog.dramas.isEmpty) {
return Scaffold(
backgroundColor: Colors.black,
body: EmptyState(
key: const Key('home-empty'),
icon: Icons.movie_outlined,
title: l10n.noStories,
subtitle: l10n.emptyHint,
actionLabel: l10n.retry,
onAction: catalog.load,
),
);
}
return _HomeTabs(
key: ValueKey(catalog.dramas.first.id),
+11 -9
View File
@@ -12,6 +12,7 @@ import '../../core/data/services/auth_controller.dart';
import '../../core/data/services/interaction_controller.dart';
import '../../core/data/services/favorite_controller.dart';
import '../../core/formatting/error_text.dart';
import '../../core/widgets/empty_state.dart';
import '../../core/widgets/remote_image.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_theme.dart';
@@ -670,12 +671,11 @@ class _MyPageState extends State<MyPage> {
const SizedBox(height: 9),
]
else
Padding(
padding: const EdgeInsets.symmetric(vertical: 22),
child: Text(
l10n.noSavedShows,
style: const TextStyle(color: Color(0xFFE6E1E3)),
),
EmptyState(
key: const Key('mine-saved-empty'),
icon: Icons.bookmark_border_rounded,
title: l10n.noSavedShows,
subtitle: l10n.emptyHint,
),
// 最近观看(服务端 /app/watch/recent):只渲染能在已加载目录里匹配到的剧;
// 匹配不到的(例如不在当前首页分区里)先不显示,K2 的记录页会做独立请求。
@@ -732,9 +732,11 @@ class _MyPageState extends State<MyPage> {
],
)
else if (_recentDramas.isEmpty)
Text(
l10n.noStories,
style: const TextStyle(color: Color(0xFFB7B3B8)),
EmptyState(
key: const Key('mine-recent-empty'),
icon: Icons.history_rounded,
title: l10n.noStories,
subtitle: l10n.emptyHint,
)
else
for (final drama in _recentDramas) ...[
+5 -5
View File
@@ -12,6 +12,7 @@ import '../../core/formatting/error_text.dart';
import '../../core/formatting/localized_date_time.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_theme.dart';
import '../../core/widgets/empty_state.dart';
import '../../l10n/app_localizations.dart';
/// Anonymous system notice list with pagination and detail dialog.
@@ -109,12 +110,11 @@ class _NoticesPageState extends State<NoticesPage> {
children: [
SizedBox(
height: MediaQuery.sizeOf(context).height * 0.7,
child: Center(
child: EmptyState(
key: const Key('notices-empty'),
child: Text(
l10n.noNotices,
style: const TextStyle(color: AppColors.muted),
),
icon: Icons.notifications_none_rounded,
title: l10n.noNotices,
subtitle: l10n.emptyHint,
),
),
],
@@ -14,6 +14,7 @@ import '../../../core/formatting/error_text.dart';
import '../../../core/platform/haptics.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/widgets/auth_gate.dart';
import '../../../core/widgets/empty_state.dart';
import '../../../l10n/app_localizations.dart';
import '../../interaction/widgets/report_dialog.dart';
import 'comment_tile.dart';
@@ -219,12 +220,11 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
);
}
if (_controller.comments.isEmpty) {
return Center(
return EmptyState(
key: const Key('comment-list-empty'),
child: Text(
l10n.noComments,
style: const TextStyle(color: AppColors.muted),
),
icon: Icons.chat_bubble_outline_rounded,
title: l10n.noComments,
compact: true,
);
}
return ListView.builder(
@@ -17,6 +17,7 @@ import '../../core/platform/haptics.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_theme.dart';
import '../../core/widgets/auth_gate.dart';
import '../../core/widgets/empty_state.dart';
import '../../core/widgets/remote_image.dart';
import '../../l10n/app_localizations.dart';
import '../drama_detail/drama_detail_view.dart';
@@ -92,11 +93,11 @@ class _WatchRecordsBodyState extends State<_WatchRecordsBody> {
);
}
if (controller.records.isEmpty) {
return Center(
child: Text(
l10n.noWatchRecords,
style: const TextStyle(color: Colors.white70),
),
return EmptyState(
key: const Key('watch-records-empty'),
icon: Icons.history_rounded,
title: l10n.noWatchRecords,
subtitle: l10n.emptyHint,
);
}
return Column(