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

Flutter中如何实现API获取联系人的选择功能及UI定制?

联系人列表UI定制与API数据获取解决方案

我在UI定制上遇到问题,参考图为分组式联系人列表(带首字母分组标题、选中项高亮+勾选标记),尝试自定义效果未果。同时想了解如何实现从API获取联系人并添加选择功能。当前代码如下:

ListView(
  children: con.contactOwnerList.map((element) {
    return Column(
             mainAxisAlignment: MainAxisAlignment.start,
             crossAxisAlignment: CrossAxisAlignment.start,
             children: [
               Text(
                 element.name
                        .toString()
                        .toUpperCase()
                        .characters
                        .first,
                ),
                Container(
                  color: Theme.of(context).colorScheme.onPrimary,
                  child: ListTile(
                           leading: const CircleAvatar(),
                           title: Text('${element.name}'),
                           subtitle: Text('${element.email}'),
                           trailing: SvgPicture.asset(
                            Assets.images.svg.createCase.circleCheck),
                              onTap: () {},
                            ),
                ),
                const Divider()
              ],
             );
   }).toList(),
),

一、UI定制实现(匹配参考图效果)

  • 首字母分组标题优化:给分组字母添加加粗、灰色字体、左右间距,避免和列表项拥挤在一起
  • 选中状态控制:维护选中ID集合,点击列表项时切换状态,动态显示勾选图标并高亮背景
  • 布局简化:移除多余容器,用ListTile自带的selected属性实现高亮,自定义分割线缩进对齐内容

二、API获取联系人与选择功能实现

  • API数据获取:在页面初始化时发起请求,获取数据后更新列表,同时处理加载、错误状态
  • 选择状态管理:用Set存储选中的联系人ID,点击时添加/移除
  • 状态联动:UI根据选中状态自动更新样式

修改后的完整代码示例

class ContactListScreen extends StatefulWidget {
  const ContactListScreen({super.key});

  @override
  State<ContactListScreen> createState() => _ContactListScreenState();
}

class _ContactListScreenState extends State<ContactListScreen> {
  final ContactController con = Get.find<ContactController>(); // 替换为你的状态管理方式
  final Set<String> _selectedContactIds = {};
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    _fetchContactsFromApi();
  }

  // 从API拉取联系人数据
  Future<void> _fetchContactsFromApi() async {
    try {
      setState(() => _isLoading = true);
      // 替换为你的实际API请求逻辑
      final contacts = await ApiService.getContacts();
      con.contactOwnerList.assignAll(contacts);
    } catch (e) {
      debugPrint('获取联系人失败:$e');
      // 可添加错误提示弹窗
    } finally {
      setState(() => _isLoading = false);
    }
  }

  // 切换联系人选中状态
  void _toggleContactSelection(String contactId) {
    setState(() {
      _selectedContactIds.contains(contactId) 
          ? _selectedContactIds.remove(contactId) 
          : _selectedContactIds.add(contactId);
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) {
      return const Center(child: CircularProgressIndicator());
    }

    return ListView.builder(
      itemCount: con.contactOwnerList.length,
      itemBuilder: (context, index) {
        final element = con.contactOwnerList[index];
        final isSelected = _selectedContactIds.contains(element.id);
        // 判断是否显示首字母分组标题(当前项首字母与上一项不同时显示)
        final showGroupTitle = index == 0 || 
            element.name.toString().toUpperCase()[0] != 
            con.contactOwnerList[index-1].name.toString().toUpperCase()[0];

        return Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 首字母分组标题
            if (showGroupTitle)
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                child: Text(
                  element.name.toString().toUpperCase()[0],
                  style: const TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.bold,
                    color: Colors.grey,
                  ),
                ),
              ),
            // 联系人列表项
            ListTile(
              leading: CircleAvatar(
                child: Text(element.name.toString().toUpperCase()[0]), // 动态显示首字母
              ),
              title: Text(element.name),
              subtitle: Text(element.email),
              trailing: isSelected 
                  ? SvgPicture.asset(Assets.images.svg.createCase.circleCheck)
                  : null,
              selected: isSelected,
              selectedTileColor: Theme.of(context).colorScheme.primaryContainer, // 选中高亮背景
              onTap: () => _toggleContactSelection(element.id),
            ),
            // 缩进分割线
            const Divider(height: 1, indent: 72),
          ],
        );
      },
    );
  }
}

补充说明

  • 替换ApiService.getContacts()为你的实际API请求方法,确保返回的联系人模型包含id字段
  • 若使用GetX/Provider等状态管理框架,可将_selectedContactIds移至ViewModel中,实现跨组件状态同步
  • 头像部分可根据需求替换为联系人头像图片,增强视觉效果

内容的提问来源于stack exchange,提问作者Tinut Chan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 19:10:03