Recently I wanted to horizontally scrolling ListView to display a number of panels. This is relatively easy to achieve, but there were two things that I did not like about it.
- Firstly the fixed width of the items means the list displays shows parts of an panel
- Secondly when the list scrolls it doesn’t snap to the edge of an panel
class MyListState extends State<MyList> {
List<String> elements = ["One","Two","Three","Four","Five","Six","Seven", "Eight"];
@override
Widget build(BuildContext context) {
double itemMargin = 5; // Margin between list elements
double pageMargin = 15;
double itemWidth = 120;
return Padding(padding: EdgeInsets.all(pageMargin),
child: Container(
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: elements.length,
itemBuilder: (BuildContext context, int index) {
return Padding(padding: EdgeInsets.only(left: itemMargin, right: itemMargin),
child:Container(
width : itemWidth,
color: Colors.blueGrey,
child: Center(child: Text(' ${elements[index]}')),
));
}
)));
}
}
DartAs it turns out both of these issues are also very easy to fix. First up, calculating the panel dimensions can be done by using a media query to get the screen width. Dividing the screen width by the minimum width of a panel and its margins gives us how many panels to display for a screen width. We can then calculate the individual panel width from the screen width, margins and number of panels.
double minWidth = 120;
Size sz = MediaQuery.of(context).size;
// How many panels to show
int numItems = (sz.width/(minWidth+itemMargin*2)).floor();
// Width of each panel
double itemWidth = ((sz.width-pageMargin*2)/numItems) - itemMargin*2;
DartNext we need to modify the ListView Builder to use a PageController with PageScrollPhysics. Normally PageController scrolls whole pages – the key to making it scroll on a per item basis is to use viewportFraction to set the fraction of width that each panel occupies. With these changes my ListView.builder changes to
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: elements.length,
physics: const PageScrollPhysics(),
controller: PageController(
viewportFraction: 1/numItems,
),
DartRunning this in Chrome confirms the ListView now behaves exactly as I had hoped – showing only whole panels and bouncing nicely to the edge of a panel as the user scrolls.
Leave a Reply