Flutter滚动控制器连接到多个滚动视图。

14
当渲染多个 widget 实例并调用 getValue 方法时,Flutter 会抛出错误“ScrollController attached to multiple scroll views”。我猜这是因为它们都在使用同一个控制器,但我不知道有没有更好的方法来修复它,而不必为每次使用它创建一个单独的widget。是否有更好的解决方法?
class NumScroller extends StatelessWidget{
  final int max,min;
  final double height,width;
  final TextAlign alignment;

  static ScrollController controller;

  NumScroller({this.height,this.width,this.alignment,this.min,this.max, initialOffset}){
    controller = new ScrollController(initialScrollOffset: initialOffset);
  }

  getValue() => (controller.offset~/height) + min;

  @override
  Widget build(BuildContext context) {
    return new Container(
        width: width,
        height: height,
        child: ListView.builder(itemBuilder: (context, index) {
          return new Container(height: height, child:Text((max - index).toString(),textAlign: alignment,));
          },
          itemCount: max - min+1,
          controller: controller,
          physics: PageScrollPhysics(),
          itemExtent: height,
        )
    );
  }

}

1
你应该使用有状态的小部件而不是无状态的。 - Rémi Rousselet
嗨@Remi,为什么这应该是“有状态的”? - Dinesh Balasubramanian
1
因为这样你可以为每个小部件实例拥有一个控制器,而不是一个静态控制器被到处使用。 - Rémi Rousselet
抱歉,我不理解。即使使用Stateless,我们也可以为每个小部件实例拥有一个控制器。 - Dinesh Balasubramanian
1
@DineshBalasubramanian 控制器应该保留。每次道具更改时(这是使用StatelessWidget时发生的情况)重新创建一个控制器会产生不必要的效果。 - Rémi Rousselet
我已经在这里添加了一个类似的答案,请参考附加到多个滚动视图的ScrollController - 答案 - Shoaib Khan
3个回答

10

当渲染多个实例时,您会遇到此错误。如果您的ScrollController是多个(每个视图一个),则不会出现任何问题。但是,由于您使用了静态,因此只有一个ScrollController。

移除静态即可解决问题。

如果还未解决,请告诉我。


没错,它起作用了。我不知道解决方案会这么简单。 - And Grow
不要紧,那个错误没有出现只是因为另一个错误。 - And Grow
我不知道发生了什么事情,现在这个可以工作了。 - And Grow
我为我的3个ListView.builder创建了这样的方式,但它们都显示相同的错误。因此,我无法跟踪/监听每个列表的滚动。_controller = ScrollController(); _controller.addListener(_scrollListener); _controller2 = ScrollController(); _controller2.addListener(_scrollListener2); _controller3 = ScrollController(); _controller3.addListener(_scrollListener3); @Dinesh - 并且 - Jamshed Alam
1
即使我为每个页面单独设置了ScrollController,我仍然遇到了同样的问题。当我导航到下一个页面并返回到之前的页面但没有使用 Navigator.pop() 时会出现错误。当通过 Navigator.pop() 返回时,一切正常。 - Shoaib Khan

8

不要使用静态控制器,每个小部件实例都应该有一个控制器。

但是您不能将控制器存储在StatelessWidget中(即使编译器允许这样做)。

您需要一个StatefulWidget来实现这一点,否则当您的小部件更新时,您将再次创建一个新的控制器,从而导致奇怪的行为。

以下是最终代码:

class NumScroller extends StatefulWidget{
  final int max,min;
  final double height,width;
  final TextAlign alignment;

  NumScroller({this.height,this.width,this.alignment,this.min,this.max, initialOffset});

  @override
  NumScrollerState createState() {
    return new NumScrollerState();
  }
}

class NumScrollerState extends State<NumScroller> {
  final ScrollController controller = ScrollController();

  getValue() => (controller.offset~/widget.height) + widget.min;

  @override
  Widget build(BuildContext context) {
    return new Container(
        width: widget.width,
        height: widget.height,
        child: ListView.builder(itemBuilder: (context, index) {
          return new Container(height: widget.height, child:Text((widget.max - index).toString(),textAlign: widget.alignment,));
          },
          itemCount: widget.max - widget.min+1,
          controller: controller,
          physics: PageScrollPhysics(),
          itemExtent: widget.height,
        )
    );
  }
}

也许这样可以工作,但是你就无法访问getValue函数了。 - And Grow
你是什么意思?如果你想的话,你可以从其他地方访问小部件的状态。 - Rémi Rousselet
怎么做?据我所知,您只能访问有状态小部件中的属性和方法,而不能访问其状态。 - And Grow
请参阅 https://docs.flutter.io/flutter/widgets/GlobalKey-class.html 或 https://docs.flutter.io/flutter/widgets/BuildContext/ancestorStateOfType.html。 - Rémi Rousselet

0

我在一个 bottomNavigationBar 中有一些屏幕,我通过为具有问题的小部件添加一个 key 来解决了这个问题。

key: GlobalKey(debugLabel: 'unique_widget_key'),

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接