I\'m trying to get ExpansionTile to collapse after I choose an item, but it does not close the list that was opened.
I tried to use the onExpansio
None of the provided solutions pleased me.
I ended up creating a custom ExpandableListTile. As you can see below, its code is very brief and easy to customize.
I also had to create two supporting classes (that only handle the required animations) to build my widget:
The main class:
class ExpandableListTile extends StatelessWidget {
const ExpandableListTile({Key key, this.title, this.expanded, this.onExpandPressed, this.child}) : super(key: key);
final Widget title;
final bool expanded;
final Widget child;
final Function onExpandPressed;
@override
Widget build(BuildContext context) {
return Column(children: [
ListTile(
title: title,
onTap: onExpandPressed,
trailing: IconButton(
onPressed: onExpandPressed,
// icon: Icon(Icons.expand_more),
icon: RotatableSection(
rotated: expanded,
child: SizedBox(height: 30, width: 30, child: Icon(Icons.expand_more),)
),
),
),
ExpandableSection(child: child, expand: expanded,)
]);
}
}
Usage (simplified):
//...
return ExpandableListTile(
onExpandPressed: (){ setState((){ _expandedItem = 0;}) },
title: Text('Item'),
expanded: _expandedItem==0,
child: Padding(
padding: const EdgeInsets.fromLTRB(8,0,0,0),
child: Container(
color: Color.fromRGBO(0, 0, 0, .2),
child: Column(children: [
ListTile(title: Text('Item 1')),
ListTile(title: Text('Item 2')),
ListTile(title: Text('Item 3')),
ListTile(title: Text('Item 4'))
],),
),
),
),
//...
The ExpandableSection class:
class ExpandableSection extends StatefulWidget {
final Widget child;
final bool expand;
ExpandableSection({this.expand = false, this.child});
@override
_ExpandableSectionState createState() => _ExpandableSectionState();
}
class _ExpandableSectionState extends State with SingleTickerProviderStateMixin {
AnimationController animationController;
Animation sizeAnimation;
Animation opacityAnimation;
@override
void initState() {
super.initState();
prepareAnimations();
_runExpandCheck();
}
///Setting up the animation
void prepareAnimations() {
animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 300),);
sizeAnimation = CurvedAnimation(parent: animationController, curve: Curves.fastOutSlowIn,);
opacityAnimation = CurvedAnimation(parent: animationController, curve: Curves.slowMiddle,);
}
void _runExpandCheck() {
if(widget.expand) { animationController.forward(); }
else { animationController.reverse(); }
}
@override
void didUpdateWidget(ExpandableSection oldWidget) {
super.didUpdateWidget(oldWidget);
_runExpandCheck();
}
@override
void dispose() {
animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: opacityAnimation,
child: SizeTransition(
axisAlignment: 1.0,
sizeFactor: sizeAnimation,
child: widget.child
)
);
}
}
The RotatableSection class:
class RotatableSection extends StatefulWidget {
final Widget child;
final bool rotated;
final double initialSpin;
final double endingSpin;
RotatableSection({this.rotated = false, this.child, this.initialSpin=0, this.endingSpin=0.5});
@override
_RotatableSectionState createState() => _RotatableSectionState();
}
class _RotatableSectionState extends State with SingleTickerProviderStateMixin {
AnimationController animationController;
Animation animation;
@override
void initState() {
super.initState();
prepareAnimations();
_runCheck();
}
final double _oneSpin = 6.283184;
///Setting up the animation
void prepareAnimations() {
animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 300),
lowerBound: _oneSpin * widget.initialSpin, upperBound: _oneSpin * widget.endingSpin, );
animation = CurvedAnimation( parent: animationController, curve: Curves.linear, );
}
void _runCheck() {
if(widget.rotated) { animationController.forward(); }
else { animationController.reverse(); }
}
@override
void didUpdateWidget(RotatableSection oldWidget) {
super.didUpdateWidget(oldWidget);
_runCheck();
}
@override
void dispose() {
animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animationController,
child: widget.child,
builder: (BuildContext context, Widget _widget) {
return new Transform.rotate(
angle: animationController.value,
child: _widget,
);
},
);
}
}