【问题标题】:How to create Expandable ListView in Flutter如何在 Flutter 中创建可扩展的 ListView
【发布时间】:2018-11-04 21:49:43
【问题描述】:

如何使用 Flutter 制作可扩展的 ListView,如下图所示?

我想制作ExpansionTiles 的可滚动列表视图,展开后会显示不可滚动的列表视图。

我尝试实现ExpansionTiles 的列表视图,其中我使用listView.builder(...) 嵌套了另一个列表视图。但是当我展开 ExpansionTile 时,列表视图并没有出现......

(截图仅供参考)

有没有办法在 Flutter 中获得类似的输出?

编辑:我的源代码:

import 'package:flutter/material.dart';

void main() => runApp(
  new MaterialApp(
    home: new MyApp(),
  )
);

var data = {
  "01/01/2018": [
    ["CocaCola", "\$ 5"],
    ["Dominos Pizza", "\$ 50"],
  ],

  "04/01/2018": [
    ["Appy Fizz", "\$ 10"],
    ["Galaxy S9+", "\$ 700"],
    ["Apple iPhone X", "\$ 999"],
  ],
};

List<String> dataKeys = data.keys.toList();

String getFullDate(String date) {
  List<String> dateSplit = date.split('/');
  List<String> months = ["Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sep", "Oct", "Nov", "Dec"];

  return "${dateSplit[0]} ${months[int.parse(dateSplit[1]) - 1]} ${dateSplit[2]}";
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<Widget> _buildList(int keyIndex) {
    List<Widget> list = [];

    for (int i = 0; i < data[dataKeys[keyIndex]].length; i++) {
      list.add(
        new Row(
          children: <Widget>[
            new CircleAvatar(
              child: new Icon(Icons.verified_user),
              radius: 20.0,
            ),
            new Text(data[dataKeys[keyIndex]][i][0])
          ],
        )
      );
    }

    return list;
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Expense Monitor"),
      ),
      body: new Container (
        child: new ListView.builder(
          itemCount: dataKeys.length,
          itemBuilder: (BuildContext context, int keyIndex) {
            return new Card(
              child: new ExpansionTile(
                title: new Text(getFullDate(dataKeys[keyIndex])),
                children: <Widget>[
                  new Column(
                    children: _buildList(keyIndex)
                  )
                ]
              ),
            );
          }
        )
      )
    );
  }
}

控制台显示的错误:

I/flutter (12945): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter (12945): The following assertion was thrown during performResize():
I/flutter (12945): Vertical viewport was given unbounded height.
I/flutter (12945): Viewports expand in the scrolling direction to fill their container.In this case, a vertical
I/flutter (12945): viewport was given an unlimited amount of vertical space in which to expand. This situation
I/flutter (12945): typically happens when a scrollable widget is nested inside another scrollable widget.
I/flutter (12945): If this widget is always nested in a scrollable widget there is no need to use a viewport because
I/flutter (12945): there will always be enough vertical space for the children. In this case, consider using a Column
I/flutter (12945): instead. Otherwise, consider using the "shrinkWrap" property (or a ShrinkWrappingViewport) to size
I/flutter (12945): the height of the viewport to the sum of the heights of its children.
I/flutter (12945): When the exception was thrown, this was the stack:
I/flutter (12945): #0      RenderViewport.performResize.<anonymous closure> (package:flutter/src/rendering/viewport.dart:944:15)
I/flutter (12945): #1      RenderViewport.performResize (package:flutter/src/rendering/viewport.dart:997:6)
I/flutter (12945): #2      RenderObject.layout (package:flutter/src/rendering/object.dart:1555:9)
I/flutter (12945): #3      _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:109:13)
......
I/flutter (12945): ════════════════════════════════════════════════════════════════════════════════════════════════════
I/flutter (12945): Another exception was thrown: RenderBox was not laid out: RenderViewport#df29c NEEDS-LAYOUT NEEDS-PAINT

