如何自定义 ExpandableListView 目前仅显示一项“已扩展”

Posted

技术标签:

【中文标题】如何自定义 ExpandableListView 目前仅显示一项“已扩展”【英文标题】:How to customize ExpandableListView to show only one item 'expanded' at the moment 【发布时间】:2019-11-10 23:49:37 【问题描述】:

我使用从how-to-create-expandable-listview-in-flutter 获得的以下代码 要在 Flutter 中创建 ExpandableListView,并且我需要我的应用在点击新项目时仅显示一个展开的项目而所有其他项目都折叠起来,请帮助我如何为此目的自定义代码:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() 
  runApp(new MaterialApp(home: new Home()));


class Home extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return new Scaffold(
      backgroundColor: Colors.grey,
      appBar: new AppBar(
        title: new Text("Expandable List"),
        backgroundColor: Colors.redAccent,
      ),
      body: new ListView.builder(
        itemBuilder: (BuildContext context, int index) 
          return new ExpandableListView(title: "Title $index");
        ,
        itemCount: 5,
      ),
    );
  


class ExpandableListView extends StatefulWidget 
  final String title;

  const ExpandableListView(Key key, this.title) : super(key: key);

  @override
  _ExpandableListViewState createState() => new _ExpandableListViewState();


class _ExpandableListViewState extends State<ExpandableListView> 
  bool expandFlag = false;

  @override
  Widget build(BuildContext context) 
    return new Container(
      margin: new EdgeInsets.symmetric(vertical: 1.0),
      child: new Column(
        children: <Widget>[
          new Container(
            color: Colors.blue,
            padding: new EdgeInsets.symmetric(horizontal: 5.0),
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                new IconButton(
                    icon: new Container(
                      height: 50.0,
                      width: 50.0,
                      decoration: new BoxDecoration(
                        color: Colors.orange,
                        shape: BoxShape.circle,
                      ),
                      child: new Center(
                        child: new Icon(
                          expandFlag ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
                          color: Colors.white,
                          size: 30.0,
                        ),
                      ),
                    ),
                    onPressed: () 
                      setState(() 
                        expandFlag = !expandFlag;
                      );
                    ),
                new Text(
                  widget.title,
                  style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
                )
              ],
            ),
          ),
          new ExpandableContainer(
              expanded: expandFlag,
              child: new ListView.builder(
                itemBuilder: (BuildContext context, int index) 
                  return new Container(
                    decoration:
                        new BoxDecoration(border: new Border.all(width: 1.0, color: Colors.grey), color: Colors.black),
                    child: new ListTile(
                      title: new Text(
                        "Cool $index",
                        style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
                      ),
                      leading: new Icon(
                        Icons.local_pizza,
                        color: Colors.white,
                      ),
                    ),
                  );
                ,
                itemCount: 15,
              ))
        ],
      ),
    );
  


class ExpandableContainer extends StatelessWidget 
  final bool expanded;
  final double collapsedHeight;
  final double expandedHeight;
  final Widget child;

  ExpandableContainer(
    @required this.child,
    this.collapsedHeight = 0.0,
    this.expandedHeight = 300.0,
    this.expanded = true,
  );

  @override
  Widget build(BuildContext context) 
    double screenWidth = MediaQuery.of(context).size.width;
    return new AnimatedContainer(
      duration: new Duration(milliseconds: 500),
      curve: Curves.easeInOut,
      width: screenWidth,
      height: expanded ? expandedHeight : collapsedHeight,
      child: new Container(
        child: child,
        decoration: new BoxDecoration(border: new Border.all(width: 1.0, color: Colors.blue)),
      ),
    );
  

【问题讨论】:

***.com/questions/51378727/…的可能重复 Flutter- ExpansionTile expand and collapse on click的可能重复 我认为这不像我的,我不能用它的代码来解决我的问题,因为我有一个由两个部分组成的Containers 列表:我点击的固定布局以及可折叠和展开的可展开布局。我需要的是折叠所有项目,除了我刚刚点击的项目。 它对我有用。 Reference请查看。 你试过我的解决方案here吗? 【参考方案1】:

如果您想坚持使用这种方法,可以将 Home 配置为 StatefulWidget 来管理 ListView 项的展开/缩回状态。为了只让被点击的小部件执行展开/缩回动作,我们需要跟踪哪个列表项被点击了。

ListView.builder(
  itemBuilder: (BuildContext context, int index) 
    return GestureDetector(
      onTap: () 
        debugPrint('List item $index tapped $expand');
        setState(() 
          /// XOR operand returns when either or both conditions are true
          /// `tapped == null` on initial app start, [tapped] is null
          /// `index == tapped` initiate action only on tapped item
          /// `!expand` should check previous expand action
          expand = ((tapped == null) || ((index == tapped) || !expand)) ? !expand : expand;
          /// This tracks which index was tapped
          tapped = index;
          debugPrint('current expand state: $expand');
        );
      ,
      /// We set ExpandableListView to be a Widget
      /// for Home StatefulWidget to be able to manage 
      /// ExpandableListView expand/retract actions
      child: expandableListView(
        index,
        "Title $index",
        index == tapped? expand: false,
      ),
    );
  
)

