feat(app): 客户端展示多语言并支持筛选分页

This commit is contained in:
Zhou Weidong committed 2026-10-24 17:20:24 +08:00
1 parent 8481611372
commit 8b50b5bd06
68 files changed
+2742 -1120

No files matched your search

+17 -180
View File
@@ -23,30 +23,18 @@ import '../../core/widgets/catalog_debug_info.dart';
import '../../core/widgets/catalog_endpoint_editor.dart';
import '../../core/widgets/empty_state.dart';
import '../../l10n/app_localizations.dart';
import '../filter/filter_page.dart';
import '../new_releases/new_releases_page.dart';
import '../player/open_player.dart';
import '../ranking/ranking_page.dart';
import '../reserve/reserve_page.dart';
import 'discover_binding.dart';
import 'discover_logic.dart';
import 'discover_types.dart';
import 'widgets/discover_category_tab.dart';
import 'widgets/discover_header.dart';
import 'widgets/discover_search_results.dart';
import 'widgets/discover_toolbar.dart';
import 'widgets/drama_grid.dart';
const Color _filterIconColor = AppColors.blueSoft;
const Color _newIconColor = AppColors.blueSoft;
const double _toolbarHeight = 36;
const double _toolbarGap = 10;
const double _toolbarRadius = 10;
const double _categoryTabHeight = 44;
const double _categoryTabGap = 20;
const double _categorySelectedFontSize = 16;
const double _categoryFontSize = 15;
const double _categoryUnderlineWidth = 20;
const double _categoryUnderlineHeight = 3;
const double _categoryUnderlineStrokeWidth = 2;
const double _stickyHeaderHeight = 44;
const Duration _stickyHeaderTransition = Duration(milliseconds: 160);
const Duration _categoryScrollDuration = Duration(milliseconds: 200);
@@ -295,7 +283,7 @@ class _DiscoverPageState extends State<DiscoverPage> {
List<Widget> _theaterBlocks(AppLocalizations l10n) {
final dramas = theater.dramas;
return [
_toolbar(l10n),
_toolbar(),
if (dramas.isEmpty)
Padding(
padding: const EdgeInsets.symmetric(vertical: 40),
@@ -318,6 +306,16 @@ class _DiscoverPageState extends State<DiscoverPage> {
child: CircularProgressIndicator(strokeWidth: 2),
),
),
)
else if (dramas.isNotEmpty && !theater.hasMore)
Padding(
key: const Key('paged-list-no-more'),
padding: const EdgeInsets.symmetric(vertical: 16),
child: Text(
l10n.noMoreItems,
textAlign: TextAlign.center,
style: const TextStyle(color: AppColors.textHint, fontSize: 12),
),
),
];
}
@@ -335,8 +333,7 @@ class _DiscoverPageState extends State<DiscoverPage> {
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
_categoryTab(
key: const Key('theater-category-all'),
DiscoverCategoryTab(
anchorKey: _anchorFor('all'),
id: 'all',
label: l10n.all,
@@ -345,8 +342,7 @@ class _DiscoverPageState extends State<DiscoverPage> {
),
for (final category in categories.categories) ...[
const SizedBox(width: _categoryTabGap),
_categoryTab(
key: Key('theater-category-${category.id}'),
DiscoverCategoryTab(
anchorKey: _anchorFor(category.id),
id: category.id,
label: category.name,
@@ -360,52 +356,6 @@ class _DiscoverPageState extends State<DiscoverPage> {
);
}
Widget _categoryTab({
required Key key,
required GlobalKey anchorKey,
required String id,
required String label,
required bool selected,
required VoidCallback onTap,
}) {
return KeyedSubtree(
key: anchorKey,
child: GestureDetector(
key: key,
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
label,
maxLines: 1,
style: TextStyle(
color: selected ? AppColors.onSurface : AppColors.muted,
fontSize: selected
? _categorySelectedFontSize
: _categoryFontSize,
fontWeight: selected ? FontWeight.w800 : FontWeight.w500,
),
),
const SizedBox(height: 4),
CustomPaint(
key: Key('theater-category-underline-$id'),
size: const Size(
_categoryUnderlineWidth,
_categoryUnderlineHeight,
),
painter: CategoryUnderlinePainter(
color: selected ? AppColors.blue : AppColors.transparent,
),
),
],
),
),
);
}
GlobalKey _anchorFor(String id) {
return _categoryAnchors.putIfAbsent(id, GlobalKey.new);
}
@@ -444,97 +394,8 @@ class _DiscoverPageState extends State<DiscoverPage> {
return true;
}
Widget _toolbar(AppLocalizations l10n) {
return Padding(
padding: const EdgeInsets.fromLTRB(14, 2, 14, 12),
child: Row(
children: [
Expanded(
child: _toolButton(
key: const Key('theater-toolbar-filter'),
icon: Icons.tune_rounded,
color: _filterIconColor,
label: l10n.filter,
onTap: () => _openPage(context, const FilterPage()),
),
),
const SizedBox(width: _toolbarGap),
Expanded(
child: _toolButton(
key: const Key('theater-toolbar-ranking'),
icon: Icons.local_fire_department_rounded,
color: AppColors.blue,
label: l10n.rankingTab,
onTap: () => _openPage(context, const RankingPage()),
),
),
const SizedBox(width: _toolbarGap),
Expanded(
child: _toolButton(
key: const Key('theater-toolbar-new'),
icon: Icons.play_circle_fill_rounded,
color: _newIconColor,
label: l10n.newDramas,
onTap: () => _openPage(context, const NewReleasesPage()),
),
),
const SizedBox(width: _toolbarGap),
Expanded(
child: _toolButton(
key: const Key('theater-toolbar-reserve'),
icon: Icons.calendar_month_rounded,
color: AppColors.blue,
label: l10n.reserve,
onTap: () => _openPage(context, const ReservePage()),
),
),
],
),
);
}
Widget _toolButton({
required Key key,
required IconData icon,
required Color color,
required String label,
required VoidCallback onTap,
}) {
return Material(
color: AppColors.card,
borderRadius: BorderRadius.circular(_toolbarRadius),
child: InkWell(
key: key,
onTap: onTap,
borderRadius: BorderRadius.circular(_toolbarRadius),
child: Container(
height: _toolbarHeight,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(_toolbarRadius),
border: Border.all(color: AppColors.border),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 16, color: color),
const SizedBox(width: 4),
Flexible(
child: Text(
label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: AppColors.onSurface,
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
),
],
),
),
),
);
Widget _toolbar() {
return DiscoverToolbar(onOpen: (page) => _openPage(context, page));
}
void _openPage(BuildContext context, Widget page) {
@@ -601,27 +462,3 @@ class _DiscoverPageState extends State<DiscoverPage> {
AppRootTab.mine => DiscoverPageAction.mine,
};
}
/// 分类 tab 的弧形选中线。
class CategoryUnderlinePainter extends CustomPainter {
const CategoryUnderlinePainter({required this.color});
final Color color;
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..style = PaintingStyle.stroke
..strokeWidth = _categoryUnderlineStrokeWidth
..strokeCap = StrokeCap.round;
final path = Path()
..moveTo(1, 1)
..quadraticBezierTo(size.width / 2, size.height + 1, size.width - 1, 1);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CategoryUnderlinePainter oldDelegate) =>
color != oldDelegate.color;
}