Flutter : How can I add divider between each List Item in my code?
AndroidIosFlutterAndroid Problem Overview
How could I add divider to list? I use Flutter for Android. I want to add a divider between each List item and I want to colorize the divider and add styles.
I tried to add new divider();
but I got errors. I also tried return new divider();
.
Here is the screen shot of my app:
https://i.stack.imgur.com/P7h8a.png" width="300" >
And here is my code:
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp();
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.purple,
buttonTheme: const ButtonThemeData(
textTheme: ButtonTextTheme.primary,
)
),
home: const MyHomePage(),
);
}
}
class Kitten {
const Kitten({this.name, this.description, this.age, this.imageurl});
final String name;
final String description;
final int age;
final String imageurl;
}
final List<Kitten> _kittens = <Kitten>[
Kitten(
name: "kitchen",
description: "mehraboon",
age: 2,
imageurl:
"https://images.pexels.com/photos/104827/cat-pet-animal-domestic-
104827.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=350",
),
Kitten(
name: "garage",
description: "khashen",
age: 1,
imageurl:
"https://images.pexels.com/photos/4602/jumping-cute-playing-animals.jpg?
auto=compress&cs=tinysrgb&dpr=2&h=350",
),
Kitten(
name: "bedroom",
description: "khar zoor",
age: 5,
imageurl:
"https://images.pexels.com/photos/978555/pexels-photo-978555.jpeg?
auto=compress&cs=tinysrgb&dpr=2&h=350",
),
Kitten(
name: "living room",
description: "chorto",
age: 3,
imageurl:
"https://images.pexels.com/photos/209037/pexels-photo-209037.jpeg?
auto=compress&cs=tinysrgb&dpr=2&h=350",
),
];
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key}) : super(key: key);
Widget _dialogBuilder(BuildContext context, Kitten kitten) {
return SimpleDialog(contentPadding: EdgeInsets.zero, children: [
Image.network(kitten.imageurl, fit: BoxFit.fill),
Padding(
padding: const EdgeInsets.all(16.0),
child:
Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Text(kitten.name),
Text('${kitten.age}'),
SizedBox(
height: 16.0,
),
Text(kitten.description),
Align(
alignment: Alignment.centerRight,
child: Wrap(
children: [
FlatButton(onPressed: () {}, child: const
Text("noooo!"),color: Colors.red,),
Padding(padding: const EdgeInsets.all(2.0),),
RaisedButton(onPressed: () {}, child: const
Text("yesss!"),color: Colors.green)
],
),
)
]))
]);
}
Widget _listItemBuilder(BuildContext context, int index) {
return new GestureDetector(
onTap: () => showDialog(
context: context,
builder: (context) => _dialogBuilder(context, _kittens[index])),
child:
Container(
padding: EdgeInsets.all( 16.0),
alignment: Alignment.centerLeft,
child: Text(_kittens[index].name,
style: Theme.of(context).textTheme.headline),
),
) ;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Keys"),
centerTitle: true,
),
body: ListView.builder(
itemCount: _kittens.length,
itemExtent: 60.0,
itemBuilder: _listItemBuilder,
),
);
}
}
Android Solutions
Solution 1 - Android
There are a number of ways to do the same thing. Let me compare them here.
For a short static list
Use ListTile.divideTiles
https://i.stack.imgur.com/C5Pl2.png" width="400">
ListView(
children: ListTile.divideTiles( // <-- ListTile.divideTiles
context: context,
tiles: [
ListTile(
title: Text('Horse'),
),
ListTile(
title: Text('Cow'),
),
ListTile(
title: Text('Camel'),
),
ListTile(
title: Text('Sheep'),
),
ListTile(
title: Text('Goat'),
),
]
).toList(),
)
For a long dynamic list
Use ListView.separated
.
https://i.stack.imgur.com/eBPDP.png" width="400">
ListView.separated(
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(
title: Text('$index sheep'),
);
},
separatorBuilder: (context, index) {
return Divider();
},
)
This returns two widgets for every item, except for the last item. The separatorBuilder
is used to add the divider.
For adding a divider after the last item
Create a custom item widget that uses a Divider or BoxDecoration.
Using Divider
https://i.stack.imgur.com/1HAGA.png" width="400">
final items = ['Horse', 'Cow', 'Camel', 'Sheep', 'Goat'];
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return Column(
children: <Widget>[
ListTile(
title: Text(items[index]),
),
Divider(), // <-- Divider
],
);
},
);
}
Using BoxDecoration
https://i.stack.imgur.com/ob62u.png" width="400">
final items = ['Horse', 'Cow', 'Camel', 'Sheep', 'Goat'];
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration( // <-- BoxDecoration
border: Border(bottom: BorderSide()),
),
child: ListTile(
title: Text(items[index]),
),
);
},
);
}
Both Divider and BoxDecoration are customizable as far as the line height and color go. Divider also has an indent option, but you could get a BoxDecoration to do the same thing with some padding.
For more style
Use a Card
https://i.stack.imgur.com/p8ABt.png" width="400">
final items = ['Horse', 'Cow', 'Camel', 'Sheep', 'Goat'];
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return Card( // <-- Card
child: ListTile(
title: Text(items[index]),
),
);
},
);
}
Solution 2 - Android
The most correct way is to use ListView.separated
ListView.separated(
itemCount: 25,
separatorBuilder: (BuildContext context, int index) => Divider(height: 1),
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text('item $index'),
);
},
);
Solution 3 - Android
Put your widget inside container with BoxDecoration as
Container(
child: YourWidgetHere(),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: Colors.black26))),
);
Solution 4 - Android
On the flutter getting started tutorial it is covered, the solution they provide is something like this:
body: ListView.builder(
itemCount: _kittens.length,
itemExtent: 60.0,
itemBuilder: (context, i) {
// Add a one-pixel-high divider widget before each row in theListView.
if (i.isOdd) return new Divider(color: Colors.purple); // notice color is added to style divider
return _listItemBuilder();
},
),
...
That should add the dividers taking into account the odd and even rows to do so.
Also to color the divider pas "color" to the Divider Class:
new Divider(color: Colors.purple);
Solution 5 - Android
recently I use this code to set divider:
ListView.separated(
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text("Hello", style: TextStyle(
color: Theme
.of(context)
.primaryColor
),),
);
},
separatorBuilder: (context, index) =>Divider(height: 1, color: Colors.green),
itemCount: 30),
and it works.
For example, I have added my app's screenshot
Solution 6 - Android
Check out this issue: ListView.builder should let clients specify a divider It makes clear that:
-
if you need to build your list with dynamic elements, for now you'll have to deal with this issue on your own. I'd recommend in the row widget building, you include a List Divider at the bottom with a column or something, except for the last one (you can test for
index == listData.length - 1
). -
But if, like in the example you show, you already know all the lists data before hand, or you build it without a
ListView.builder
, then you can and should use the named constructorListView.divideTiles
Solution 7 - Android
following this Just add Divider() :
Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
Image.network(video["imageUrl"]),
Container(
height: 6.0,
),
Text(
video["name"],
textScaleFactor: 1.05,
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
Divider(
color: Theme.of(context).primaryColor,
)
],
);
Solution 8 - Android
Dart 2.3
Another way, especially for generic non-list-view: Using for
in a Collection (link) with the ...
spread operator
Column(
children: [
for(var i=0; i<4; i+=1)
...[Container(height: 100, width: 100), Divider() ]])
Solution 9 - Android
Thats another way usig Container.
ListTile(
leading: Icon(Icons.home),
title: Text('Home'),
),
Container(height: 1, color: Colors.grey), //divider
ListTile(
leading: Icon(Icons.logout),
title: Text('Logout'),
),
Solution 10 - Android
The question assumes that we have access to material.dart
for Android styling (ListTile
and Divider
). If you want Cupertino styles, we can:
-
Use a
Column
view wrapper for the row, and add aContainer
with height 1.Column( children: <Widget>[ row, // A custom Row Padding( padding: const EdgeInsets.only( left: 16, // Adjust separator left padding. right: 16, // Adjust separator right padding. ), child: Container( height: 1, color: Styles.productRowDivider, // Custom style ), ), ], );
-
The
Divider
is not available incupertino.dart
. We can use the sameContainer
technique withListView.separated
:ListView.separated( itemCount: 100, itemBuilder: (context, index) { return row; }, separatorBuilder: (context, index) { return Container( height: 1, color: Styles.productRowDivider, // Custom style ); }, );
Solution 11 - Android
Create a Container like this
Container(height: 1, color: Colors.grey),
And add with your ListTile like this
ListView.builder(
itemCount: _feed.items.length,
itemBuilder: (BuildContext context, int index) {
final item = _feed.items[index];
return Column(
children: <Widget>[
Container(height: 1, color: Colors.grey), //Container for devider
ListTile( //Your tile item
title: title(item.title),
subtitle: subtitle(item.pubDate),
leading: thumbnail(item.enclosure.url),
trailing: rightIcon(),
contentPadding: EdgeInsets.all(5.0),
onTap: () => openFeed(item.link),
)]);
},
);
Now you can see final output in my screenshot