ScrollController 未附加到任何滚动视图

Posted

技术标签:

【中文标题】ScrollController 未附加到任何滚动视图【英文标题】:ScrollController not attached to any scroll views 【发布时间】:2019-02-06 10:22:16 【问题描述】:

我正在使用 CustomScrollView,并为它提供了一个控制器。 ScrollController 有效,我什至给它添加了一个监听器并打印出滚动视图的位置。

CustomScrollView(
    controller: _scrollController,

现在,我要做的就是跳转到 initState() 函数中的位置 50.0。

_scrollController.jumpTo(50.0);

但是,我得到了错误

scrollController 未附加到任何滚动视图

【问题讨论】:

请发布更多代码。你在哪里打电话jumpTo?不允许你用initState或类似的方式来做。 @boformer 是的,正如我在问题中提到的那样,我在 initState 中调用它,文档没有提到任何关于不在 initState 中调用它的内容。 我也试过了。它不起作用,因为控制器仅在运行 build 方法并且滚动视图附加到树后附加。 【参考方案1】:

推迟它不是正确的解决方案。最好等到树完成构建使用

WidgetsBinding.instance
        .addPostFrameCallback((_));

样本

WidgetsBinding.instance.addPostFrameCallback((_) 
      if(pageController.hasClients)
              pageController.animateToPage(page index, duration: Duration(milliseconds: 1), curve: Curves.easeInOut);
      
);

【讨论】:

添加回调函数后页面没有移动索引页面。我已经删除了回调函数并添加了 if(pageController.hasClients) pageController.animateToPage(page index, duration: Duration(milliseconds: 1), curve: Curves.easeInOut); 它现在可以工作了。【参考方案2】:

我用这段代码使appbar的颜色在0位置透明,黑色超过70

backgroundColor: !_scrollController.hasClients
            ? Colors.transparent
            : _scrollController.offset > 70
                ? Color.fromRGBO(7, 7, 7, 1)
                : Colors.transparent,

【讨论】:

【参考方案3】:

我已经尝试过上述解决方案,但设置了 ScrollController initialScrollOffset,检查 hasClients 和 jumpTo,WidgetsBinding 没有人为我工作。

最后通过检查位置'scrollcontroller.positions.length'解决了我的问题

if (_sc.positions.length == 0 || _sc.position.pixels == 0.0) 
    return Container();

您必须检查控制器位置以消除错误

参考:https://api.flutter.dev/flutter/widgets/ScrollController/position.html

【讨论】:

【参考方案4】:

初始化滚动控制器:

ScrollController _scrollController = ScrollController(); 

在你想滚动的地方使用下面的代码:

SchedulerBinding.instance.addPostFrameCallback((_) 
  _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
);

【讨论】:

【参考方案5】:

我对两个控制器使用相同的 ScrollController 解决了我的问题。 首先我创建 ScrollController():

ScrollController scollBarController = ScrollController();

在我的代码中:

Container(
    width: MediaQuery.of(context).size.width,
    height: MediaQuery.of(context).size.height,
    child: Scrollbar(
      controller: scollBarController,
      isAlwaysShown: true,
      child: StaggeredGridView.count(
        controller: scollBarController,
        crossAxisCount: 16,
        staggeredTiles: _staggeredTamanho,
        shrinkWrap: true,
        children: [
          ...
        ], //listStaggered,
        mainAxisSpacing: 7.0,
      ),
    ),
  ),

【讨论】:

不适合我。滚动条(isAlwaysShown:true,控制器:控制器,子:ListView(控制器:控制器,【参考方案6】:

@carlosx2 的答案是正确的,但如果有人想知道将 WigetsBinding 放在哪里。就是这样。

@override
void initState()
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_)
    //write or call your logic
    //code will run when widget rendering complete
  );

【讨论】:

在 Dart 2.12 中实例可以为空。 "方法'addPostFrameCallback'不能被无条件调用,因为接收者可以是'null'。"【参考方案7】:

使用 setState() 方法绑定到与滚动相关的小部件(您的列表视图等)

setState(() 
    topicsScrollController.animateTo(....);
);

【讨论】:

【参考方案8】:

当您尝试将 ScrollController 绑定到实际上不存在(尚)的小部件时,有时会发生这种情况。所以它尝试绑定,但没有要绑定的 ListView/ScrollView。 假设你有这个代码:

Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: <Widget>[
    Expanded(
      flex: 8,
      child: Container(
        child: ListView.builder(
          controller: scrollController,
          itemCount: messages.length,
          itemBuilder: (context, index) 
            return MessageTile(message: messages[index]);
          ),
        ),
     ),
 ]),

这里我们动态构建一个 ListView。由于我们之前从未声明过这个 ListView,scrollController 没有任何要绑定的东西。但这很容易解决:

//首先声明ListView和ScrollController

final scrollController = ScrollController(initialScrollOffset: 0);
ListView list = ListView.builder(
  controller: scrollController,
  itemCount: messages.length,
  itemBuilder: (context, index) 
    return MessageTile(message: messages[index]);
  
);

// 然后在你的构建函数中只引用已经构建的列表

Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: <Widget>[
    Expanded(
      flex: 8,
      child: Container(
         child: list,
      ),
    ),
  ]),

【讨论】:

【参考方案9】:

我找到了可行的解决方案,但坦率地说,我想该方法不是最佳实践。 在我的情况下, controller.jumpTo() 在附加到任何滚动视图之前被调用。为了解决问题,我延迟了几毫秒,然后调用 .jumpTo(),因为 build 将被调用并且控制器将附加到任何滚动视图。

Future.delayed(Duration(milliseconds: <some time, ex: 100>), () 
         _scrollController.jumpTo(50.0);
        );

我完全同意这是不好的解决方案,但它可以解决问题。

【讨论】:

【参考方案10】:

首先使用其 hasClients 属性检查 scrollController 是否附加到滚动视图。

if (_scrollController.hasClients) 
    _scrollController.jumpTo(50.0);

【讨论】:

我的错误是我将控制器连接到 Scrollbar 而不是 SingleChildScrollView child: Scrollbar( child: SingleChildScrollView( controller: _scrollController, 。我想我应该分享一下,以防你们中的任何人有类似的情况。 如何让 listview.builder 始终显示滚动条【参考方案11】:

要设置ScrollController 的初始位置,请使用initialScrollOffset 属性:

_scrollController = ScrollController(initialScrollOffset: 50.0);

【讨论】:

在新问题中发布您正在使用的确切代码并标记我 下面建议了更好的解决方案。首先检查它是否通过if(_scrollController.hasClients) 附加,然后您应该调用jumpToanimateTo。如果您跳过客户端检查,在 android 上它仍然可以工作.. 但在 ios 上它会导致问题。 我认为它不会在 IOS(至少在 14 中)和 Flutter 2.5 中引起问题。除非您添加 Scrollbar() 作为父级 - 对我来说,这会导致 IOS 中的异常。

以上是关于ScrollController 未附加到任何滚动视图的主要内容,如果未能解决你的问题,请参考以下文章

附加到多个滚动视图的 ScrollController

如何获得 ScrollController 的全尺寸

25.Flutter的ListView监听滚动事件之ScrollController

滚动带有ScrollController的CustomScrollView时,SliverAppbar仍然可见

Flutter 可滚动组件:ScrollController

Flutter - ScrollController 滚动监听及控制