鸿蒙flutter第三方库适配 - 联系人备份工具
联系人备份工具应用
欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
适配的第三方库地址:
- flutter_contacts: https://pub.dev/packages/flutter_contacts
- share_plus: https://pub.dev/packages/share_plus
- sqflite: https://pub.dev/packages/sqflite
- permission_handler: https://pub.dev/packages/permission_handler
一、项目概述
运行效果图





1.1 应用简介
联系人备份工具是一款专注于通讯录管理的实用工具应用,旨在帮助用户安全、便捷地管理手机联系人。应用支持多种备份方式、智能重复检测、灵活分组管理,让用户的通讯录始终保持整洁有序。
应用以清新青绿为主色调,象征安全与可靠。涵盖联系人管理、备份恢复、重复检测、分组管理四大核心模块。用户可以轻松备份联系人、导入导出数据、合并重复项、按组分类管理,实现通讯录的高效管理。
1.2 核心功能
| 功能模块 | 功能描述 | 实现方式 |
|---|---|---|
| 联系人管理 | 查看、编辑、删除联系人 | 通讯录API |
| 本地备份 | 备份联系人到本地存储 | 文件存储 |
| 云端备份 | 同步联系人到云端服务 | 网络同步 |
| 导入导出 | 导出VCF/CSV格式文件 | 文件处理 |
| 重复检测 | 智能识别重复联系人 | 算法匹配 |
| 合并功能 | 一键合并重复联系人 | 数据合并 |
| 分组管理 | 按类别分组管理联系人 | 分类管理 |
| 搜索功能 | 快速搜索联系人信息 | 模糊搜索 |
1.3 联系人分组定义
| 序号 | 分组名称 | 图标 | 颜色 | 描述 |
|---|---|---|---|---|
| 1 | 家人 | 👨👩👧👦 | 红色 | 家庭成员 |
| 2 | 朋友 | 👥 | 蓝色 | 社交好友 |
| 3 | 工作 | 💼 | 绿色 | 工作伙伴 |
| 4 | 同学 | 🎓 | 橙色 | 学校同学 |
| 5 | 其他 | ⋯ | 灰色 | 其他联系人 |
1.4 备份类型定义
| 序号 | 备份类型 | 图标 | 描述 | 特点 |
|---|---|---|---|---|
| 1 | 本地备份 | 📱 | 备份到手机存储 | 无需网络 |
| 2 | 云端备份 | ☁️ | 同步到云端服务 | 跨设备同步 |
| 3 | 导出文件 | 📥 | 导出VCF/CSV文件 | 便于分享 |
1.5 重复检测规则
| 序号 | 匹配字段 | 优先级 | 描述 |
|---|---|---|---|
| 1 | 电话号码 | 高 | 完全匹配电话号码 |
| 2 | 姓名 | 中 | 完全匹配联系人姓名 |
| 3 | 邮箱 | 中 | 完全匹配邮箱地址 |
| 4 | 姓名+电话 | 低 | 组合匹配 |
1.6 导出格式定义
| 序号 | 格式名称 | 扩展名 | 描述 | 兼容性 |
|---|---|---|---|---|
| 1 | VCF | .vcf | vCard标准格式 | 通用 |
| 2 | CSV | .csv | 表格数据格式 | Excel兼容 |
| 3 | JSON | .json | 结构化数据格式 | 开发友好 |
1.7 技术栈
| 技术领域 | 技术选型 | 版本要求 |
|---|---|---|
| 开发框架 | Flutter | >= 3.0.0 |
| 编程语言 | Dart | >= 2.17.0 |
| 设计规范 | Material Design 3 | - |
| 状态管理 | StatefulWidget | - |
| 通讯录管理 | Flutter Contacts | >= 1.0.0 |
| 文件分享 | Share Plus | >= 7.0.0 |
| 数据存储 | Sqflite | >= 2.0.0 |
| 权限管理 | Permission Handler | >= 11.0.0 |
| 目标平台 | 鸿蒙OS / Web | API 21+ |
1.8 项目结构
lib/
└── main_contact_backup.dart
├── ContactBackupApp # 应用入口
├── ContactGroup # 联系人分组枚举
├── BackupType # 备份类型枚举
├── Contact # 联系人模型
├── BackupRecord # 备份记录模型
├── DuplicateContact # 重复联系人模型
├── ContactBackupHomePage # 主页面(底部导航)
├── _buildContactsPage # 联系人页
├── _buildBackupPage # 备份页
├── _buildDuplicatesPage # 重复检测页
└── _buildGroupsPage # 分组页
二、系统架构
2.1 整体架构图
2.2 类图设计
2.3 页面导航流程
2.4 备份恢复流程
三、核心模块设计
3.1 数据模型设计
3.1.1 联系人分组枚举 (ContactGroup)
enum ContactGroup {
family('家人', Icons.family_restroom, Colors.red),
friends('朋友', Icons.people, Colors.blue),
work('工作', Icons.work, Colors.green),
school('同学', Icons.school, Colors.orange),
other('其他', Icons.more_horiz, Colors.grey);
final String label;
final IconData icon;
final Color color;
const ContactGroup(this.label, this.icon, this.color);
}
3.1.2 备份类型枚举 (BackupType)
enum BackupType {
local('本地备份', Icons.phone_android),
cloud('云端备份', Icons.cloud_upload),
export('导出文件', Icons.file_download);
final String label;
final IconData icon;
const BackupType(this.label, this.icon);
}
3.1.3 联系人模型 (Contact)
class Contact {
final String id;
final String name;
final String? phone;
final String? email;
final String? company;
final String? notes;
final ContactGroup group;
final bool isFavorite;
final DateTime createdAt;
final DateTime? updatedAt;
const Contact({
required this.id,
required this.name,
this.phone,
this.email,
this.company,
this.notes,
required this.group,
required this.isFavorite,
required this.createdAt,
this.updatedAt,
});
Contact copyWith({
String? name,
String? phone,
String? email,
String? company,
String? notes,
ContactGroup? group,
bool? isFavorite,
DateTime? updatedAt,
}) {
return Contact(
id: id,
name: name ?? this.name,
phone: phone ?? this.phone,
email: email ?? this.email,
company: company ?? this.company,
notes: notes ?? this.notes,
group: group ?? this.group,
isFavorite: isFavorite ?? this.isFavorite,
createdAt: createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
String get initials {
if (name.isEmpty) return '?';
final parts = name.split(' ');
if (parts.length >= 2) {
return '${parts[0][0]}${parts[1][0]}'.toUpperCase();
}
return name[0].toUpperCase();
}
}
3.1.4 联系人分组分布
3.2 页面结构设计
3.2.1 主页面布局
3.2.2 联系人页结构
3.2.3 备份页结构
3.2.4 重复检测页结构
3.3 重复检测逻辑
3.4 备份管理逻辑
四、UI设计规范
4.1 配色方案
应用以清新青绿为主色调,象征安全与可靠:
| 颜色类型 | 色值 | 用途 |
|---|---|---|
| 主色 | #009688 (Teal) | 导航、主题元素 |
| 辅助色 | #4DB6AC | 卡片背景 |
| 第三色 | #80CBC4 | 进度条背景 |
| 强调色 | #B2DFDB | 分组页面 |
| 背景色 | #FAFAFA | 页面背景 |
| 卡片背景 | #FFFFFF | 信息卡片 |
| 家人色 | #F44336 | 家人分组 |
| 朋友色 | #2196F3 | 朋友分组 |
4.2 分组配色
| 分组 | 色值 | 视觉效果 |
|---|---|---|
| 家人 | #F44336 | 红色 |
| 朋友 | #2196F3 | 蓝色 |
| 工作 | #4CAF50 | 绿色 |
| 同学 | #FF9800 | 橙色 |
| 其他 | #9E9E9E | 灰色 |
4.3 字体规范
| 元素 | 字号 | 字重 | 颜色 |
|---|---|---|---|
| 页面标题 | 24px | Bold | 主色 |
| 联系人名称 | 16px | Medium | #000000 |
| 联系方式 | 13px | Regular | #666666 |
| 分组标签 | 11px | Medium | 分组色 |
| 备份名称 | 15px | Medium | #000000 |
| 提示文字 | 14px | Regular | #999999 |
4.4 组件规范
4.4.1 联系人卡片
┌─────────────────────────────────────┐
│ ⭕ 张三 [朋友] │
│ 138****1234 │
│ ⭐ │
└─────────────────────────────────────┘
4.4.2 备份选项卡片
┌─────────────────────────────────────┐
│ 备份选项 │
│ │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ 📱 │ │ ☁️ │ │ 📥 │ │
│ │ 本地 │ │ 云端 │ │ 导出 │ │
│ │ 备份 │ │ 备份 │ │ 文件 │ │
│ └────────┘ └────────┘ └────────┘ │
└─────────────────────────────────────┘
4.4.3 备份记录卡片
┌─────────────────────────────────────┐
│ 📱 自动备份_20240115 ⋮ │
│ 28 个联系人 · 45.7 KB │
│ 2024-01-15 10:30 │
└─────────────────────────────────────┘
4.4.4 重复联系人卡片
┌─────────────────────────────────────┐
│ ⚠️ 重复: 电话号码相同 │
│ ───────────────────────────────── │
│ ⭕ 张三 [保留] │
│ 138****1234 │
│ ⭕ 张三 │
│ 138****1234 │
│ ───────────────────────────────── │
│ [合并] [查看详情] │
└─────────────────────────────────────┘
4.4.5 分组卡片
┌─────────────────────────────────────┐
│ 👥 12 │
│ │
│ 朋友 │
│ 12 位联系人 │
└─────────────────────────────────────┘
五、核心功能实现
5.1 联系人管理器实现
class ContactManager {
List<Contact> _contacts = [];
Future<void> loadContacts() async {
if (await Permission.contacts.request().isGranted) {
final contacts = await FlutterContacts.getContacts(
withProperties: true,
withPhoto: true,
);
_contacts = contacts.map(_convertToContact).toList();
}
}
Future<void> addContact(Contact contact) async {
final newContact = Contact(
id: 'contact_${DateTime.now().millisecondsSinceEpoch}',
name: contact.name,
phone: contact.phone,
email: contact.email,
company: contact.company,
notes: contact.notes,
group: contact.group,
isFavorite: false,
createdAt: DateTime.now(),
);
_contacts.add(newContact);
await _saveToSystemContacts(newContact);
}
Future<void> updateContact(Contact contact) async {
final index = _contacts.indexWhere((c) => c.id == contact.id);
if (index != -1) {
_contacts[index] = contact.copyWith(updatedAt: DateTime.now());
await _updateSystemContacts(contact);
}
}
Future<void> deleteContact(String contactId) async {
_contacts.removeWhere((c) => c.id == contactId);
await _deleteFromSystemContacts(contactId);
}
List<Contact> searchContacts(String query) {
final lowerQuery = query.toLowerCase();
return _contacts.where((c) {
return c.name.toLowerCase().contains(lowerQuery) ||
(c.phone?.contains(query) ?? false) ||
(c.email?.toLowerCase().contains(lowerQuery) ?? false);
}).toList();
}
}
5.2 备份管理器实现
class BackupManager {
final DatabaseHelper _dbHelper = DatabaseHelper();
Future<BackupRecord> createLocalBackup(List<Contact> contacts) async {
final fileName = 'backup_${DateTime.now().millisecondsSinceEpoch}.json';
final data = jsonEncode(contacts.map((c) => c.toJson()).toList());
final file = await _saveToFile(fileName, data);
final record = BackupRecord(
id: 'backup_${DateTime.now().millisecondsSinceEpoch}',
name: '本地备份_${DateTime.now().toString().substring(0, 10)}',
type: BackupType.local,
contactCount: contacts.length,
fileSize: data.length,
createdAt: DateTime.now(),
filePath: file.path,
);
await _dbHelper.insertBackupRecord(record);
return record;
}
Future<BackupRecord> createCloudBackup(List<Contact> contacts) async {
final response = await _cloudService.uploadContacts(contacts);
final record = BackupRecord(
id: 'backup_${DateTime.now().millisecondsSinceEpoch}',
name: '云端备份_${DateTime.now().toString().substring(0, 10)}',
type: BackupType.cloud,
contactCount: contacts.length,
fileSize: response.size,
createdAt: DateTime.now(),
);
await _dbHelper.insertBackupRecord(record);
return record;
}
Future<void> restoreFromBackup(BackupRecord record) async {
final data = await _loadBackupData(record);
final contacts = (jsonDecode(data) as List)
.map((json) => Contact.fromJson(json))
.toList();
for (final contact in contacts) {
await _contactManager.addContact(contact);
}
}
Future<String> exportToVCF(List<Contact> contacts) async {
final buffer = StringBuffer();
for (final contact in contacts) {
buffer.writeln('BEGIN:VCARD');
buffer.writeln('VERSION:3.0');
buffer.writeln('FN:${contact.name}');
if (contact.phone != null) {
buffer.writeln('TEL:${contact.phone}');
}
if (contact.email != null) {
buffer.writeln('EMAIL:${contact.email}');
}
if (contact.company != null) {
buffer.writeln('ORG:${contact.company}');
}
buffer.writeln('END:VCARD');
}
return buffer.toString();
}
}
5.3 重复检测器实现
class DuplicateDetector {
List<DuplicateContact> findDuplicates(List<Contact> contacts) {
final duplicates = <DuplicateContact>[];
final phoneMap = <String, List<Contact>>{};
final nameMap = <String, List<Contact>>{};
final emailMap = <String, List<Contact>>{};
for (final contact in contacts) {
if (contact.phone != null) {
phoneMap.putIfAbsent(contact.phone!, () => []).add(contact);
}
nameMap.putIfAbsent(contact.name, () => []).add(contact);
if (contact.email != null) {
emailMap.putIfAbsent(contact.email!, () => []).add(contact);
}
}
for (final entry in phoneMap.entries) {
if (entry.value.length > 1) {
duplicates.add(DuplicateContact(
original: entry.value.first,
duplicates: entry.value.skip(1).toList(),
matchField: '电话号码',
));
}
}
for (final entry in nameMap.entries) {
if (entry.value.length > 1) {
final existingIds = duplicates
.expand((d) => [d.original.id, ...d.duplicates.map((c) => c.id)])
.toSet();
if (!existingIds.contains(entry.value.first.id)) {
duplicates.add(DuplicateContact(
original: entry.value.first,
duplicates: entry.value.skip(1).toList(),
matchField: '姓名',
));
}
}
}
return duplicates;
}
Contact mergeContacts(Contact original, List<Contact> duplicates) {
String? phone = original.phone;
String? email = original.email;
String? company = original.company;
String? notes = original.notes;
for (final dup in duplicates) {
phone ??= dup.phone;
email ??= dup.email;
company ??= dup.company;
notes ??= dup.notes;
}
return original.copyWith(
phone: phone,
email: email,
company: company,
notes: notes,
updatedAt: DateTime.now(),
);
}
}
5.4 权限管理实现
class PermissionManager {
Future<bool> requestContactsPermission() async {
final status = await Permission.contacts.status;
if (status.isGranted) return true;
final result = await Permission.contacts.request();
if (result.isGranted) return true;
if (result.isPermanentlyDenied) {
await openAppSettings();
}
return false;
}
Future<bool> requestStoragePermission() async {
if (await Permission.storage.request().isGranted) {
return true;
}
return false;
}
Future<bool> checkAllPermissions() async {
final contactsGranted = await requestContactsPermission();
final storageGranted = await requestStoragePermission();
return contactsGranted && storageGranted;
}
}
5.5 数据库存储实现
class DatabaseHelper {
static Database? _database;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
final path = join(await getDatabasesPath(), 'contact_backup.db');
return await openDatabase(
path,
version: 1,
onCreate: (db, version) async {
await db.execute('''
CREATE TABLE backup_records (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
type TEXT NOT NULL,
contact_count INTEGER NOT NULL,
file_size INTEGER NOT NULL,
created_at TEXT NOT NULL,
file_path TEXT
)
''');
},
);
}
Future<void> insertBackupRecord(BackupRecord record) async {
final db = await database;
await db.insert('backup_records', {
'id': record.id,
'name': record.name,
'type': record.type.name,
'contact_count': record.contactCount,
'file_size': record.fileSize,
'created_at': record.createdAt.toIso8601String(),
'file_path': record.filePath,
});
}
Future<List<BackupRecord>> getBackupRecords() async {
final db = await database;
final List<Map<String, dynamic>> maps = await db.query(
'backup_records',
orderBy: 'created_at DESC',
);
return maps.map((map) => BackupRecord(
id: map['id'],
name: map['name'],
type: BackupType.values.firstWhere((t) => t.name == map['type']),
contactCount: map['contact_count'],
fileSize: map['file_size'],
createdAt: DateTime.parse(map['created_at']),
filePath: map['file_path'],
)).toList();
}
}
六、交互设计
6.1 联系人管理流程
6.2 备份恢复流程
6.3 重复合并流程
七、扩展功能规划
7.1 后续版本规划
7.2 功能扩展建议
7.2.1 云端同步
同步功能:
- 多设备实时同步
- 版本历史管理
- 冲突自动解决
- 增量同步优化
7.2.2 智能整理
整理功能:
- 自动识别重复
- 智能分组建议
- 信息补全提示
- 批量编辑操作
7.2.3 隐私保护
保护功能:
- 敏感信息加密
- 访问密码保护
- 隐私联系人隐藏
- 安全删除擦除
八、注意事项
8.1 开发注意事项
-
权限申请:正确申请通讯录、存储等敏感权限
-
数据安全:联系人数据属于隐私信息,需妥善处理
-
性能优化:大量联系人时需优化加载和搜索性能
-
兼容性:适配不同Android/iOS版本的通讯录API
-
数据同步:确保本地数据与系统通讯录保持同步
8.2 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 权限被拒绝 | 用户拒绝授权 | 引导用户到设置开启 |
| 联系人加载慢 | 数据量大 | 实现分页加载 |
| 备份失败 | 存储空间不足 | 检查存储空间 |
| 导入失败 | 文件格式错误 | 验证文件格式 |
| 同步冲突 | 多设备修改 | 提供冲突解决选项 |
8.3 使用技巧
📱 联系人备份工具使用技巧 📱
备份管理
- 定期备份联系人数据
- 保留多个备份版本
- 备份前检查存储空间
- 重要联系人单独导出
重复整理
- 定期检测重复联系人
- 合并前仔细核对信息
- 保留最完整的记录
- 及时清理无效联系人
分组管理
- 合理设置联系人分组
- 重要联系人设为收藏
- 定期整理分组归属
- 使用搜索快速定位
九、运行说明
9.1 环境要求
| 环境 | 版本要求 |
|---|---|
| Flutter SDK | >= 3.0.0 |
| Dart SDK | >= 2.17.0 |
| 鸿蒙OS | API 21+ |
| Android | API 21+ |
| iOS | 12.0+ |
| Web浏览器 | Chrome 90+ |
9.2 依赖配置
在 pubspec.yaml 中添加以下依赖:
dependencies:
flutter:
sdk: flutter
flutter_contacts: ^1.0.0
share_plus: ^7.0.0
sqflite: ^2.0.0
permission_handler: ^11.0.0
path_provider: ^2.0.0
path: ^1.8.0
9.3 权限配置
Android (android/app/src/main/AndroidManifest.xml):
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
iOS (ios/Runner/Info.plist):
<key>NSContactsUsageDescription</key>
<string>需要访问通讯录以备份和管理联系人</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问存储以保存备份文件</string>
9.4 运行命令
# 查看可用设备
flutter devices
# 运行到Web服务器
flutter run -d web-server -t lib/main_contact_backup.dart --web-port 8147
# 运行到鸿蒙设备
flutter run -d 127.0.0.1:5555 -t lib/main_contact_backup.dart
# 运行到Android设备
flutter run -d android -t lib/main_contact_backup.dart
# 代码分析
flutter analyze lib/main_contact_backup.dart
十、总结
联系人备份工具应用通过多种备份方式、智能重复检测、灵活分组管理,帮助用户安全、便捷地管理手机联系人。应用支持本地备份、云端备份、导出文件三种备份方式,智能识别电话号码、姓名、邮箱重复的联系人,按家人、朋友、工作、同学、其他五类分组管理。
应用采用 Material Design 3 设计规范,以清新青绿为主色调,象征安全与可靠。通过本应用,希望能够帮助用户轻松管理通讯录,让联系人数据始终保持整洁有序。
联系人备份工具——守护您的通讯录安全
更多推荐


所有评论(0)