FLUTTER同步滚动多个滚动

时间:2019-02-25 05:06:01

标签: scroll dart controller flutter synchronization

简单地说:

是否可以将多个可滚动的小部件(例如SingleSchildScrollView)同步在一起?


我只想要2个可滚动对象,它们可以在我滚动一个时滚动另一个。

这样,我可以使用Stack将它们放在彼此的顶部,而后面的一个可以跟随前面的一个滚动。

或者可以将它们放在另一组ColumnRow中,以便它们是分开的,但仍然可以通过滚动其中一个来滚动。

我尝试使用controller,但似乎并没有按照我的想法做。


例如,尝试以下代码, “ RIGHT”将在“ LEFT”的前面,如果我尝试滚动它们,则只有RIGHT会移动。那么如何同时将它们一起移动?

请不要告诉我将堆栈放在ListView中,那不是我所需要的。

class _MyHomePageState extends State<MyHomePage> {

  final ScrollController _mycontroller = new ScrollController();

  @override
  Widget build(BuildContext context) {
    body:
      Container(
        height: 100,
        child:
          Stack( children: <Widget>[
            SingleChildScrollView(
              controller: _mycontroller,
              child: Column( children: <Widget>[
                Text('LEFT            '),
                Text('LEFT            '),
                Text('LEFT            '),
                Text('LEFT            '),
                Text('LEFT            '),
                Text('LEFT            '),
              ],)
            ),
            SingleChildScrollView(
              controller: _mycontroller,
              child: Column(children: <Widget>[
                Text('          RIGHT'),
                Text('          RIGHT'),
                Text('          RIGHT'),
                Text('          RIGHT'),
                Text('          RIGHT'),
                Text('          RIGHT'),
              ],)
            ),
          ])
      )
}}

我相信之前曾在多个论坛中提出过这个问题,但没有人提出任何结论或解决方案。 (请参阅here

4 个答案:

答案 0 :(得分:6)

我正面临着同样的问题,现在有一个官方软件包:linked_scroll_controller

使用该软件包,您只需创建一个母版LinkedScrollControllerGroup来跟踪滚动偏移量,然后它将通过{{3}为您提供单独的div(保持同步) }。

答案 1 :(得分:1)

我找到了这个并为我工作

static final tracking = TrackingScrollController();

// implementation
child: ListView(
  controller: tracking,

还可以处理多个综合浏览量。

答案 2 :(得分:0)

我设法通过使用offsetScrollNotification来同步多个滚动条。

这是一个粗略的代码示例:

class _MyHomePageState extends State<MyHomePage> {

  ScrollController _mycontroller1 = new ScrollController(); // make seperate controllers
  ScrollController _mycontroller2 = new ScrollController(); // for each scrollables

  @override
  Widget build(BuildContext context) {
    body:
      Container(
        height: 100,
        child: NotificationListener<ScrollNotification>( // this part right here is the key
          Stack( children: <Widget>[

            SingleChildScrollView( // this one stays at the back
              controller: _mycontroller1,
              child: Column( children: <Widget>[
                Text('LEFT            '),
                Text('LEFT            '),
                Text('LEFT            '),
                Text('LEFT            '),
                Text('LEFT            '),
                Text('LEFT            '),
              ],)
            ),
            SingleChildScrollView( // this is the one you scroll
              controller: _mycontroller2,
              child: Column(children: <Widget>[
                Text('          RIGHT'),
                Text('          RIGHT'),
                Text('          RIGHT'),
                Text('          RIGHT'),
                Text('          RIGHT'),
                Text('          RIGHT'),
              ],)
            ),
          ]),

          onNotification: (ScrollNotification scrollInfo) {  // HEY!! LISTEN!!
            // this will set controller1's offset the same as controller2's
            _mycontroller1.jumpTo(_mycontroller2.offset); 

            // you can check both offsets in terminal
            print('check -- offset Left: '+_mycontroller1.offset.toInt().toString()+ ' -- offset Right: '+_mycontroller2.offset.toInt().toString()); 
          }
        )
      )
}}

基本上每个SingleChildScrollView都有自己的controller。 每个controller都有自己的offset值。 使用NotificationListener<ScrollNotification>可以在滚动时随时通知任何运动。

然后针对每个滚动手势(我相信这是一帧一帧的基础), 我们可以添加jumpTo()命令来以我们喜欢的任何方式设置offset

欢呼!

PS。如果列表的长度不同,则偏移量将不同,并且如果您尝试滚动超过其限制,则会收到堆栈溢出错误。确保添加一些异常或错误处理。 (即if else等)

答案 3 :(得分:0)

感谢您的回答@Chris,我遇到了同样的问题,并在您的问题上建立了解决方案。它可以在多个窗口小部件上工作,并可以从“组”中的任何窗口小部件进行同步滚动。


PSA:这似乎很好,但是我才刚刚开始,这可能会以您能想象的最美妙的方式打破


它为每个应同步的可滚动Widget使用NotificationListener<ScrollNotification>加上独立的ScrollController
该类如下所示:

class SyncScrollController {
  List<ScrollController> _registeredScrollControllers = new List<ScrollController>();

  ScrollController _scrollingController;
  bool _scrollingActive = false;

  SyncScrollController(List<ScrollController> controllers) {
    controllers.forEach((controller) => registerScrollController(controller));
  }

  void registerScrollController(ScrollController controller) {
    _registeredScrollControllers.add(controller);
  }

  void processNotification(ScrollNotification notification, ScrollController sender) {
    if (notification is ScrollStartNotification && !_scrollingActive) {
      _scrollingController = sender;
      _scrollingActive = true;
      return;
    }

    if (identical(sender, _scrollingController) && _scrollingActive) {
      if (notification is ScrollEndNotification) {
        _scrollingController = null;
        _scrollingActive = false;
        return;
      }

      if (notification is ScrollUpdateNotification) {
        _registeredScrollControllers.forEach((controller) => {if (!identical(_scrollingController, controller)) controller..jumpTo(_scrollingController.offset)});
        return;
      }
    }
  }
}

这个想法是,您向该帮助程序注册了每个小部件ScrollController,以便它具有对应该滚动的每个小部件的引用。您可以通过将ScrollController的数组传递给SyncScrollController的构造函数来实现,或者稍后通过调用registerScrollController并将ScrollController作为参数传递给函数来实现。
您需要将processNotification方法绑定到NotificationListener的事件处理程序。这可能全部在小部件本身中实现,但是我还没有足够的经验。

使用该类看起来像这样:

为scollControllers创建字段

  ScrollController _firstScroller = new ScrollController();
  ScrollController _secondScroller = new ScrollController();
  ScrollController _thirdScroller = new ScrollController();

  SyncScrollController _syncScroller;

初始化SyncScrollController

@override
void initState() {
  _syncScroller = new SyncScrollController([_firstScroller , _secondScroller, _thirdScroller]);
  super.initState();
}

完整的NotificationListener示例

NotificationListener<ScrollNotification>(
  child: SingleChildScrollView(
    controller: _firstScroller,
    child: Container(
    ),
  ),
  onNotification: (ScrollNotification scrollInfo) {
    _syncScroller.processNotification(scrollInfo, _firstScroller);
  }
),

可能为每个可滚动小部件实现上述示例,并编辑SyncController(参数 controller:)和processNotification scrollController参数(在 _firstScroller 上方) )。您可以实施更多的故障保险,例如检查_syncScroller != null等,以上适用PSA:)