- 登录: QQ/163/126/189/搜狐/Gmail/Outlook/Yahoo/iCloud/企业邮/自定义, 自动识别+测试连接 - 收信: IMAP 多文件夹同步/未读/星标/搜索(本地+服务器) - 读信: HTML渲染/CID内嵌图/原文查看/附件下载 - 写信: 收件人/抄送/密送/附件/富文本/草稿/回复转发 - 存储: SQLite + 系统安全存储(DPAPI/Keychain/Keystore) - 测试: 单元测试 + Mock IMAP/SMTP 服务器集成测试(23项全过) - 可迁移: Windows/Android/iOS/macOS/Linux 一套代码
71 lines
2.3 KiB
Dart
71 lines
2.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../../core/models/address.dart';
|
|
|
|
/// 附件展示 chip
|
|
class AttachmentChip extends StatelessWidget {
|
|
final MailAttachment attachment;
|
|
final VoidCallback onTap; // 打开/保存
|
|
final VoidCallback? onRemove;
|
|
|
|
const AttachmentChip({
|
|
super.key,
|
|
required this.attachment,
|
|
required this.onTap,
|
|
this.onRemove,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final scheme = Theme.of(context).colorScheme;
|
|
final icon = _iconFor(attachment.fileName);
|
|
return InputChip(
|
|
avatar: Icon(icon, size: 18, color: scheme.primary),
|
|
label: Text(
|
|
attachment.fileName,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(fontSize: 12.5),
|
|
),
|
|
tooltip: '${attachment.fileName} (${_size(attachment.size)})',
|
|
onPressed: onTap,
|
|
onDeleted: onRemove,
|
|
deleteIcon: onRemove == null ? null : const Icon(Icons.close, size: 15),
|
|
visualDensity: VisualDensity.compact,
|
|
);
|
|
}
|
|
|
|
IconData _iconFor(String name) {
|
|
final lower = name.toLowerCase();
|
|
if (lower.endsWith('.pdf')) return Icons.picture_as_pdf_outlined;
|
|
if (lower.endsWith('.doc') || lower.endsWith('.docx')) return Icons.description_outlined;
|
|
if (lower.endsWith('.xls') || lower.endsWith('.xlsx') || lower.endsWith('.csv')) {
|
|
return Icons.table_chart_outlined;
|
|
}
|
|
if (lower.endsWith('.ppt') || lower.endsWith('.pptx')) return Icons.slideshow_outlined;
|
|
if (lower.endsWith('.zip') || lower.endsWith('.rar') || lower.endsWith('.7z')) {
|
|
return Icons.folder_zip_outlined;
|
|
}
|
|
if (lower.endsWith('.png') ||
|
|
lower.endsWith('.jpg') ||
|
|
lower.endsWith('.jpeg') ||
|
|
lower.endsWith('.gif') ||
|
|
lower.endsWith('.webp') ||
|
|
lower.endsWith('.bmp')) {
|
|
return Icons.image_outlined;
|
|
}
|
|
if (lower.endsWith('.mp3') || lower.endsWith('.wav') || lower.endsWith('.flac')) {
|
|
return Icons.music_note_outlined;
|
|
}
|
|
if (lower.endsWith('.mp4') || lower.endsWith('.mov') || lower.endsWith('.avi')) {
|
|
return Icons.movie_outlined;
|
|
}
|
|
return Icons.insert_drive_file_outlined;
|
|
}
|
|
|
|
static String _size(int bytes) {
|
|
if (bytes < 1024) return '$bytes B';
|
|
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
|
|
return '${(bytes / 1048576).toStringAsFixed(1)} MB';
|
|
}
|
|
}
|