我正在创建一个使用水平ListView实现的图像滚动条类。 ScrollBar应该显示一个左箭头或右箭头,具体取决于ListView是否在两个方向上都使屏幕溢出。当用户滚动时,可以使用NotificationListener更新箭头(请参见下面的代码)。但是,在用户触发通知之前,我不知道如何设置ScrollBar的初始状态。
我还尝试使用ScrollController访问ListView的初始位置,但似乎只有在build方法返回后,此信息才可用。 (错误消息显示为“ ScrollController未附加到任何滚动视图。”)
class ImageScrollbar extends StatefulWidget {
@override
_ImageScrollbarState createState() => _ImageScrollbarState();
}
class _ImageScrollbarState extends State<ImageScrollbar> {
bool _moreToLeft = false;
bool _moreToRight = false; // <-- how to properly initialize?
// how can we know within the build method whether the listview
// has gone off screen, in which case _moreToRight = true?
// update arrows in response to user action (scrolling)
_updateArrows(ScrollMetrics sm) {
setState(() {
_moreToLeft = sm.pixels != sm.minScrollExtent;
_moreToRight = sm.pixels != sm.maxScrollExtent;
});
}
@override
Widget build(BuildContext context) {
// create image widgets
var image_widgets = [Image.asset('example.png', fit: BoxFit.cover)];
// build the scroll bar
var _scrollController = new ScrollController();
var sa = ListView(
padding: const EdgeInsets.all(8.0),
scrollDirection: Axis.horizontal,
controller: _scrollController,
children: image_widgets);
// print(_scrollController.position); <-- causes error because _scrollController not yet attached
var sb = <Widget>[
NotificationListener<ScrollNotification>(
onNotification: (scrollNotification) {
if (scrollNotification is ScrollUpdateNotification) {
_updateArrows(scrollNotification.metrics);
}
},
child: sa,
)];
if (_moreToLeft) {
sb += Positioned(
left: -30,
child: Icon(Icons.arrow_left, size: 72),
);
}
if (_moreToRight) {
sb += Positioned(
right: -30,
child: Icon(Icons.arrow_right, size: 72),
);
}
return Stack(
alignment: Alignment(0.0, 0.0),
overflow: Overflow.visible,
children: sb,
);
}
}
一旦用户滚动,就会调用_updateArrows
方法,一切都很好。但是,当应用首次加载时,即使ListView实际上从右侧溢出屏幕,也没有向右箭头。这是因为我将_moreToRight
的默认值设置为false
,但是我需要根据实际布局动态确定它,而无需事先知道ScrollBar中将有多少个图像。 (我只是在示例代码中使用了一张图片,以使其变得简单。)
答案 0 :(得分:0)
我找到了可行的解决方案。
1)从NotificationListener更改为ScrollListener。确实不是必需的(我不认为),但是代码更简单。
2)在帧后回调中调用状态设置函数。
3)睡眠一小段时间,以确保ListView已完全呈现。
基本上:
bool _moreToLeft = false;
bool _moreToRight = false;
ScrollController _controller;
_scrollListener() {
setState(() {
_moreToLeft = _controller.position.pixels != _controller.position.minScrollExtent;
_moreToRight = _controller.position.pixels != _controller.position.maxScrollExtent;
});
}
@override
void initState() {
super.initState();
_controller = ScrollController();
_controller.addListener(_scrollListener);
WidgetsBinding.instance.addPostFrameCallback((_) async {
// must introduce this lag to make sure the scrollbar is completely rendered
await Future.delayed(const Duration(milliseconds : 1000));
_scrollListener();
});
}
@override
Widget build(BuildContext context) {
// ...
ListView(
padding: const EdgeInsets.all(8.0),
scrollDirection: Axis.horizontal,
controller: _controller,
children: kids);
// ...
}