Issue
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
I worked around this issue by:
- setting
itemCount
to 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.
Answered By - user2233706
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.