Flutter : How to add a Header Row to a ListView
Solution 1
Here's how I solved this. Thanks @najeira for getting me thinking about other solutions.
In the first body Column I used the same layout for my Header that I used for the ListTile
.
Because my data ListTil
e, in this case, includes a CircleAvatar
, all the horizontal spacing is off a bit... 5 columns where the CircleAvatar
is rendered... then 4 evenly spaced columns.
So... I added a ListTile
to the first body Column
, a CircleAvatar
with a backgroundColor
of transparent, and then a Row
of my 4 Headings.
ListTile(
onTap: null,
leading: CircleAvatar(
backgroundColor: Colors.transparent,
),
title: Row(
children: <Widget>[
Expanded(child: Text("First Name")),
Expanded(child: Text("Last Name")),
Expanded(child: Text("City")),
Expanded(child: Text("Id")),
]
),
),
Solution 2
Return the header as first row by itemBuilder:
ListView.builder(
itemCount: data == null ? 1 : data.length + 1,
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
// return the header
return new Column(...);
}
index -= 1;
// return row
var row = data[index];
return new InkWell(... with row ...);
},
);
Solution 3
You can add Container
and ListView
in Column
.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text("Demo App1"),
),
body: Column(
children: <Widget>[
Container(
height: 40.0,
child: Row(
children: <Widget>[
Container(
padding: EdgeInsets.all(4.0),
width: 100.0,
child: Text(
"Name",
style: TextStyle(fontSize: 18),
)),
Container(
padding: EdgeInsets.all(4.0),
width: 100.0,
child: Text(
"Age",
style: TextStyle(fontSize: 18),
)),
],
),
),
Expanded(
child: ListView.builder(
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return Row(
children: <Widget>[
Container(
padding: EdgeInsets.all(4.0),
width: 100.0,
child: Text(
"Name $index",
style: TextStyle(fontSize: 18),
)),
Container(
padding: EdgeInsets.all(4.0),
width: 100.0,
child: Text(
"Age $index",
style: TextStyle(fontSize: 18),
),
)
],
);
},
),
),
],
),
),
);
}
}
Solution 4
You can add a column to the first item in the item list like this
new ListView.builder(
itemCount: litems.length,
itemBuilder: (BuildContext ctxt, int index) {
if (index == 0) {
return Column(
children: <Widget>[
Header(),
rowContent(index),
],
);
} else {
return rowContent(index);
}
},
)
Solution 5
najeira's solution is easy and simple, but you can get the same and more flexible result without touching index.
Instead of using listView, you could use CustomScrollView & SliverList which is functionally the same as listView.
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
// you could add any widget
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.transparent,
),
title: Row(
children: <Widget>[
Expanded(child: Text("First Name")),
Expanded(child: Text("Last Name")),
Expanded(child: Text("City")),
Expanded(child: Text("Id")),
],
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => APIDetailView(data[index])),
);
},
child: ListTile(
//return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.blue,
child: Text(data[index]["FirstName"][0]),
),
title: Row(
children: <Widget>[
Expanded(child: Text(data[index]["FirstName"])),
Expanded(child: Text(data[index]["LastName"])),
Expanded(child: Text(data[index]["Bill_City"])),
Expanded(child: Text(data[index]["Customer_Id"])),
],
),
),
);
},
childCount: data == null ? 0 : data.length,
),
),
],
),
);
Admin
Updated on October 04, 2021Comments
-
Admin over 2 years
Very new to Flutter. I've been able to utilize HTTP requests for data, build a ListView, edit a Row in that List and other basics. Excellent environment.
I've managed to cobble together a badly constructed Header for a ListView... but I know this isn't right. I can't get the Header text to line up properly.
I see that the Drawer Class has a DrawerHeader Class, but can't see that ListView has a ListViewHeader.
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Contacts'), actions: [ IconButton(icon: Icon(Icons.add_circle), onPressed: getCustData ), ], ), //body: body: Column( children: [ Row( children: [ Expanded(child: Text('', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))), Expanded(child: Text('First Name', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))), Expanded(child: Text('Last Name', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))), Expanded(child: Text('City', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))), Expanded(child: Text('Customer Id', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))), Expanded(child: Text('', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))), ] ),
Expanded(child:Container( child: ListView.builder( itemCount: data == null ? 0 : data.length, itemBuilder: (BuildContext context, int index) { return InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => APIDetailView(data[index])), ); }, child: ListTile( //return new ListTile( onTap: null, leading: CircleAvatar( backgroundColor: Colors.blue, child: Text(data[index]["FirstName"][0]), ), title: Row( children: <Widget>[ Expanded(child: Text(data[index]["FirstName"])), Expanded(child: Text(data[index]["LastName"])), Expanded(child: Text(data[index]["Bill_City"])), Expanded(child: Text(data[index]["Customer_Id"])), ] ) ), ); }, //itemBuilder ), ), ), ] ) );
} }
Thanks.
-
Admin about 6 yearsThanks... I feel this is much cleaner than what I had. I'm still struggling to get the Header titles to line up with the ListView columns.
-
najeira about 6 yearsCan you make each column a fixed width?
-
Admin about 6 yearsI wouldn't like to. It's a mobile app... never know what screen width I'm dealing with.
-
najeira about 6 yearsYou can get the width of the screen from MediaQuery. How about calculating the width with a percentage?
-
Admin about 6 yearsNice. I'll give that a try and post back.
-
Marco Altran about 5 yearsThis approach should be avoided as it completely ignores the first or the last element depending on the list size.
-
Abbas.M almost 5 yearsWould you by any chance know how to make the whole thing scrollable horrizontally? Was facing your same issue with header except that my list items are WAY bigger, i have like 10 columns in the table and want to make the whole thing scrollable sideways as well
-
F-1 over 4 yearsshows first and last elements because it uses
data.length + 1
andindex -= 1;
, if you miss that out it won't work -
Jonas over 4 yearsYou could wrap the whole thing in a ListView with the scroll direction set to horizontal.
-
dorsz over 4 yearsI got an issue with "A RenderFlex overflowed by 69 pixels on the bottom" when using ListView and this one helped me. The trick here is to wrap ListView in Expanded widget. Didn't work when I used SingleChildScrollView, because I wanted non-scrollable header and scrollable list.
-
M.M.Hasibuzzaman over 3 years@Abbas.M wrap the title: Row(...) with ```SingleChildScrollView```` , it will scroll
-
Eradicatore about 3 yearsYea, this is very simple and straight forward. I will say though, now this has me thinking of trying to make it a sticky header instead. :-)
-
无夜之星辰 almost 3 yearsGood idea. Simple and easy to use.
-
Maksym Letiushov over 2 yearsIf list items have a fixed height I would suggest using
SliverFixedExtentList
instead ofSliverList
. Just specifyitemExtent
and scrolling will be better.