Flutter 折叠组件 ExpansionTile ExpansionPanelList

在 Flutter 中,ExpansionTile 是一个非常常用的组件,用于创建可折叠/展开的列表项。它通常用于实现 FAQ、设置面板或分组列表等功能。

1. 基本使用

最基础的用法是提供一个标题(title)和展开后显示的内容列表(children)。通常将其放在 ListView 中。

less 复制代码
ExpansionTile(
  title: Text('点击展开'),
  children: <Widget>[
    ListTile(title: Text('内容 1')),
    ListTile(title: Text('内容 2')),
  ],
)

2. 常用属性

ExpansionTile 提供了丰富的属性来自定义外观和行为:

  • title: 必填,标题组件。
  • subtitle: 子标题,显示在标题下方。
  • leading: 标题左侧的组件,通常用于放置图标(Icon)。
  • trailing: 标题右侧的组件,默认是一个展开/收起的箭头图标。
  • children: 必填,展开后显示的内容列表。
  • initiallyExpanded : 布尔值,设置初始状态是否展开(默认为 false)。
  • onExpansionChanged : 展开/折叠状态改变时的回调函数,接收一个 bool 参数表示当前是否展开。
  • backgroundColor: 展开时的背景颜色。
  • childrenPadding: 展开内容区域的内边距。

3. 进阶自定义

  • 调整内边距 :使用 contentPadding 可以调整标题区域的内边距,从而改变高度;使用 headerPadding 可以移除或自定义标题行的默认水平内边距。
  • 图标位置 :默认箭头在右侧,可以通过 controlAffinity 属性将其移动到左侧。
  • 动态控制 :如果需要代码控制展开/收起(例如选择某项后自动折叠),可以使用 ExpansibleController 或结合 StatefulWidgetonExpansionChanged 来管理状态。

4. 完整示例

less 复制代码
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('ExpansionTile 示例')),
        body: ListView(
          children: [
            ExpansionTile(
              leading: Icon(Icons.info),
              title: Text('更多信息'),
              subtitle: Text('点击查看详情'),
              initiallyExpanded: false,
              backgroundColor: Colors.grey[200],
              onExpansionChanged: (bool expanded) {
                print(expanded ? '已展开' : '已折叠');
              },
              childrenPadding: EdgeInsets.all(16.0),
              children: [
                ListTile(title: Text('这是展开后的详细内容 1')),
                ListTile(title: Text('这是展开后的详细内容 2')),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

全屏的Container套用ExpansionTile也是全屏的,可以设置对齐方式,按实际大小布局。

less 复制代码
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(home: HomePage()));
}

class HomePage extends StatefulWidget {
  HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  var res = "no result";
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("联想输入")),
      body: Container(
        // 如果不设置alignment,ExpansionTile就是全屏的
        alignment: Alignment.topLeft,
        color: Colors.green,
        width: MediaQuery.of(context).size.width,
        height: MediaQuery.of(context).size.height,
        child: const ExpansionTile(
          title: Text('背景颜色不变'),
          // 折叠时候默认是透明色
          // collapsedBackgroundColor: Colors.white,
          backgroundColor: Colors.white, // 展开时
          children: [
            ListTile(title: Text('展开后的内容1'), subtitle: Text('展开后的内容2')),
            ListTile(title: Text('展开后的内容1'), subtitle: Text('展开后的内容2')),
          ],
        ),
      ),
    );
  }
}

ExpansionPanelList

必须用一个可滚动widet作为父容器。

isExpanded不是默认状态,是当前状态。因为需要全局变量控制。

less 复制代码
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(home: HomePage()));
}

class HomePage extends StatefulWidget {
  HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class ItemState {
  bool isExpanded = false;
}

class _HomePageState extends State<HomePage> {
  List<ItemState> itemStates = [];
  _HomePageState() {
    for (int i = 0; i < 2; i++) {
      itemStates.add(ItemState());
    }
  }
  var res = "no result";
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("联想输入")),
      body: Container(
        // 如果不设置alignment,ExpansionTile就是全屏的
        alignment: Alignment.topLeft,
        color: Colors.blueGrey,
        width: MediaQuery.of(context).size.width,
        height: MediaQuery.of(context).size.height,
        child: SingleChildScrollView(
          child: ExpansionPanelList(
            // 点击展开或者关闭时触发
            expansionCallback: (index, isExpanded) {
              setState(() {
                res = isExpanded ? '展开' : '折叠';
                itemStates[index].isExpanded = isExpanded;
                debugPrint("$index $res");
              });
            },
            // 展开或者收起来的动画
            animationDuration: Duration(milliseconds: 300),

            // 分割线的颜色
            dividerColor: Colors.black,

            // 展开或者收起来的图标颜色
            expandIconColor: Colors.orange,
            children: [
              ExpansionPanel(
                // 默认是否展开
                isExpanded: itemStates[0].isExpanded,
                // 背景颜色
                backgroundColor: Colors.deepPurple,
                // 是否可以点击标题
                canTapOnHeader: true,
                // 收起来的时候显示的内容
                headerBuilder: (context, isExpanded) {
                  return ListTile(title: Text('背景颜色不变'));
                },
                // 展开后的内容
                body: Column(
                  children: [
                    ListTile(title: Text('展开后的内容1'), subtitle: Text('展开后的内容2')),
                    ListTile(title: Text('展开后的内容1'), subtitle: Text('展开后的内容2')),
                  ],
                ),
              ),
              ExpansionPanel(
                // 默认是否展开,默认是false
                isExpanded: itemStates[1].isExpanded,
                // 背景颜色
                backgroundColor: Colors.transparent,
                // 是否可以点击标题
                canTapOnHeader: true,
                // 收起来的时候显示的内容
                headerBuilder: (context, isExpanded) {
                  return ListTile(title: Text('背景颜色不变'));
                },
                // 展开后的内容
                body: Column(
                  children: [
                    ListTile(title: Text('展开后的内容1'), subtitle: Text('展开后的内容2')),
                    ListTile(title: Text('展开后的内容1'), subtitle: Text('展开后的内容2')),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
相关推荐
不羁的木木1 天前
给鸿蒙 App 增加用系统应用打开文件的能力 —— open_app_file 的鸿蒙使用指南
flutter·harmonyos
HouWan1 天前
Flutter: MediaQuery.of(context) 为什么可能拖慢页面?
android·flutter·ios
西西学代码1 天前
flutter---井字游戏
flutter
tushanxing1 天前
Day 2: 容器与单子布局基础
flutter
程序员老刘2 天前
跨平台开发地图 | 2026年9月
flutter·客户端
HouWan2 天前
Flutter iOS UISceneDelegate 迁移指南:理清新的 Scene 生命周期
flutter·ios·app
技术任我行XTing2 天前
【DFX系列】Flutter 鸿蒙应用外接纹理介绍及问题定位
flutter·harmonyos
恋猫de小郭2 天前
Flutter GSoC 2026 提案进度解读,补上 DevTools、FFI 和原生平台的关键缺口
android·前端·flutter