fix(ui): 列表剧名统一两行省略,收藏卡去掉观看进度副标题

筛选、追剧收藏、新片、榜单、缓存与推荐上新等列表卡都按两行剧名预留高度,
超长标题显示省略号;收藏卡不再显示「看到第 N 集」,文字区高度同步收窄。
This commit is contained in:
Vicky authored and trae-solo-agent committed 2026-10-29 15:44:30 +08:00
1 parent 4dd37b2588
commit c311985601
9 files changed
+31 -48

No files matched your search

+5 -5
View File
@@ -150,8 +150,8 @@ abstract final class AppDimens {
static const double recommendSectionTitle = 17; static const double recommendSectionTitle = 17;
static const double recommendSectionSubtitle = 11; static const double recommendSectionSubtitle = 11;
static const double recommendSectionMore = 12; static const double recommendSectionMore = 12;
// 封面 122*(4/3)≈163 + 间距6 + 两行标题39 + 间距6 + 标签13 ≈ 227。 // 封面 122*(4/3)≈163 + 间距6 + 两行标题≈42 + 间距6 + 标签≈13 ≈ 230,留 6 余量。
static const double recommendRowHeight = 228; static const double recommendRowHeight = 236;
static const double recommendCardWidth = 122; static const double recommendCardWidth = 122;
/// 首页热播榜。 /// 首页热播榜。
@@ -189,9 +189,9 @@ abstract final class AppDimens {
static const double followingAddIconStroke = 1.5; static const double followingAddIconStroke = 1.5;
static const double followingGridGap = 6; static const double followingGridGap = 6;
static const double followingGridRowGap = 12; static const double followingGridRowGap = 12;
// 单行标题 16*1.3=20.8 + 间距 6+4 + 副标题行高约 18.6 ≈ 49.4,留 0.6 余量;
// 按 48 估算在副标题行高偏高的字体下会溢出 1~2px。 /// 追剧网格卡文字区:间距 6 + 两行剧名 16*1.3*2=41.6 ≈ 48,留 4 余量。
static const double followingCardTextExtent = 50; static const double followingCardTextExtent = 52;
static const double followingSubtitleFont = 12; static const double followingSubtitleFont = 12;
static const double followingSubtitleGap = 4; static const double followingSubtitleGap = 4;
static const double followingProgressHeight = 2; static const double followingProgressHeight = 2;
+1 -1
View File
@@ -374,7 +374,7 @@ class _LicenseTile extends StatelessWidget {
Expanded( Expanded(
child: Text( child: Text(
license.dramaTitle, license.dramaTitle,
maxLines: 1, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle( style: const TextStyle(
color: AppColors.onSurface, color: AppColors.onSurface,
+3 -1
View File
@@ -23,7 +23,9 @@ const double _optionHorizontal = 5;
const double _optionVertical = 5; const double _optionVertical = 5;
const double _optionRunGap = 4; const double _optionRunGap = 4;
const double _cardGap = 14; const double _cardGap = 14;
const double _gridAspectRatio = 0.56;
/// 卡片比例:三列网格里要装下封面 + 两行剧名 + 元信息。
const double _gridAspectRatio = 0.53;
const double _groupFontSize = 13; const double _groupFontSize = 13;
const double _labelFontSize = 12; const double _labelFontSize = 12;
@@ -88,7 +88,7 @@ class FilterResultCard extends StatelessWidget {
Text( Text(
drama.title, drama.title,
key: Key('filter-title-${drama.id}'), key: Key('filter-title-${drama.id}'),
maxLines: 1, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle( style: const TextStyle(
color: AppColors.onSurface, color: AppColors.onSurface,
@@ -167,18 +167,10 @@ class FollowingFavoritesGrid extends StatelessWidget {
bool isEditing, bool isEditing,
Set<String> picked, Set<String> picked,
) { ) {
final l10n = AppLocalizations.of(context)!;
if (index == controller.cards.length) { if (index == controller.cards.length) {
return FollowingAddCard(onTap: onGoHome); return FollowingAddCard(onTap: onGoHome);
} }
final drama = controller.cards[index]; final drama = controller.cards[index];
final record = history.recordForDrama(drama.id);
final episode = drama.episodes.isEmpty ? 1 : drama.episodes.first.number;
final watchedEpisode = record?.episodeNumber ?? 0;
final total = drama.episodeCount;
final subtitle = watchedEpisode > 0 && total > 0
? l10n.watchedEpisodeTotal(watchedEpisode, total)
: l10n.favoriteEpisodeProgress(episode, total);
return FollowingGridCard( return FollowingGridCard(
dramaId: drama.id, dramaId: drama.id,
title: drama.localizedTitle(Localizations.localeOf(context).languageCode), title: drama.localizedTitle(Localizations.localeOf(context).languageCode),
@@ -186,10 +178,7 @@ class FollowingFavoritesGrid extends StatelessWidget {
viewCount: drama.viewCount, viewCount: drama.viewCount,
editing: isEditing, editing: isEditing,
selected: isEditing ? picked.contains(drama.id) : null, selected: isEditing ? picked.contains(drama.id) : null,
subtitle: subtitle, // TODO(backend): Drama 增加 hasUpdate 字段后,在此传 updateBadge。
// TODO(backend): Drama 增加 hasUpdate / latestEpisodeNumber / isFinished
// 字段后,在此传 updateBadge(l10n.hasUpdateBadge)、红色更新副标题
// (l10n.updatedToEpisode)或灰色完结副标题(l10n.finishedAllEpisodes)。
onTap: () => _openOrToggle(context, drama.id, isEditing), onTap: () => _openOrToggle(context, drama.id, isEditing),
); );
} }
+1 -18
View File
@@ -19,21 +19,18 @@ class FollowingGridCard extends StatelessWidget {
required this.title, required this.title,
required this.posterUrl, required this.posterUrl,
required this.viewCount, required this.viewCount,
required this.subtitle,
required this.onTap, required this.onTap,
this.progress, this.progress,
this.editing = false, this.editing = false,
this.selected, this.selected,
this.onRemove, this.onRemove,
this.updateBadge, this.updateBadge,
this.subtitleColor,
}); });
final String dramaId; final String dramaId;
final String title; final String title;
final String posterUrl; final String posterUrl;
final int viewCount; final int viewCount;
final String subtitle;
final VoidCallback onTap; final VoidCallback onTap;
final double? progress; final double? progress;
final bool editing; final bool editing;
@@ -44,9 +41,6 @@ class FollowingGridCard extends StatelessWidget {
/// 待后端在 Drama 增加更新状态字段后由收藏网格传入。 /// 待后端在 Drama 增加更新状态字段后由收藏网格传入。
final String? updateBadge; final String? updateBadge;
/// 副标题颜色;更新中用强调红,完结用默认灰。
final Color? subtitleColor;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return InkWell( return InkWell(
@@ -165,7 +159,7 @@ class FollowingGridCard extends StatelessWidget {
const SizedBox(height: AppDimens.cardCoverTitleGap), const SizedBox(height: AppDimens.cardCoverTitleGap),
Text( Text(
title, title,
maxLines: 1, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
textAlign: TextAlign.left, textAlign: TextAlign.left,
style: const TextStyle( style: const TextStyle(
@@ -175,17 +169,6 @@ class FollowingGridCard extends StatelessWidget {
height: AppDimens.cardTitleLineHeight, height: AppDimens.cardTitleLineHeight,
), ),
), ),
const SizedBox(height: AppDimens.followingSubtitleGap),
Text(
subtitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.left,
style: TextStyle(
color: subtitleColor ?? AppColors.textTertiary,
fontSize: AppDimens.followingCardSubtitleFontNew,
),
),
], ],
), ),
); );
@@ -22,7 +22,9 @@ const int _moreLimit = 12;
const int _filmHoles = 8; const int _filmHoles = 8;
const double _cardPadding = 8; const double _cardPadding = 8;
const double _cardWidth = 124; const double _cardWidth = 124;
const double _rowHeight = 204;
/// 横向卡片行高:封面 144 + 两行剧名 + 上映日期。
const double _rowHeight = 226;
const double _cardRadius = 12; const double _cardRadius = 12;
const double _loadMoreThreshold = 200; const double _loadMoreThreshold = 200;
@@ -173,9 +175,7 @@ class _NewReleasesPageState extends State<NewReleasesPage> {
child: SizedBox(height: 14), child: SizedBox(height: 14),
), ),
if (more.isNotEmpty) ...[ if (more.isNotEmpty) ...[
SliverToBoxAdapter( SliverToBoxAdapter(child: _moreHeader(l10n)),
child: _moreHeader(l10n),
),
const SliverToBoxAdapter( const SliverToBoxAdapter(
child: SizedBox(height: 10), child: SizedBox(height: 10),
), ),
@@ -467,7 +467,7 @@ class _NewReleasesPageState extends State<NewReleasesPage> {
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
drama.title, drama.title,
maxLines: 1, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle( style: const TextStyle(
color: AppColors.onSurface, color: AppColors.onSurface,
+1 -1
View File
@@ -464,7 +464,7 @@ class _RankingRow extends StatelessWidget {
children: [ children: [
Text( Text(
drama.title, drama.title,
maxLines: 1, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle( style: const TextStyle(
color: AppColors.textPrimary, color: AppColors.textPrimary,
@@ -15,6 +15,12 @@ const double _cardCoverHeight = 157;
const double _cardGap = 9; const double _cardGap = 9;
const double _cardRadius = 12; const double _cardRadius = 12;
/// 海报卡封面以下的高度:上间距 8 + 两行剧名 + 2 + 集数行 + 余量。
///
/// 横向列表和网格都要给一个有限的卡片高度,这里是唯一来源,
/// 两行剧名撑高时只改这一处,不会出现某个入口高度不够而溢出。
const double homePosterCardBodyExtent = 62;
/// 上新横滑卡板块。无板块标题,卡片左上角带「上新」角标。 /// 上新横滑卡板块。无板块标题,卡片左上角带「上新」角标。
/// 内层横向 ListView 会自行识别横向手势,不与外层纵向滚动冲突。 /// 内层横向 ListView 会自行识别横向手势,不与外层纵向滚动冲突。
class HomeNewArrivalRow extends StatelessWidget { class HomeNewArrivalRow extends StatelessWidget {
@@ -32,8 +38,7 @@ class HomeNewArrivalRow extends StatelessWidget {
if (dramas.isEmpty) return const SizedBox.shrink(); if (dramas.isEmpty) return const SizedBox.shrink();
return SizedBox( return SizedBox(
key: const Key('home-new-arrival-row'), key: const Key('home-new-arrival-row'),
height: height: _cardCoverHeight + homePosterCardBodyExtent,
_cardCoverHeight + 8 + 20 + 2 + 16, // 封面 + 剧名行 + 集数行 + 余量
child: ListView.separated( child: ListView.separated(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: _pageSide), padding: const EdgeInsets.symmetric(horizontal: _pageSide),
@@ -130,7 +135,7 @@ class HomePosterCard extends StatelessWidget {
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
drama.localizedTitle(lang), drama.localizedTitle(lang),
maxLines: 1, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle( style: const TextStyle(
color: AppColors.textPrimary, color: AppColors.textPrimary,
@@ -192,7 +197,11 @@ class HomeEditorPickBanner extends StatelessWidget {
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.centerLeft, begin: Alignment.centerLeft,
end: Alignment.centerRight, end: Alignment.centerRight,
colors: [Color(0xE6000000), Color(0x4D000000), Color(0x1A000000)], colors: [
Color(0xE6000000),
Color(0x4D000000),
Color(0x1A000000),
],
), ),
), ),
), ),