#在这里插入图片描述

案例概述

本案例展示如何使用 ExpansionTile 创建可展开/折叠的列表。可展开列表是组织分类信息的有效方式,广泛应用于菜单、常见问题、设置选项等场景。通过展开和折叠,用户可以专注于感兴趣的内容,减少屏幕混乱。ExpansionTile 提供了内置的展开/折叠动画和状态管理,使实现变得简单。

可展开列表需要管理每个项的展开状态、支持嵌套列表、处理动态加载等。此外,还应考虑全选/取消全选、搜索过滤、键盘导航等高级功能。

核心概念

1. ExpansionTile

  • 可展开的列表项;
  • 支持标题和子内容;
  • 支持展开/折叠动画。

2. 展开状态

  • initiallyExpanded:初始是否展开;
  • onExpansionChanged:展开状态改变回调;
  • 支持多个独立的展开/折叠状态。

3. 嵌套列表

  • ExpansionTile 内部可包含 ListView;
  • 支持多层嵌套;
  • 支持动态加载子项。

代码详解

1. 基础展开列表

ExpansionTile(
  title: Text('分类'),
  children: [
    ListTile(title: Text('项目1')),
    ListTile(title: Text('项目2')),
  ],
)

2. 展开状态管理

Map<int, bool> _expandedStates = {};

ExpansionTile(
  initiallyExpanded: _expandedStates[index] ?? false,
  onExpansionChanged: (expanded) {
    setState(() => _expandedStates[index] = expanded);
  },
  title: Text('分类'),
  children: [...],
)

3. 嵌套列表

ExpansionTile(
  title: Text('分类'),
  children: [
    ListView.builder(
      shrinkWrap: true,
      physics: NeverScrollableScrollPhysics(),
      itemCount: items.length,
      itemBuilder: (context, index) {
        return ListTile(title: Text(items[index]));
      },
    ),
  ],
)

高级话题:展开列表的企业级应用

1. 动态加载子项

ExpansionTile(
  title: Text('分类'),
  onExpansionChanged: (expanded) {
    if (expanded && !_loadedCategories.contains(index)) {
      _loadSubItems(index);
    }
  },
  children: [...],
)

2. 展开列表搜索

List<Map<String, dynamic>> get _filteredCategories {
  return _categories.where((cat) {
    return cat['title'].toLowerCase().contains(_searchQuery.toLowerCase()) ||
        cat['items'].any((item) => item.toLowerCase().contains(_searchQuery.toLowerCase()));
  }).toList();
}

3. 展开列表排序

void _sortCategories(String sortBy) {
  setState(() {
    _categories.sort((a, b) {
      if (sortBy == 'name') {
        return a['title'].compareTo(b['title']);
      }
      return a['items'].length.compareTo(b['items'].length);
    });
  });
}

4. 展开列表的全选/取消全选

void _expandAll() {
  setState(() {
    _expandedStates.updateAll((key, value) => true);
  });
}

void _collapseAll() {
  setState(() {
    _expandedStates.updateAll((key, value) => false);
  });
}

5. 展开列表的计数显示

ExpansionTile(
  title: Row(
    children: [
      Text('分类'),
      SizedBox(width: 8),
      Badge(label: Text(items.length.toString())),
    ],
  ),
  children: [...],
)

6. 展开列表的操作菜单

ExpansionTile(
  trailing: PopupMenuButton<String>(
    itemBuilder: (context) => [
      PopupMenuItem(value: 'edit', child: Text('编辑')),
      PopupMenuItem(value: 'delete', child: Text('删除')),
    ],
  ),
  title: Text('分类'),
  children: [...],
)

7. 展开列表的键盘导航

Focus(
  onKey: (node, event) {
    if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
      setState(() => _expandedStates[index] = !_expandedStates[index]!);
      return KeyEventResult.handled;
    }
    return KeyEventResult.ignored;
  },
  child: ExpansionTile(...),
)

8. 展开列表的无障碍支持

ExpansionTile(
  title: Semantics(
    label: '分类 ${index + 1}',
    child: Text('分类'),
  ),
  children: [...],
)

9. 展开列表的动画自定义

ExpansionTile(
  title: Text('分类'),
  collapsedBackgroundColor: Colors.grey.shade100,
  backgroundColor: Colors.blue.shade50,
  children: [...],
)

10. 展开列表的测试

test('展开列表展开', () {
  expect(_expandedStates[0], false);
  _toggleExpanded(0);
  expect(_expandedStates[0], true);
});

通过这些企业级技巧,你可以构建出功能完整的展开列表系统。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