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 或结合 StatefulWidget 与 onExpansionChanged 来管理状态。

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')),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
相关推荐
陆断枫10 小时前
Flutter 3.47 动画引擎盘点:Impeller 全平台,和那些"新特性"
flutter
程序员老刘11 小时前
Flet 1.0 思路很好,可惜来晚了
flutter·ai编程·客户端
m0_7381858217 小时前
Flutter 鸿蒙化实战:qr_code_scanner_plus 适配 OpenHarmony,二维码扫描
flutter·华为·harmonyos·鸿蒙
恋猫de小郭17 小时前
Flutter + EmbeddingGemma 2,谷歌发布完全端侧的 AI Edge Foresight
android·前端·flutter
君赏1 天前
基于 FlutterPatch 快速接入 Flutter 热更新
flutter
律宏阔1 天前
Dart FFI 内存管理:用 using + Arena 替代嵌套 try-finally
前端·flutter
律宏阔1 天前
Flutter 调用 Go:从 c-shared + ffigen 到 @Native + Native Assets 踩坑记录
前端·flutter
律宏阔1 天前
Dart FFI 回调无法使用局部变量?使用 NativeCallable.isolateLocal
前端·flutter
天空之城--2 天前
Android一周动态:AI编程、Compose与车载生态新信号
android·flutter
码林鼠2 天前
Flutter之Widget/State 入门文档
flutter