feat(flutter): 应用浅色暖橙视觉主题
This commit is contained in:
1 parent
0d2367431d
commit
634b63e16c
56 files changed
+941
-409
No files matched your search
@@ -27,7 +27,7 @@ class CategoryDramasPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
return Theme(
|
||||
data: AppTheme.dark(),
|
||||
data: AppTheme.light(),
|
||||
child: Scaffold(
|
||||
key: const Key('category-dramas-page'),
|
||||
backgroundColor: AppColors.ink,
|
||||
|
||||
@@ -108,9 +108,9 @@ class _DiscoverPageState extends State<DiscoverPage> {
|
||||
),
|
||||
);
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle.light,
|
||||
value: SystemUiOverlayStyle.dark,
|
||||
child: Theme(
|
||||
data: AppTheme.dark(),
|
||||
data: AppTheme.light(),
|
||||
child: embedded
|
||||
? content
|
||||
: Scaffold(backgroundColor: AppColors.ink, body: content),
|
||||
@@ -340,6 +340,10 @@ class _DiscoverPageState extends State<DiscoverPage> {
|
||||
}
|
||||
|
||||
Widget _bottomNav(AppLocalizations l10n) => AppBottomNav(
|
||||
centerAction: AppBottomNavCenterAction(
|
||||
label: l10n.createTab,
|
||||
onTap: () => _act(DiscoverPageAction.create),
|
||||
),
|
||||
items: [
|
||||
AppBottomNavItem(
|
||||
tabKey: const Key('discover-nav-home'),
|
||||
|
||||
@@ -88,12 +88,12 @@ class DiscoverHeader extends StatelessWidget {
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [Color(0xFF6A5CFF), AppColors.blue],
|
||||
colors: [AppColors.secondary, AppColors.blue],
|
||||
),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.person_rounded,
|
||||
color: AppColors.onSurface,
|
||||
color: AppColors.onPrimary,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
@@ -124,7 +124,7 @@ class _HeaderIcon extends StatelessWidget {
|
||||
onPressed: onTap,
|
||||
padding: EdgeInsets.zero,
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF2C2D33),
|
||||
backgroundColor: AppColors.raised,
|
||||
foregroundColor: AppColors.onSurface,
|
||||
),
|
||||
icon: Icon(icon, size: 18),
|
||||
|
||||
@@ -5,11 +5,15 @@ import 'package:flutter/material.dart';
|
||||
import '../../../core/config/app_config.dart';
|
||||
import '../../../core/data/models/drama.dart';
|
||||
import '../../../core/theme/app_colors.dart';
|
||||
import '../../../core/theme/app_dimens.dart';
|
||||
import '../../../core/theme/app_theme.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
|
||||
const int _columns = 2;
|
||||
const double _columnGap = 10;
|
||||
const double _cardGap = 10;
|
||||
const double _addSize = 28;
|
||||
const double _addIconSize = 18;
|
||||
|
||||
/// 剧场/分类页共用的双列剧目网格。
|
||||
class DramaGrid extends StatelessWidget {
|
||||
@@ -71,19 +75,33 @@ class DramaGridCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
key: Key('home-drama-card-${drama.id}'),
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_cover(),
|
||||
const SizedBox(height: 5),
|
||||
_title(),
|
||||
const SizedBox(height: 2),
|
||||
_genre(),
|
||||
],
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.card,
|
||||
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
|
||||
boxShadow: const [AppTheme.cardShadow],
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: InkWell(
|
||||
key: Key('home-drama-card-${drama.id}'),
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(AppDimens.spaceSm),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_cover(),
|
||||
const SizedBox(height: AppDimens.spaceSm),
|
||||
_title(),
|
||||
const SizedBox(height: AppDimens.spaceXs),
|
||||
_footer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -94,34 +112,90 @@ class DramaGridCard extends StatelessWidget {
|
||||
drama.episodes.any((episode) => episode.isFree);
|
||||
return ClipRRect(
|
||||
key: Key('home-drama-cover-${drama.id}'),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderRadius: BorderRadius.circular(AppDimens.coverRadius),
|
||||
child: Stack(
|
||||
children: [
|
||||
_DiscoverCoverImage(path: drama.posterUrl),
|
||||
if (free) Positioned(top: 6, left: 6, child: _freeBadge()),
|
||||
Positioned(top: 8, left: 8, child: _tagBadge(free)),
|
||||
if (_lockedCount > 0)
|
||||
Positioned(top: 8, right: 8, child: _lockedBadge()),
|
||||
Positioned(right: 8, bottom: 8, child: _episodeBadge()),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _freeBadge() {
|
||||
int get _lockedCount {
|
||||
final explicit = drama.episodes.where((episode) => episode.locked).length;
|
||||
if (explicit > 0) return explicit;
|
||||
final inferred = drama.episodeCount - drama.freeEpisodes;
|
||||
return inferred > 0 ? inferred : 0;
|
||||
}
|
||||
|
||||
Widget _tagBadge(bool free) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
key: Key('drama-tag-${drama.id}'),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.blue,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
color: AppColors.badgePink,
|
||||
borderRadius: BorderRadius.circular(AppDimens.pillRadius),
|
||||
),
|
||||
child: Text(
|
||||
l10n.free,
|
||||
free ? l10n.free : l10n.originalTag,
|
||||
style: const TextStyle(
|
||||
color: AppColors.onPrimary,
|
||||
fontSize: 8,
|
||||
color: AppColors.badgePinkText,
|
||||
fontSize: AppDimens.cardMeta,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _lockedBadge() {
|
||||
return Container(
|
||||
key: Key('drama-locked-${drama.id}'),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.overlayDark,
|
||||
borderRadius: BorderRadius.circular(AppDimens.pillRadius),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.lock_rounded, color: AppColors.onPrimary, size: 11),
|
||||
const SizedBox(width: 2),
|
||||
Text(
|
||||
l10n.lockedEpisodeCount(_lockedCount),
|
||||
style: const TextStyle(
|
||||
color: AppColors.onPrimary,
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _episodeBadge() {
|
||||
return Container(
|
||||
key: Key('drama-episodes-badge-${drama.id}'),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.badgeBeige,
|
||||
borderRadius: BorderRadius.circular(AppDimens.pillRadius),
|
||||
),
|
||||
child: Text(
|
||||
l10n.allEpisodesBadge(drama.episodeCount),
|
||||
style: const TextStyle(
|
||||
color: AppColors.onSurface,
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _title() {
|
||||
return Text(
|
||||
drama.title,
|
||||
@@ -130,7 +204,7 @@ class DramaGridCard extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
color: AppColors.onSurface,
|
||||
fontSize: 11,
|
||||
fontSize: AppDimens.cardTitle,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
);
|
||||
@@ -143,7 +217,32 @@ class DramaGridCard extends StatelessWidget {
|
||||
key: Key('home-drama-genre-${drama.id}'),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(color: Color(0xFFBBBBBB), fontSize: 9),
|
||||
style: const TextStyle(
|
||||
color: AppColors.muted,
|
||||
fontSize: AppDimens.cardMeta,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _footer() {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(child: _genre()),
|
||||
Container(
|
||||
key: Key('drama-add-${drama.id}'),
|
||||
width: _addSize,
|
||||
height: _addSize,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.blue,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.add_rounded,
|
||||
color: AppColors.onPrimary,
|
||||
size: _addIconSize,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ class _RankingSectionState extends State<RankingSection> {
|
||||
final selected = logic.board == board;
|
||||
return Material(
|
||||
key: key,
|
||||
color: selected ? AppColors.blue : AppColors.raised,
|
||||
color: selected ? AppColors.blue : AppColors.card,
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: InkWell(
|
||||
@@ -145,7 +145,7 @@ class _RankingSectionState extends State<RankingSection> {
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: selected ? AppColors.onPrimary : AppColors.muted,
|
||||
color: selected ? AppColors.onPrimary : AppColors.onSurface,
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user