我如何知道ListView最初呈现时是否在屏幕上溢出?

时间:2019-08-24 03:45:30

标签: flutter

我正在创建一个使用水平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中将有多少个图像。 (我只是在示例代码中使用了一张图片,以使其变得简单。)

1 个答案:

答案 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);

// ...

}