How to create Expandable ListView in Flutter
Solution 1
Try this:
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,
),
];
Solution 2
Try this:
First Make an ExpandableContainer using AnimatedContainer.
Then Make an ExpandableListView which will create a Column . The first child of Column will be a button to expand and Second will be ExpandableContainer . ExpandableContainer will have a ListView as its child.
The last step will be to make a ListView of ExpandableListView.
The Result :
The Code :
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)),
),
);
}
}
Solution 3
Screenshot:
Code:
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-'),
),
],
),
);
}
}
Melvin Abraham
Updated on May 18, 2021Comments
-
Melvin Abraham almost 3 years
How to make an Expandable ListView using Flutter like the screenshot below?
I want to make a scrollable list view of
ExpansionTiles
which when expanded shows a non-scrollable list view.I tried to implement list view of
ExpansionTiles
inside which I nested another list view usinglistView.builder(...)
. But when I expanded theExpansionTile
the list view didn't show up...(The screenshot is for illustrative purpose)
Is there a way to get similar output in Flutter?
EDIT: My Source Code:
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) ) ] ), ); } ) ) ); } }
Error as shown in Console:
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