Flutter - Collapsing ExpansionTile after choosing an item

后端 未结 6 1417
傲寒
傲寒 2020-11-27 20:04

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

6条回答
  •  南笙
    南笙 (楼主)
    2020-11-27 21:02

    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:

    • ExpandableSection: a widget that can be easily controlled by one parameter "expanded".
    • RotatableSection: a widget to rotate the "Expand More" icon based on one parameter.

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

提交回复
热议问题