介绍
ExpansionPanel
官方提供的一个扩展面板的控件,使用它可以轻松实现展开收起的功能。
ExpansionPanel
ExpansionPanel({
@required this.headerBuilder,
@required this.body,
this.isExpanded = false,
this.canTapOnHeader = false,
}) : assert(headerBuilder != null),
assert(body != null),
assert(isExpanded != null),
assert(canTapOnHeader != null);
ExpansionPanel
的构造函数有4个参数:
headerBuilder
:header
样式body
:body
样式isExpanded
:是否展开canTapOnHeader
:header
是否可点击
ExpansionPanelList
Creates an expansion panel to be used as a child for [ExpansionPanelList].
源码注释提到ExpansionPanel
不能单独使用,要作为ExpansionPanelList
的child
。
const ExpansionPanelList({
Key key,
this.children = const <ExpansionPanel>[],
this.expansionCallback,
this.animationDuration = kThemeAnimationDuration,
}) : assert(children != null),
assert(animationDuration != null),
_allowOnlyOnePanelOpen = false,
initialOpenPanelValue = null,
super(key: key);
ExpansionPanelList
的构造函数有3个参数:
children
:ExpansionPanel
类型的数组expansionCallback
:点击时的回调,会返回对应点击的index
和当前isExpanded
的值animationDuration
:动画持续时间
效果
Demo代码
class Item {
String familyName;
List<String> fullName;
bool isExpanded;
Item(this.familyName, this.fullName, this.isExpanded);
}
List<Item> getDataSource() {
return [
Item('张', ['张一一', '张二二', '张三三', '张四四'], false),
Item('王', ['王一一', '王二二', '王三三', '王四四'], false),
Item('李', ['李一一', '李二二', '李三三', '李四四'], false),
Item('赵', ['赵一一', '赵二二', '赵三三', '赵四四'], false),
Item('周', ['周一一', '周二二', '周三三', '周四四'], false),
Item('吴', ['吴一一', '吴二二', '吴三三', '吴四四'], false),
Item('孙', ['孙一一', '孙二二', '孙三三', '孙四四'], false),
];
}
List<Item> _dataSource = getDataSource();
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
_dataSource[index].isExpanded = !isExpanded;
});
},
children: _dataSource.map<ExpansionPanel>((Item item) {
return ExpansionPanel(
canTapOnHeader: true,
isExpanded: item.isExpanded,
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text(item.familyName),
);
},
body: ListView.separated(
itemBuilder: (BuildContext context, int index) {
return Container(
padding: EdgeInsets.fromLTRB(15, 10, 15, 10),
child: Text(item.fullName[index]),
);
},
separatorBuilder: (BuildContext context, int index) {
return new Container(height: 1.0, color: Color(0xfff2f2f2));
},
itemCount: item.fullName.length,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
),
);
}).toList(),
animationDuration: Duration(microseconds: 100),
),
);
}
总结
代码简单易懂不做多解释,有几个需要注意的点这里总结下:
-
在
build
方法直接return
一个ExpansionPanelList
会发现没有任何内容展示。Log
提示:RenderListBody must have unlimited space along its main axis.
也就是说要保持RenderListBody
的主轴上必须有无限的空间。这里我们在外面包一层SingleChildScrollView
来达到主轴无限的空间的目的。 -
注意
ListView.separated
中的属性设置。
shrinkWrap: true, // 多用于嵌套`listView`内容大小不确定的情况
physics: NeverScrollableScrollPhysics(), // 禁止滚动,这里想要的效果是展开后跟着ExpansionPanelList一起滚动而不是内部ListView单独滚动
- 如果你的需求是只能展开一个,点击时其他展开的自动收起的情况,使用
ExpansionPanelList.radio
配合ExpansionPanelRadio
能达到你想要的效果。ExpansionPanelRadio
是ExpansionPanel
的子类,多了一个value
作为唯一标识,用来区分每个ExpansionPanelRadio
。