Dart/Flutter中移除嵌套值为空的列表元素及多嵌套处理
解决联系人同步Firebase的空值报错与多字段处理问题
问题根源分析
- 过滤逻辑无效:原代码里直接调用
contact.phones?.first,当phones是空列表时会直接抛出Bad state: No element错误,根本走不到判断逻辑;而且最后映射数据时用的是原contacts列表,不是过滤后的result,等于白做了过滤。 - 空字段未安全处理:直接访问
k.phones?.first.value或写错的k.email?.value(注意contacts_service中邮箱是emails列表),当列表为空时会触发错误。 - 重复代码冗余:电话和邮箱的格式化逻辑重复,没有复用。
正确解决方案
步骤1:修复过滤逻辑(可选)
如果想保留所有联系人(哪怕无电话/邮箱),可以跳过过滤直接在映射时处理空值;如果只想保留有有效联系方式的联系人,过滤条件要判断列表是否非空:
List<Contact> filteredContacts = contacts.where((contact) { // 保留有电话或邮箱的联系人 return (contact.phones?.isNotEmpty == true) || (contact.emails?.isNotEmpty == true); }).toList();
步骤2:安全处理嵌套字段 + 复用格式化逻辑
提取通用格式化函数,对每个字段做空安全判断,避免直接访问列表的first元素:
// 通用格式化函数:处理空值,移除空格和特殊字符 String? formatContactField(String? value) { if (value == null || value.trim().isEmpty) return null; return value .replaceAll(RegExp(r"\s\b|\b\s"), "") .replaceAll(RegExp(r'[^\w\s]+'), ''); }
完整修改后的代码
import '../../backend/backend.dart'; import '../../flutter_flow/flutter_flow_theme.dart'; import '../../flutter_flow/flutter_flow_util.dart'; import 'index.dart'; import 'package:flutter/material.dart'; import 'package:contacts_service/contacts_service.dart'; // 通用格式化函数:处理空值并清理特殊字符 String? formatContactField(String? value) { if (value == null || value.trim().isEmpty) return null; return value .replaceAll(RegExp(r"\s\b|\b\s"), "") .replaceAll(RegExp(r'[^\w\s]+'), ''); } Future syncContactstoFirebase(String? userID) async { if (userID == null) { print("User ID is null, aborting sync"); return; } List<Contact> contacts = await ContactsService.getContacts(); // 可选:过滤掉没有电话和邮箱的联系人 List<Contact> filteredContacts = contacts.where((contact) { return (contact.phones?.isNotEmpty == true) || (contact.emails?.isNotEmpty == true); }).toList(); final instance = FirebaseFirestore.instance; CollectionReference collection = instance.collection('users').doc(userID).collection('contacts'); Map<String, dynamic> data = { 'contacts': filteredContacts .map((k) => { 'name': k.displayName ?? '未知名称', // 给空名称设置默认值 'phone': formatContactField(k.phones?.isNotEmpty == true ? k.phones!.first.value : null), 'email': formatContactField(k.emails?.isNotEmpty == true ? k.emails!.first.value : null), }) .toList(), }; return collection .doc() .set(data) .then((value) => print("Contacts Updated")) .catchError((error) => print("Failed to update Contacts: $error")); }
关键修改点说明
- 空安全判断:所有访问
phones/emails列表的地方,先判断isNotEmpty,再安全获取first.value,彻底避免空列表取元素的错误。 - 格式化复用:把重复的字符串处理逻辑抽成函数,减少冗余,方便后续维护。
- 默认值处理:给
displayName设置默认值,避免Firebase存储null(可根据需求调整)。 - 用户ID校验:先判断
userID是否为空,避免后续Firestore操作报错。 - 过滤生效:映射数据时使用
filteredContacts而非原contacts,确保过滤逻辑真正生效。
内容的提问来源于stack exchange,提问作者user2818170
相关产品推荐
相关产品推荐

