'ListView widget not entirely visible when scaled
When I scale a horizontal ListView widget, I observe that only a portion of the list items are visible when the widget is scrolled all the way to the right:
import 'dart:ui';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final sideLength = 50.0;
final scale = 2.0;
return MaterialApp(
scrollBehavior: MyCustomScrollBehavior(),
home: Scaffold(
body: Transform.scale(
scale: scale,
alignment: Alignment(-1, -1),
child: Container(
height: sideLength * scale,
child: ListView.builder(
itemCount: 20,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) => Container(
width: sideLength,
height: sideLength,
child: Text(index.toString()),
decoration: BoxDecoration(
border: Border.all(width: 3, color: Colors.red))),
)),
)));
}
}
class MyCustomScrollBehavior extends MaterialScrollBehavior {
// Override behavior methods and getters like dragDevices
@override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
};
}
On the Pixel 2 emulator, only the first 16 items are visible when I scroll to extreme right. For example:
When the scale is 1 or if the Transform.scale widget is not there, all 20 items are visible.
I observe the following behavior:
| Total item count | Last item scrollable to |
|---|---|
| 8 | 4 |
| 10 | 6 |
| 20 | 16 |
| 30 | 26 |
| 50 | 46 |
So it seems like the last 4 items are always left out.
Ultimately my goal is to create a responsive widget that scales according to the dimensions of screen, so I'm looking for a generic solution.
The custom scroll behavior is only there so that horizontal scrolling works on dartpad.dev, as per this answer.
Solution 1:[1]
I worked around this issue by:
- setting
itemCountto a higher value than the desired item count. This allows you to scroll to the last desired item in theListView - having a scroll controller that checks whether you're past the last visible desired item within the viewport. If so, jump to the last possible scroll offset within the viewport
import 'dart:ui';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final ScrollController _scrollController = ScrollController();
static const actualTotalItems = 20;
static const sideLength = 50.0;
static const scale = 2.0;
MyApp() {
_scrollController.addListener(() {
if (_scrollController.offset > sideLength * actualTotalItems - _scrollController.position.viewportDimension / scale) {
_scrollController.jumpTo(sideLength * actualTotalItems - _scrollController.position.viewportDimension / scale);
}
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
scrollBehavior: MyCustomScrollBehavior(),
home: Scaffold(
body: Container(
transform: Matrix4.diagonal3Values(scale, scale, 1),
height: sideLength * scale,
child: ListView.builder(
itemCount: actualTotalItems * 2,
scrollDirection: Axis.horizontal,
controller: _scrollController,
itemBuilder: (context, index) => Container(
width: sideLength,
height: sideLength,
child: Text(index.toString()),
decoration: BoxDecoration(border: Border.all(width: 3, color: Colors.red)))))));
}
}
class MyCustomScrollBehavior extends MaterialScrollBehavior {
// Override behavior methods and getters like dragDevices
@override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
};
}
I scaled the widget using the transform property in the Container to have a smaller widget tree, but that has no impact on the solution. The Transform widget could have been used as in the OP.
Solution 2:[2]
Transform only affects how the child should be painted.
On the other hand, The scale argument multiplies the x and y axes.
For example: Imagine a photographer who is taking a landscape photo that contains a tree, now if the photographer gets closer to the tree, the upper part of the tree will slightly be out of the photo.
Try adding padding or margin to the Container and observe how the widget is affected by the scale. Then you will know how to manipulate it.
body: Transform.scale(
scale: scale,
alignment: Alignment(0, -5),
child: Container(
height: sideLength * scale,
margin: EdgeInsets.only(left: 100, right: 100),
child: ListView.builder(
Solution 3:[3]
Give here width and height by MediaQuery
Widget build(BuildContext context) {
return MaterialApp(
scrollBehavior: MyCustomScrollBehavior(),
home: Scaffold(
body: Transform.scale(
scale: scale,
alignment: Alignment(-1, -1),
child: Container(
height: sideLength * scale,
child: ListView.builder(
itemCount: 20,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) => Container(
width: MediaQuery.of(context).size.width*0.90 ,
height: MediaQuery.of(context).size.height*0.80 ,
child: Text(index.toString()),
decoration: BoxDecoration(
border: Border.all(width: 3, color: Colors.red))),
)),
)));
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Mhmd Zawi |
| Solution 3 | Dharman |

