feat(flutter): 去掉剧场封面角标并重做创作页

This commit is contained in:
Zhou Weidong committed 2026-10-20 15:21:54 +08:00
1 parent 72410b2a87
commit 6e9a93c920
11 files changed
+277 -74

No files matched your search

+233 -50
View File
@@ -3,11 +3,19 @@
import 'package:flutter/material.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_dimens.dart';
import '../../core/theme/app_theme.dart';
import '../../l10n/app_localizations.dart';
/// K28-B 创作页:AI 制作与上传发布入口。
const double _cardHeight = 196;
const double _cardRadius = 20;
const double _arrowSize = 36;
const double _illustrationWidth = 156;
const double _illustrationHeight = 88;
const double _mottoLineWidth = 96;
const List<Color> _aiGradientStart = [Color(0xFFFFE3D0), Color(0xFFFFC9AC)];
const List<Color> _uploadGradient = [Color(0xFFFFEAD9), Color(0xFFFFD3B3)];
/// 创作页:AI 制作与上传发布两张主卡,底部品牌标语。
class CreatePage extends StatelessWidget {
const CreatePage({super.key});
@@ -20,46 +28,72 @@ class CreatePage extends StatelessWidget {
bottom: false,
child: ListView(
key: const Key('create-page'),
padding: const EdgeInsets.fromLTRB(
AppDimens.pageHorizontal,
AppDimens.spaceXl,
AppDimens.pageHorizontal,
AppDimens.spaceXl,
),
padding: const EdgeInsets.fromLTRB(16, 28, 16, 24),
children: [
Text(
l10n.createTab,
style: const TextStyle(
fontFamily: 'Outfit',
color: AppColors.onSurface,
fontSize: AppDimens.heroTitle,
fontSize: 28,
fontWeight: FontWeight.w800,
),
),
const SizedBox(height: AppDimens.spaceSm),
const SizedBox(height: 6),
Text(
l10n.createSubtitle,
style: const TextStyle(
color: AppColors.muted,
fontSize: AppDimens.body,
),
style: const TextStyle(color: AppColors.muted, fontSize: 13),
),
const SizedBox(height: AppDimens.spaceXl),
_CreateEntry(
const SizedBox(height: 24),
_CreateCard(
key: const Key('create-ai'),
icon: Icons.auto_awesome_rounded,
title: l10n.createAiTitle,
subtitle: l10n.createAiSubtitle,
illustration: const _AiIllustration(),
onTap: () => _openPlaceholder(context, l10n.createAiTitle),
),
const SizedBox(height: AppDimens.spaceMd),
_CreateEntry(
const SizedBox(height: 16),
_CreateCard(
key: const Key('create-upload'),
icon: Icons.upload_file_rounded,
title: l10n.createUploadTitle,
subtitle: l10n.createUploadSubtitle,
illustration: const _UploadIllustration(),
onTap: () => _openPlaceholder(context, l10n.createUploadTitle),
),
const SizedBox(height: 32),
Text(
l10n.createMottoPrimary,
key: const Key('create-motto-primary'),
textAlign: TextAlign.center,
style: const TextStyle(
color: AppColors.onSurface,
fontSize: 18,
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 4),
Text(
l10n.createMottoSecondary,
key: const Key('create-motto-secondary'),
textAlign: TextAlign.center,
style: const TextStyle(
color: AppColors.onSurface,
fontSize: 18,
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 10),
Center(
child: Container(
key: const Key('create-motto-line'),
width: _mottoLineWidth,
height: 2,
decoration: BoxDecoration(
color: AppColors.blue,
borderRadius: BorderRadius.circular(1),
),
),
),
],
),
),
@@ -73,18 +107,18 @@ class CreatePage extends StatelessWidget {
}
}
class _CreateEntry extends StatelessWidget {
const _CreateEntry({
class _CreateCard extends StatelessWidget {
const _CreateCard({
super.key,
required this.icon,
required this.title,
required this.subtitle,
required this.illustration,
required this.onTap,
});
final IconData icon;
final String title;
final String subtitle;
final Widget illustration;
final VoidCallback onTap;
@override
@@ -92,57 +126,66 @@ class _CreateEntry extends StatelessWidget {
return Container(
decoration: BoxDecoration(
color: AppColors.card,
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
borderRadius: BorderRadius.circular(_cardRadius),
boxShadow: const [AppTheme.cardShadow],
),
child: Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
borderRadius: BorderRadius.circular(_cardRadius),
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(AppDimens.cardPadding),
child: Row(
child: SizedBox(
height: _cardHeight,
child: Stack(
children: [
Container(
width: AppDimens.buttonLarge,
height: AppDimens.buttonLarge,
decoration: BoxDecoration(
color: AppColors.badgePink,
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
),
child: Icon(icon, color: AppColors.secondary, size: 22),
),
const SizedBox(width: AppDimens.spaceMd),
Expanded(
Padding(
padding: const EdgeInsets.fromLTRB(20, 18, 68, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: AppColors.onSurface,
fontSize: AppDimens.listTitle,
fontWeight: FontWeight.w700,
fontSize: 22,
fontWeight: FontWeight.w900,
),
),
const SizedBox(height: AppDimens.spaceXs),
const SizedBox(height: 6),
Text(
subtitle,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: AppColors.muted,
fontSize: AppDimens.caption,
fontSize: 13,
),
),
],
),
),
const Icon(
Icons.chevron_right_rounded,
color: AppColors.subtle,
size: 20,
Positioned(
top: 16,
right: 16,
child: Container(
width: _arrowSize,
height: _arrowSize,
decoration: const BoxDecoration(
color: AppColors.raised,
shape: BoxShape.circle,
),
child: const Icon(
Icons.arrow_forward_rounded,
color: AppColors.blue,
size: 18,
),
),
),
Positioned(right: 14, bottom: 14, child: illustration),
],
),
),
@@ -152,6 +195,146 @@ class _CreateEntry extends StatelessWidget {
}
}
class _AiIllustration extends StatelessWidget {
const _AiIllustration();
@override
Widget build(BuildContext context) {
return Container(
width: _illustrationWidth,
height: _illustrationHeight,
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: _aiGradientStart,
),
borderRadius: BorderRadius.circular(16),
),
child: Stack(
children: [
const Positioned(
left: 12,
top: 10,
child: Icon(
Icons.auto_awesome_rounded,
color: AppColors.secondary,
size: 30,
),
),
const Positioned(
right: 14,
top: 8,
child: Icon(
Icons.auto_awesome_rounded,
color: AppColors.blue,
size: 12,
),
),
Positioned(
right: 32,
top: 30,
child: Container(
width: 5,
height: 5,
decoration: BoxDecoration(
color: AppColors.blue.withValues(alpha: 0.55),
shape: BoxShape.circle,
),
),
),
const Positioned(
left: 12,
bottom: 10,
child: Text(
'Every Story Matters',
style: TextStyle(
color: AppColors.muted,
fontSize: 10,
fontWeight: FontWeight.w700,
),
),
),
],
),
);
}
}
class _UploadIllustration extends StatelessWidget {
const _UploadIllustration();
@override
Widget build(BuildContext context) {
return Container(
width: _illustrationWidth,
height: _illustrationHeight,
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: _uploadGradient,
),
borderRadius: BorderRadius.circular(16),
),
child: Stack(
children: [
const Positioned(
left: 12,
top: 8,
child: Icon(
Icons.cloud_upload_rounded,
color: AppColors.secondary,
size: 30,
),
),
Positioned(
right: 16,
top: 12,
child: Container(
width: 30,
height: 20,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.55),
borderRadius: BorderRadius.circular(4),
),
child: const Icon(
Icons.movie_creation_outlined,
color: AppColors.muted,
size: 12,
),
),
),
Positioned(
right: 52,
top: 34,
child: Container(
width: 5,
height: 5,
decoration: BoxDecoration(
color: AppColors.blue.withValues(alpha: 0.55),
shape: BoxShape.circle,
),
),
),
const Positioned(
left: 12,
bottom: 10,
child: Text(
'Ready to publish',
style: TextStyle(
color: AppColors.muted,
fontSize: 10,
fontWeight: FontWeight.w700,
),
),
),
],
),
);
}
}
class _CreatePlaceholderPage extends StatelessWidget {
const _CreatePlaceholderPage({required this.title});
@@ -173,12 +356,12 @@ class _CreatePlaceholderPage extends StatelessWidget {
color: AppColors.blue,
size: 42,
),
const SizedBox(height: AppDimens.spaceMd),
const SizedBox(height: 12),
Text(
l10n.comingSoon,
style: const TextStyle(
color: AppColors.onSurface,
fontSize: AppDimens.sectionTitle,
fontSize: 16,
fontWeight: FontWeight.w700,
),
),
@@ -23,7 +23,6 @@ const double _tagChipFontSize = 11;
const double _heatFontSize = 9;
const double _heatIconSize = 11;
const int _maxTags = 3;
const Color _freeBadgeColor = Color(0xFF12B76A);
const Color _heatBackground = Color(0x99000000);
const Color _tagChipBackground = Color(0xFFF4F1EE);
final RegExp _tagSplitter = RegExp(r'[,/、·]');
@@ -86,9 +85,6 @@ class DramaGridCard extends StatelessWidget {
final Drama drama;
final VoidCallback onTap;
bool get _isFree =>
drama.freeEpisodes > 0 || drama.episodes.any((episode) => episode.isFree);
@override
Widget build(BuildContext context) {
return GestureDetector(
@@ -118,7 +114,6 @@ class DramaGridCard extends StatelessWidget {
fit: StackFit.expand,
children: [
_DiscoverCoverImage(path: drama.posterUrl),
Positioned(top: 8, left: 8, child: _tagBadge()),
Positioned(left: 8, bottom: 8, child: _heatBadge()),
],
),
@@ -126,25 +121,6 @@ class DramaGridCard extends StatelessWidget {
);
}
Widget _tagBadge() {
return Container(
key: Key('drama-tag-${drama.id}'),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: _freeBadgeColor,
borderRadius: BorderRadius.circular(AppDimens.pillRadius),
),
child: Text(
_isFree ? l10n.free : l10n.originalTag,
style: const TextStyle(
color: AppColors.onPrimary,
fontSize: 10,
fontWeight: FontWeight.w700,
),
),
);
}
Widget _heatBadge() {
return Container(
key: Key('drama-heat-${drama.id}'),