完整代码

void main() 
  runApp(MaterialApp(home: Home()));


class Home extends StatefulWidget 
  @override
  createState() => HomeState();


class HomeState extends State<Home> 
  bool expand = false;
  int? tapped;
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      backgroundColor: Colors.grey,
      appBar: AppBar(
        title: Text("Expandable List"),
        backgroundColor: Colors.redAccent,
      ),
      body: ListView.builder(
        itemBuilder: (BuildContext context, int index) 
          return GestureDetector(
            onTap: () 
              debugPrint('List item $index tapped $expand');
              setState(() 
                /// XOR operand returns when either or both conditions are true
                /// `tapped == null` on initial app start, [tapped] is null
                /// `index == tapped` initiate action only on tapped item
                /// `!expand` should check previous expand action
                expand = ((tapped == null) || ((index == tapped) || !expand)) ? !expand : expand;
                /// This tracks which index was tapped
                tapped = index;
                debugPrint('current expand state: $expand');
              );
            ,
            /// We set ExpandableListView to be a Widget
            /// for Home StatefulWidget to be able to manage
            /// ExpandableListView expand/retract actions
            child: expandableListView(
              index,
              "Title $index",
              index == tapped? expand: false,
            ),
            // child: ExpandableListView(
            //   title: "Title $index",
            //   isExpanded: expand,
            // ),
          );
        ,
        itemCount: 5,
      ),
    );
  

  Widget expandableListView(int index, String title, bool isExpanded) 
    debugPrint('List item build $index $isExpanded');
    return Container(
      margin: EdgeInsets.symmetric(vertical: 1.0),
      child: Column(
        children: <Widget>[
          Container(
            color: Colors.blue,
            padding: EdgeInsets.symmetric(horizontal: 5.0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                IconButton(
                    icon: Container(
                      height: 50.0,
                      width: 50.0,
                      decoration: BoxDecoration(
                        color: Colors.orange,
                        shape: BoxShape.circle,
                      ),
                      child: Center(
                        child: Icon(
                          isExpanded
                              ? Icons.keyboard_arrow_up
                              : Icons.keyboard_arrow_down,
                          color: Colors.white,
                          size: 30.0,
                        ),
                      ),
                    ),
                    onPressed: () 
                      // setState(() 
                      //   expandFlag = !expandFlag;
                      // );
                    ),
                Text(
                  title,
                  style: TextStyle(
                      fontWeight: FontWeight.bold, color: Colors.white),
                )
              ],
            ),
          ),
          ExpandableContainer(
              expanded: isExpanded,
              child: ListView.builder(
                itemBuilder: (BuildContext context, int index) 
                  return Container(
                    decoration: BoxDecoration(
                        border: Border.all(width: 1.0, color: Colors.grey),
                        color: Colors.black),
                    child: ListTile(
                      title: Text(
                        "Cool $index",
                        style: TextStyle(
                            fontWeight: FontWeight.bold, color: Colors.white),
                      ),
                      leading: Icon(
                        Icons.local_pizza,
                        color: Colors.white,
                      ),
                    ),
                  );
                ,
                itemCount: 15,
              ))
        ],
      ),
    );
  


class ExpandableContainer extends StatelessWidget 
  final bool expanded;
  final double collapsedHeight;
  final double expandedHeight;
  final Widget child;

  ExpandableContainer(
    required this.child,
    this.collapsedHeight = 0.0,
    this.expandedHeight = 300.0,
    this.expanded = true,
  );

  @override
  Widget build(BuildContext context) 
    double screenWidth = MediaQuery.of(context).size.width;
    return AnimatedContainer(
      duration: Duration(milliseconds: 500),
      curve: Curves.easeInOut,
      width: screenWidth,
      height: expanded ? expandedHeight : collapsedHeight,
      child: Container(
        child: child,
        decoration:
            BoxDecoration(border: Border.all(width: 1.0, color: Colors.blue)),
      ),
    );
  

【讨论】:

以上是关于如何自定义 ExpandableListView 目前仅显示一项“已扩展”的主要内容,如果未能解决你的问题,请参考以下文章

如何为自定义 ExpandableListView 调用 notifyDataSetChanged?

如何完全隐藏 ExpandableListView 的 groupIndicator?

Android自定义ViewGroup——带悬停标题的ExpandableListView

在 ExpandableListView 中,如何在子结果的末尾再显示一行?

使用自定义适配器实现 Expandable ListView

ExpandableListView - 自动折叠组