【问题讨论】:

  • 请添加您的代码。您是否在控制台中遇到错误?
  • @GünterZöchbauer 我已经包含了代码和堆栈跟踪。
  • 我认为这是Flutter 中的一个错误。在我删除build 文件夹并重建项目后问题得到解决。
  • Ir 是一个已知问题,因为一段时间以来,如果没有 flutter clean,某些更改不会生效,它会删除 build folder,但至少应该在 master 中修复。

标签: android listview dart expandablelistview flutter


【解决方案1】:

试试这个:

import 'package:flutter/material.dart';

void main() => runApp(new MaterialApp(home: new MyApp(), debugShowCheckedModeBanner: false,),);

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new ListView.builder(
        itemCount: vehicles.length,
        itemBuilder: (context, i) {
          return new ExpansionTile(
            title: new Text(vehicles[i].title, style: new TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold, fontStyle: FontStyle.italic),),
            children: <Widget>[
              new Column(
                children: _buildExpandableContent(vehicles[i]),
              ),
            ],
          );
        },
      ),
    );
  }

  _buildExpandableContent(Vehicle vehicle) {
    List<Widget> columnContent = [];

    for (String content in vehicle.contents)
      columnContent.add(
        new ListTile(
          title: new Text(content, style: new TextStyle(fontSize: 18.0),),
          leading: new Icon(vehicle.icon),
        ),
      );

    return columnContent;
  }
}

class Vehicle {
  final String title;
  List<String> contents = [];
  final IconData icon;

  Vehicle(this.title, this.contents, this.icon);
}

List<Vehicle> vehicles = [
  new Vehicle(
    'Bike',
    ['Vehicle no. 1', 'Vehicle no. 2', 'Vehicle no. 7', 'Vehicle no. 10'],
    Icons.motorcycle,
  ),
  new Vehicle(
    'Cars',
    ['Vehicle no. 3', 'Vehicle no. 4', 'Vehicle no. 6'],
    Icons.directions_car,
  ),
];

【讨论】:

  • 我接受了这个答案,因为我的方法足够接近并且很容易实现。
  • 如果您想要左侧的箭头,只需打开 ExpansionTile 类并复制其中的所有内容并将类和状态重命名为其他名称。然后用前导替换尾随属性。删除所有导入并按 Alt + Space 进行自动建议并导入建议的导入。最后用这个 CustomExpansionTile 代替原来的来得到左边的箭头。
  • 如何让这个列表一次只能展开?
  • 谢谢@VinothKumar。你的代码对我帮助很大
【解决方案2】:

试试这个:

首先使用 AnimatedContainer 制作一个 ExpandableContainer

然后创建一个 ExpandableListView ,它将创建一个 Column 。 Column 的第一个子元素是展开按钮,第二个子元素是 ExpandableContainerExpandableContainer 将有一个 ListView 作为其子级。

最后一步是制作 ExpandableListViewListView

结果:

代码:

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

【讨论】:

  • 能否将嵌套列表视图设为不可滚动,因为我只想在每个展开面板中呈现静态列表
  • 是的,您可以使用 ExpandableContainer 中的 Column 并调整 expandHeight 。但正如我认为你需要在嵌套列表中使用 ListView 我建议上面的代码,对于你的情况,你可以使用 ExpansionTile。
  • @AjayKumar 我不想固定expandedHeight,我希望它应该根据嵌套的ListView大小扩展并显示嵌套列表的所有项目,这段代码可以吗?
【解决方案3】:

截图:


代码:

class MyPage extends StatelessWidget {
  List<Widget> _getChildren(int count, String name) => List<Widget>.generate(
        count,
        (i) => ListTile(title: Text('$name$i')),
      );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(
        children: [
          ExpansionTile(
            title: Text('List-A'),
            children: _getChildren(4, 'A-'),
          ),
          ExpansionTile(
            title: Text('List-B'),
            children: _getChildren(3, 'B-'),
          ),
        ],
      ),
    );
  }
}

【讨论】:

猜你喜欢
  • 2020-07-16
  • 1970-01-01
  • 2011-08-06
  • 2021-02-16
  • 2016-02-07
  • 1970-01-01
  • 1970-01-01
  • 2019-11-17
  • 1970-01-01
相关资源
最近更新 更多