You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter使用sqflite无法将contact_service获取的联系人存入本地数据库

Flutter使用sqflite存储联系人到本地数据库解决方案

现有代码问题梳理

  • 字段赋值错误:phone字段错误赋值为联系人姓名displayName,未读取真实手机号数据
  • 异步调用遗漏await:insert方法为异步Future类型,循环调用时未添加等待逻辑,会导致数据库写入操作未完成就提前结束,出现数据丢失
  • 联系人手机号结构未处理:contact_service返回的联系人手机号是List<Item>类型,需手动提取对应值,同时要做空值判断避免空指针异常
  • 建表语句不规范:synced为INTEGER类型,默认值错误添加了单引号,不符合SQL语法规范
  • 边界判断缺失:循环固定取前10条联系人,当设备联系人总数小于10时会触发数组越界崩溃

修正后完整代码

import 'dart:io';
import 'package:contacts_service/contacts_service.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';

class ContactDatabase {
  static final _databaseName = "contact.db";
  static final _databaseVersion = 1;
  List<Contact> contact = [];

  ContactDatabase._init();

  static Database? _database;
  static final ContactDatabase instance = ContactDatabase._init();

  Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDB();
    return _database!;
  }

  _initDB() async {
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    String path = join(documentsDirectory.path, _databaseName);
    return await openDatabase(path,
        version: _databaseVersion, onCreate: _onCreate);
  }

  static final contact_table = 'contacts';
  static final id = 'id';
  static final name = 'name';
  static final phone = 'phone';
  static final avatar = 'avatar';
  static final synced = 'synced';

  // 修正建表语句synced字段默认值格式
  static final CREATE_TABLE = "CREATE TABLE $contact_table"
      "($name TEXT ,"
      "$phone TEXT ,"
      "$avatar TEXT ,"
      "$synced INTEGER DEFAULT 0 ,"
      "$id INTEGER PRIMARY KEY AUTOINCREMENT)";

  Future _onCreate(Database db, int version) async {
    await db.execute(CREATE_TABLE);
  }

  Future<int> insert(String table, Map<String, dynamic> row) async {
    Database db = await instance.database;
    return await db.insert(table, row,
        conflictAlgorithm: ConflictAlgorithm.replace);
  }

  Future<void> insertContact() async {
    contact = (await ContactsService.getContacts(withThumbnails: false)).toList();
    // 修正边界判断,取实际联系人总数和10的最小值
    int loopCount = contact.length < 10 ? contact.length : 10;
    for (var i = 0; i < loopCount; i++) {
      // 提取手机号,做空判断处理
      String phoneNum = "NA";
      if(contact[i].phones != null && contact[i].phones!.isNotEmpty) {
        phoneNum = contact[i].phones!.first.value ?? "NA";
      }
      Map<String, dynamic> row = {
        name: contact[i].displayName ?? "未知联系人",
        phone: phoneNum,
        avatar: contact[i].avatar != null && contact[i].avatar!.isNotEmpty 
            ? contact[i].avatar.toString() 
            : "NA",
        synced: 1,
      };
      // 添加await等待写入完成
      await insert(contact_table, row);
    }
  }

  Future<List<Map<String, dynamic>>> queryAllRows() async {
    Database db = await instance.database;
    return await db.query(contact_table);
  }
}

使用注意事项

  • 调用存储方法前需要先申请联系人读取权限,安卓端需在AndroidManifest.xml中添加READ_CONTACTS权限配置,iOS端需在Info.plist中添加NSContactsUsageDescription权限使用说明
  • 调用存储方法时需添加await等待操作完成:await ContactDatabase.instance.insertContact();
  • 如果需要存储同一联系人的多个手机号,可以将手机号列表转为JSON字符串存储,也可以单独建立手机号关联表存储多号码数据

内容的提问来源于stack exchange,提问作者Nitesh Sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 05:18:02