Flutter实现从系统默认通讯录直接选择联系人
Flutter调用系统默认通讯录选择联系人的实现方案
核心思路
通过Flutter的MethodChannel调用原生平台的系统联系人选择器,直接唤起系统默认通讯录应用完成选择,无需在应用内加载全部联系人列表。
1. 配置权限
安卓端(AndroidManifest.xml)
在android/app/src/main/AndroidManifest.xml中添加权限:
<uses-permission android:name="android.permission.READ_CONTACTS" />
iOS端(Info.plist)
在ios/Runner/Info.plist中添加权限描述:
<key>NSContactsUsageDescription</key> <string>需要访问通讯录以选择联系人</string>
2. 实现平台通道(MethodChannel)
Flutter端代码
import 'package:flutter/services.dart'; class ContactPicker { static const MethodChannel _channel = MethodChannel('contact_picker'); // 唤起系统联系人选择器 static Future<Map<String, String>?> pickContact() async { try { final result = await _channel.invokeMethod('pickContact'); if (result != null) { return { 'name': result['name'], 'phone': result['phone'], }; } return null; } on PlatformException catch (e) { print('选择联系人失败: ${e.message}'); return null; } } } // 使用示例 void _pickContact() async { final contact = await ContactPicker.pickContact(); if (contact != null) { print('选择的联系人: ${contact['name']}, ${contact['phone']}'); // 处理选中的联系人信息 } }
安卓端原生代码(MainActivity.kt)
import android.content.Intent import android.provider.ContactsContract import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { private val CHANNEL = "contact_picker" private val REQUEST_CODE_PICK_CONTACT = 1001 private var resultCallback: MethodChannel.Result? = null override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> when (call.method) { "pickContact" -> { resultCallback = result val pickContactIntent = Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI) startActivityForResult(pickContactIntent, REQUEST_CODE_PICK_CONTACT) } else -> result.notImplemented() } } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_CODE_PICK_CONTACT && resultCode == RESULT_OK && data != null) { val cursor = contentResolver.query(data.data!!, null, null, null, null) cursor?.let { if (it.moveToFirst()) { val name = it.getString(it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)) val phone = it.getString(it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER)) resultCallback?.success(mapOf("name" to name, "phone" to phone)) } else { resultCallback?.error("EMPTY", "未选中联系人", null) } it.close() } ?: run { resultCallback?.error("ERROR", "获取联系人失败", null) } resultCallback = null } else { resultCallback?.error("CANCEL", "用户取消选择", null) resultCallback = null } } }
iOS端原生代码(AppDelegate.swift)
import UIKit import Flutter import ContactsUI @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate, CNContactPickerDelegate { private let CHANNEL = "contact_picker" private var resultCallback: FlutterResult? override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { let controller : FlutterViewController = window?.rootViewController as! FlutterViewController let channel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: controller.binaryMessenger) channel.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in guard let self = self else { return } switch call.method { case "pickContact": self.resultCallback = result let picker = CNContactPickerViewController() picker.delegate = self picker.displayedPropertyKeys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey] controller.present(picker, animated: true, completion: nil) default: result(FlutterMethodNotImplemented) } }) GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) { var name = "" if !contact.givenName.isEmpty { name += contact.givenName } if !contact.familyName.isEmpty { name += " " + contact.familyName } name = name.trimmingCharacters(in: .whitespaces) var phone = "" if let phoneNumber = contact.phoneNumbers.first?.value { phone = phoneNumber.stringValue } resultCallback?([ "name": name, "phone": phone ]) resultCallback = nil picker.dismiss(animated: true) } func contactPickerDidCancel(_ picker: CNContactPickerViewController) { resultCallback?(FlutterError(code: "CANCEL", message: "用户取消选择", details: nil)) resultCallback = nil picker.dismiss(animated: true) } }
3. 注意事项
- 安卓端需要动态申请权限,可配合
permission_handler插件实现权限申请逻辑,避免直接唤起选择器时权限不足崩溃。 - iOS端需确保在
Info.plist中正确配置权限描述,否则应用会被拒。 - 不同系统版本的联系人选择器可能有细微差异,需在目标版本上测试兼容性。
内容的提问来源于stack exchange,提问作者Alpit Panchal
相关产品推荐
相关产品推荐

