在 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')),
],
),
),
],
),
),
),
);
}
}