Files
xianyan/lib/features/mine/profile/presentation/learn_us_sections.dart
Developer ae1df22732 feat: v6.10.3 多语言翻译补全 + 17项功能修复
- 引导页协议多语言支持(languageId传递)
- 登录页双书名号修复 + 注册页协议勾选
- 个人中心页面多语言(18个翻译键)
- 网络断开提示增加关闭/刷新按钮
- 了解我们:新增秋叶qy开发者 + ayk签名修改 + 贡献者精简 + 微风暴微信搜索
- iOS快捷按钮重复修复(删除Info.plist静态定义)
- 测试账号123456警告提示
- 扫码登录自动跳转(HTTP轮询+WebSocket双通道)
- 登录页老用户按钮改次要色
- Syncfusion图表崩溃修复(DeferredBuilder+animationDuration:0)
- macOS标题栏跟随软件夜间模式
- 平台兼容分发渠道弹窗
- 软件著作权图片+交叉水印
- 桌面小部件平台兼容说明默认收起
- iOS/macOS图标更新+名称确认为闲言
- 12个语言文件补全roleNative+7个分发渠道翻译字段
2026-06-02 04:50:32 +08:00

1094 lines
34 KiB
Dart

/// ============================================================
/// 闲言APP — 了解我们页面(分区组件)
/// 创建时间: 2026-05-29
/// 更新时间: 2026-06-02
/// 作用: 官方网站、开发者、团队、QQ群、贡献者与特别鸣谢等分区
/// 上次更新: 团队新增原生栈成员,简化贡献者和特别鸣谢列表
/// ============================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import '../../../../core/theme/app_theme.dart';
import '../../../../core/theme/app_spacing.dart';
import '../../../../core/theme/app_typography.dart';
import '../../../../core/theme/app_radius.dart';
import '../../../../core/constants/app_constants.dart';
import '../../../../shared/widgets/containers/glass_container.dart';
import '../../../../l10n/translations.dart';
import 'about_shared_widgets.dart';
import 'learn_us_widgets.dart';
// ============================================================
// 官方网站区域
// ============================================================
class OfficialSiteSection extends StatelessWidget {
const OfficialSiteSection({super.key, required this.ext, required this.t});
final AppThemeExtension ext;
final T t;
@override
Widget build(BuildContext context) {
final links = [
LinkItemData(
icon: CupertinoIcons.globe,
label: t.about.officialSite,
url: 'https://s2ss.com/',
),
LinkItemData(
icon: CupertinoIcons.doc_text,
label: t.about.downloadPage,
url: 'https://s2ss.com/download',
),
];
return GlassContainer(
depth: GlassDepth.elevated,
padding: EdgeInsets.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AboutSectionTitle(
icon: CupertinoIcons.globe,
title: t.about.officialSiteTitle,
ext: ext,
),
Padding(
padding: const EdgeInsets.fromLTRB(
AppSpacing.md,
0,
AppSpacing.md,
AppSpacing.sm,
),
child: Text(
t.about.officialSiteDesc,
style: AppTypography.caption1.copyWith(color: ext.textHint),
),
),
...links.map(
(link) => Column(
children: [
LinkTile(link: link, ext: ext, t: t),
if (link != links.last) AboutDivider(ext: ext, leftIndent: 36),
],
),
),
AboutDivider(ext: ext, leftIndent: 36),
GestureDetector(
onTap: () => _showProductsSheet(context),
behavior: HitTestBehavior.opaque,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.md,
),
child: Row(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: ext.iconTintYellow.withValues(alpha: 0.12),
borderRadius: AppRadius.smBorder,
),
child: Icon(
CupertinoIcons.square_grid_2x2,
size: 18,
color: ext.iconTintYellow,
),
),
const SizedBox(width: AppSpacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
t.about.otherProducts,
style: AppTypography.body.copyWith(
fontWeight: FontWeight.w500,
color: ext.textPrimary,
),
),
const SizedBox(height: 2),
Text(
'情景诗词 · 小妈厨房',
style: AppTypography.caption2.copyWith(
color: ext.textHint,
),
),
],
),
),
Icon(
CupertinoIcons.chevron_right,
size: 14,
color: ext.textHint,
),
],
),
),
),
],
),
);
}
void _showProductsSheet(BuildContext context) {
final products = [
_ProductData(
emoji: '🌤️',
name: t.about.productPoetry,
icp: '滇ICP备2022000863号-15A',
desc: t.about.productPoetryDesc,
url: 'https://poe.vogov.cn/app.html',
color: ext.iconTintYellow,
),
_ProductData(
emoji: '🍳',
name: t.about.productKitchen,
icp: '滇ICP备2022000863号-16A',
desc: t.about.productKitchenDesc,
url: 'https://eat.wktyl.com/',
color: ext.iconTintMint,
),
];
showCupertinoModalPopup<void>(
context: context,
builder: (ctx) => Container(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(ctx).size.height * 0.65,
),
decoration: BoxDecoration(
color: ext.bgElevated,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(AppRadius.xl),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: AppSpacing.sm),
Container(
width: 36,
height: 4,
decoration: BoxDecoration(
color: ext.textHint.withValues(alpha: 0.3),
borderRadius: AppRadius.smBorder,
),
),
const SizedBox(height: AppSpacing.md),
Padding(
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
child: Row(
children: [
const Text('📦', style: TextStyle(fontSize: 18)),
const SizedBox(width: AppSpacing.sm),
Text(
t.about.otherProducts,
style: AppTypography.title3.copyWith(
fontWeight: FontWeight.w700,
color: ext.textPrimary,
),
),
const Spacer(),
GestureDetector(
onTap: () => Navigator.pop(ctx),
child: Container(
width: 28,
height: 28,
decoration: BoxDecoration(
color: ext.textHint.withValues(alpha: 0.12),
shape: BoxShape.circle,
),
child: Icon(
CupertinoIcons.xmark,
size: 14,
color: ext.textSecondary,
),
),
),
],
),
),
const SizedBox(height: AppSpacing.sm),
Flexible(
child: ListView.separated(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
itemCount: products.length,
separatorBuilder: (_, __) =>
const SizedBox(height: AppSpacing.sm),
itemBuilder: (_, index) => _ProductCard(
product: products[index],
ext: ext,
onTap: () {
Navigator.pop(ctx);
launchAboutUrl(context, products[index].url);
},
),
),
),
const SizedBox(height: AppSpacing.xl),
],
),
),
);
}
}
// ============================================================
// 开发者区域
// ============================================================
class DeveloperSection extends StatelessWidget {
const DeveloperSection({super.key, required this.ext, required this.t});
final AppThemeExtension ext;
final T t;
@override
Widget build(BuildContext context) {
return GlassContainer(
depth: GlassDepth.elevated,
padding: EdgeInsets.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AboutSectionTitle(
icon: CupertinoIcons.building_2_fill,
title: t.about.developer,
ext: ext,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.md,
),
child: Row(
children: [
Container(
width: 44,
height: 44,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
ext.accentLight,
ext.accent.withValues(alpha: 0.05),
],
),
borderRadius: AppRadius.mdBorder,
),
child: Icon(
CupertinoIcons.building_2_fill,
size: 22,
color: ext.accent,
),
),
const SizedBox(width: AppSpacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
t.about.companyName,
style: AppTypography.headline.copyWith(
fontWeight: FontWeight.bold,
color: ext.textPrimary,
),
),
const SizedBox(height: 2),
Text(
t.about.companyDesc,
style: AppTypography.caption1.copyWith(
color: ext.textHint,
),
),
],
),
),
],
),
),
AboutDivider(ext: ext, leftIndent: 36),
EmailTile(ext: ext, t: t),
AboutDivider(ext: ext, leftIndent: 36),
WeChatTile(ext: ext, t: t),
],
),
);
}
}
// ============================================================
// 团队信息区域
// ============================================================
class TeamSection extends StatelessWidget {
const TeamSection({super.key, required this.ext, required this.t});
final AppThemeExtension ext;
final T t;
@override
Widget build(BuildContext context) {
final members = [
TeamMemberData(
'💻',
t.about.roleDesign,
t.about.member1,
t.about.member1Sig,
t.about.member1Social,
),
TeamMemberData(
'🎨',
t.about.roleUIUX,
t.about.member2,
t.about.member2Sig,
t.about.member2Social,
),
TeamMemberData(
'⚙️',
t.about.roleBackend,
t.about.member3,
t.about.member3Sig,
t.about.member3Social,
),
TeamMemberData(
'🌐',
t.about.roleSupport,
t.about.member4,
t.about.member4Sig,
'',
),
TeamMemberData(
'📱',
t.about.roleNative,
'秋叶qy',
'春风若有怜花意,可否许我再少年',
'',
),
];
return GlassContainer(
depth: GlassDepth.elevated,
padding: EdgeInsets.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AboutSectionTitle(
icon: CupertinoIcons.person_2_fill,
title: t.about.teamInfo,
ext: ext,
),
...members.map(
(m) => Column(
children: [
TeamMemberTile(member: m, ext: ext),
if (m != members.last) AboutDivider(ext: ext, leftIndent: 36),
],
),
),
],
),
);
}
}
// ============================================================
// QQ群区域
// ============================================================
class QQGroupSection extends StatelessWidget {
const QQGroupSection({super.key, required this.ext, required this.t});
final AppThemeExtension ext;
final T t;
@override
Widget build(BuildContext context) {
const qqGroupUrl =
'http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Hg7woyP9rwz8ib3nwy38CjSyGQ0IcVQi&authKey=SVsg4NVfh0IEmvUIUQ3y2LMHzhTrMXGnvz0bgP2DBcQ5z71tRbPdu7M2prgXd894&noverify=0&group_code=271129018';
final isTelegram = t.about.qqGroupTelegram.isNotEmpty;
return GlassContainer(
depth: GlassDepth.elevated,
padding: EdgeInsets.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AboutSectionTitle(
icon: isTelegram
? CupertinoIcons.paperplane_fill
: CupertinoIcons.chat_bubble_2_fill,
title: isTelegram ? t.about.qqGroupTelegram : t.about.qqGroup,
ext: ext,
),
if (isTelegram)
_buildTelegramTile(context)
else
_buildQQGroupTile(context, qqGroupUrl),
],
),
);
}
Widget _buildQQGroupTile(BuildContext context, String qqGroupUrl) {
return GestureDetector(
onTap: () => _showQQGroupDialog(context, qqGroupUrl),
behavior: HitTestBehavior.opaque,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.md,
),
child: Row(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: const Color(0xFF12B7F5).withValues(alpha: 0.12),
borderRadius: AppRadius.smBorder,
),
child: const Center(
child: Text('🐧', style: TextStyle(fontSize: 18)),
),
),
const SizedBox(width: AppSpacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${AppConstants.appName} v${AppVersion.version}',
style: AppTypography.body.copyWith(
fontWeight: FontWeight.w500,
color: ext.textPrimary,
),
),
const SizedBox(height: 2),
Text(
t.about.qqGroupDesc,
style: AppTypography.caption2.copyWith(color: ext.textHint),
),
],
),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
onTap: () {
Clipboard.setData(const ClipboardData(text: '271129018'));
showCupertinoDialog<void>(
context: context,
builder: (ctx) => CupertinoAlertDialog(
content: Text(t.about.copied),
actions: [
CupertinoDialogAction(
isDefaultAction: true,
onPressed: () => Navigator.pop(ctx),
child: const Text('OK'),
),
],
),
);
},
child: Container(
padding: const EdgeInsets.all(AppSpacing.xs),
decoration: BoxDecoration(
color: const Color(0xFF12B7F5).withValues(alpha: 0.1),
borderRadius: AppRadius.smBorder,
),
child: const Icon(
CupertinoIcons.doc_on_clipboard,
size: 14,
color: Color(0xFF12B7F5),
),
),
),
const SizedBox(width: AppSpacing.xs),
const Icon(
CupertinoIcons.arrow_up_right_square,
size: 16,
color: Color(0xFF12B7F5),
),
],
),
],
),
),
);
}
Widget _buildTelegramTile(BuildContext context) {
final telegramUrl = t.about.qqGroupTelegramDesc;
return GestureDetector(
onTap: () => launchAboutUrl(context, telegramUrl),
behavior: HitTestBehavior.opaque,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.md,
),
child: Row(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: const Color(0xFF0088CC).withValues(alpha: 0.12),
borderRadius: AppRadius.smBorder,
),
child: const Center(
child: Text('✈️', style: TextStyle(fontSize: 18)),
),
),
const SizedBox(width: AppSpacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
t.about.qqGroupTelegram,
style: AppTypography.body.copyWith(
fontWeight: FontWeight.w500,
color: ext.textPrimary,
),
),
const SizedBox(height: 2),
Text(
telegramUrl,
style: AppTypography.caption2.copyWith(
color: const Color(0xFF0088CC),
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
const Icon(
CupertinoIcons.arrow_up_right_square,
size: 16,
color: Color(0xFF0088CC),
),
],
),
),
);
}
void _showQQGroupDialog(BuildContext context, String url) {
showCupertinoDialog<void>(
context: context,
builder: (ctx) => CupertinoAlertDialog(
title: Text(t.about.joinQQGroup),
content: Text(t.about.qqGroupConfirm),
actions: [
CupertinoDialogAction(
isDestructiveAction: true,
onPressed: () => Navigator.of(ctx).pop(),
child: Text(t.common.cancel),
),
CupertinoDialogAction(
isDefaultAction: true,
onPressed: () {
Navigator.of(ctx).pop();
launchAboutUrl(context, url);
},
child: Text(t.common.confirm),
),
],
),
);
}
}
// ============================================================
// 贡献者与特别鸣谢区域
// ============================================================
class ContributorsSection extends StatelessWidget {
const ContributorsSection({super.key, required this.ext, required this.t});
final AppThemeExtension ext;
final T t;
@override
Widget build(BuildContext context) {
return GlassContainer(
depth: GlassDepth.elevated,
padding: EdgeInsets.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AboutSectionTitle(
icon: CupertinoIcons.star_circle,
title: t.about.contributors,
ext: ext,
),
GestureDetector(
onTap: () => _showContributorsSheet(context),
behavior: HitTestBehavior.opaque,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.md,
),
child: Row(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: ext.iconTintYellow.withValues(alpha: 0.12),
borderRadius: AppRadius.smBorder,
),
child: Icon(
CupertinoIcons.group_solid,
size: 18,
color: ext.iconTintYellow,
),
),
const SizedBox(width: AppSpacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
t.about.contributors,
style: AppTypography.body.copyWith(
fontWeight: FontWeight.w500,
color: ext.textPrimary,
),
),
const SizedBox(height: 2),
Text(
t.about.contributorsDesc,
style: AppTypography.caption2.copyWith(
color: ext.textHint,
),
),
],
),
),
Icon(
CupertinoIcons.chevron_right,
size: 14,
color: ext.textHint,
),
],
),
),
),
AboutDivider(ext: ext, leftIndent: 36),
GestureDetector(
onTap: () => _showSpecialThanksSheet(context),
behavior: HitTestBehavior.opaque,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.md,
),
child: Row(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: ext.iconTintPurple.withValues(alpha: 0.12),
borderRadius: AppRadius.smBorder,
),
child: Icon(
CupertinoIcons.heart_circle,
size: 18,
color: ext.iconTintPurple,
),
),
const SizedBox(width: AppSpacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
t.about.specialThanks,
style: AppTypography.body.copyWith(
fontWeight: FontWeight.w500,
color: ext.textPrimary,
),
),
const SizedBox(height: 2),
Text(
t.about.specialThanksDesc,
style: AppTypography.caption2.copyWith(
color: ext.textHint,
),
),
],
),
),
Icon(
CupertinoIcons.chevron_right,
size: 14,
color: ext.textHint,
),
],
),
),
),
],
),
);
}
void _showContributorsSheet(BuildContext context) {
final contributors = [
ContributorData('佳佳', t.about.contributorRole5),
];
showCupertinoModalPopup<void>(
context: context,
builder: (ctx) => Container(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(ctx).size.height * 0.65,
),
decoration: BoxDecoration(
color: ext.bgElevated,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(AppRadius.xl),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: AppSpacing.sm),
Container(
width: 36,
height: 4,
decoration: BoxDecoration(
color: ext.textHint.withValues(alpha: 0.3),
borderRadius: AppRadius.smBorder,
),
),
const SizedBox(height: AppSpacing.md),
Padding(
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
child: Row(
children: [
const Text('🌟', style: TextStyle(fontSize: 18)),
const SizedBox(width: AppSpacing.sm),
Text(
t.about.contributors,
style: AppTypography.title3.copyWith(
fontWeight: FontWeight.w700,
color: ext.textPrimary,
),
),
const Spacer(),
GestureDetector(
onTap: () => Navigator.pop(ctx),
child: Container(
width: 28,
height: 28,
decoration: BoxDecoration(
color: ext.textHint.withValues(alpha: 0.12),
shape: BoxShape.circle,
),
child: Icon(
CupertinoIcons.xmark,
size: 14,
color: ext.textSecondary,
),
),
),
],
),
),
const SizedBox(height: AppSpacing.sm),
Flexible(
child: ListView.separated(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
itemCount: contributors.length,
separatorBuilder: (_, __) =>
const SizedBox(height: AppSpacing.sm),
itemBuilder: (_, index) => _ContributorCard(
contributor: contributors[index],
ext: ext,
),
),
),
const SizedBox(height: AppSpacing.xl),
],
),
),
);
}
void _showSpecialThanksSheet(BuildContext context) {
final thanks = [
ContributorData('Tools/Plugins', t.about.specialThanksTools),
];
showCupertinoModalPopup<void>(
context: context,
builder: (ctx) => Container(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(ctx).size.height * 0.65,
),
decoration: BoxDecoration(
color: ext.bgElevated,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(AppRadius.xl),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: AppSpacing.sm),
Container(
width: 36,
height: 4,
decoration: BoxDecoration(
color: ext.textHint.withValues(alpha: 0.3),
borderRadius: AppRadius.smBorder,
),
),
const SizedBox(height: AppSpacing.md),
Padding(
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
child: Row(
children: [
const Text('💝', style: TextStyle(fontSize: 18)),
const SizedBox(width: AppSpacing.sm),
Text(
t.about.specialThanks,
style: AppTypography.title3.copyWith(
fontWeight: FontWeight.w700,
color: ext.textPrimary,
),
),
const Spacer(),
GestureDetector(
onTap: () => Navigator.pop(ctx),
child: Container(
width: 28,
height: 28,
decoration: BoxDecoration(
color: ext.textHint.withValues(alpha: 0.12),
shape: BoxShape.circle,
),
child: Icon(
CupertinoIcons.xmark,
size: 14,
color: ext.textSecondary,
),
),
),
],
),
),
const SizedBox(height: AppSpacing.sm),
Flexible(
child: ListView.separated(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
itemCount: thanks.length,
separatorBuilder: (_, __) =>
const SizedBox(height: AppSpacing.sm),
itemBuilder: (_, index) =>
_ContributorCard(contributor: thanks[index], ext: ext),
),
),
const SizedBox(height: AppSpacing.xl),
],
),
),
);
}
}
class _ContributorCard extends StatelessWidget {
const _ContributorCard({required this.contributor, required this.ext});
final ContributorData contributor;
final AppThemeExtension ext;
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: ext.bgPrimary,
borderRadius: AppRadius.lgBorder,
border: Border.all(color: ext.textHint.withValues(alpha: 0.1)),
),
child: Row(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: ext.accent.withValues(alpha: 0.12),
borderRadius: AppRadius.mdBorder,
),
child: Center(
child: Text(
contributor.initial,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: ext.accent,
),
),
),
),
const SizedBox(width: AppSpacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
contributor.name,
style: AppTypography.body.copyWith(
fontWeight: FontWeight.w600,
color: ext.textPrimary,
),
),
const SizedBox(height: 2),
Text(
contributor.role,
style: AppTypography.caption2.copyWith(color: ext.textHint),
),
],
),
),
],
),
);
}
}
class ContributorData {
const ContributorData(this.name, this.role);
final String name;
final String role;
/// 获取名称首字符作为头像占位
/// 英文名取首字母大写,中文名取第一个汉字
String get initial {
if (name.isEmpty) return '?';
final firstChar = name[0];
if (RegExp(r'[a-zA-Z]').hasMatch(firstChar)) {
return firstChar.toUpperCase();
}
return firstChar;
}
}
// ============================================================
// 其他产品数据与卡片
// ============================================================
class _ProductData {
const _ProductData({
required this.emoji,
required this.name,
required this.icp,
required this.desc,
required this.url,
required this.color,
});
final String emoji;
final String name;
final String icp;
final String desc;
final String url;
final Color color;
}
class _ProductCard extends StatelessWidget {
const _ProductCard({
required this.product,
required this.ext,
required this.onTap,
});
final _ProductData product;
final AppThemeExtension ext;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
behavior: HitTestBehavior.opaque,
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: ext.bgPrimary,
borderRadius: AppRadius.lgBorder,
border: Border.all(color: ext.textHint.withValues(alpha: 0.1)),
),
child: Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: product.color.withValues(alpha: 0.12),
borderRadius: AppRadius.mdBorder,
),
child: Center(
child: Text(
product.emoji,
style: const TextStyle(fontSize: 18),
),
),
),
const SizedBox(width: AppSpacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: AppTypography.body.copyWith(
fontWeight: FontWeight.w600,
color: ext.textPrimary,
),
),
const SizedBox(height: 2),
Text(
product.icp,
style: AppTypography.caption1.copyWith(color: ext.textHint),
),
const SizedBox(height: 1),
Text(
product.desc,
style: AppTypography.caption2.copyWith(color: ext.textHint),
),
],
),
),
const SizedBox(width: AppSpacing.sm),
Icon(CupertinoIcons.chevron_right, size: 14, color: ext.textHint),
],
),
),
);
}
}