Files
mail-hub/lib/ui/widgets/email_list_tile.dart
hz4th_coder 6c1225f588 MailHub v1.0.0: 跨平台邮箱客户端(纯 Dart IMAP/SMTP/MIME 协议栈)
- 登录: QQ/163/126/189/搜狐/Gmail/Outlook/Yahoo/iCloud/企业邮/自定义, 自动识别+测试连接
- 收信: IMAP 多文件夹同步/未读/星标/搜索(本地+服务器)
- 读信: HTML渲染/CID内嵌图/原文查看/附件下载
- 写信: 收件人/抄送/密送/附件/富文本/草稿/回复转发
- 存储: SQLite + 系统安全存储(DPAPI/Keychain/Keystore)
- 测试: 单元测试 + Mock IMAP/SMTP 服务器集成测试(23项全过)
- 可迁移: Windows/Android/iOS/macOS/Linux 一套代码
2026-08-18 01:10:11 +08:00

153 lines
5.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../../core/models/email.dart';
/// 邮件列表项
class EmailListTile extends StatelessWidget {
final Email email;
final bool selected;
final VoidCallback onTap;
final VoidCallback? onFlag;
const EmailListTile({
super.key,
required this.email,
required this.selected,
required this.onTap,
this.onFlag,
});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final bg = selected
? scheme.primaryContainer.withValues(alpha: 0.45)
: email.seen
? Colors.transparent
: scheme.surfaceContainerHighest.withValues(alpha: 0.35);
return Material(
color: bg,
child: InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 已读/未读圆点
Padding(
padding: const EdgeInsets.only(top: 6),
child: email.seen
? const SizedBox(width: 8)
: Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: scheme.primary,
shape: BoxShape.circle,
),
),
),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
email.fromLabel.isEmpty ? '(无发件人)' : email.fromLabel,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
fontWeight: email.seen ? FontWeight.w500 : FontWeight.w700,
color: email.seen ? scheme.onSurface : scheme.onSurface,
),
),
),
const SizedBox(width: 8),
Text(
_formatDate(email.date),
style: TextStyle(fontSize: 12, color: scheme.onSurfaceVariant),
),
],
),
const SizedBox(height: 3),
Text(
email.subject.isEmpty ? '(无主题)' : email.subject,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13.5,
fontWeight: email.seen ? FontWeight.w400 : FontWeight.w600,
color: email.seen ? scheme.onSurfaceVariant : scheme.onSurface,
),
),
if (email.snippet.isNotEmpty) ...[
const SizedBox(height: 2),
Text(
email.snippet,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12,
color: scheme.onSurfaceVariant.withValues(alpha: 0.8),
),
),
],
],
),
),
const SizedBox(width: 6),
// 附件 / 星标
Column(
children: [
if (email.hasAttachments)
Icon(Icons.attach_file,
size: 16, color: scheme.onSurfaceVariant),
if (email.hasAttachments) const SizedBox(height: 2),
IconButton(
visualDensity: VisualDensity.compact,
constraints: const BoxConstraints(),
padding: EdgeInsets.zero,
iconSize: 19,
icon: Icon(
email.flagged ? Icons.star : Icons.star_border,
color: email.flagged ? Colors.amber : scheme.outlineVariant,
),
onPressed: onFlag,
),
],
),
],
),
),
),
);
}
String _formatDate(DateTime? date) {
if (date == null) return '';
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
final d = DateTime(date.year, date.month, date.day);
final diff = today.difference(d).inDays;
if (diff == 0) {
return DateFormat('HH:mm').format(date);
}
if (diff == 1) return '昨天';
if (diff < 7) {
const week = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
return week[date.weekday - 1];
}
if (date.year == now.year) {
return DateFormat('M/d').format(date);
}
return DateFormat('yyyy/M/d').format(date);
}
}