简单来说:
有没有办法让多个可滚动小部件(例如,
SingleSchildScrollView
)同步在一起?
我只想要 2 个可滚动项,当我滚动一个时,可以滚动另一个。
这样我就可以使用
Stack
将它们放在一起,后面的可以跟随前面的滚动。
或者可以将它们放在另一组
Column
或 Row
中,以便它们是分开的,但仍然只需滚动其中一个即可滚动。
我尝试使用
controller
,但它似乎没有按照我的想法进行。
例如尝试下面的代码, “右”将位于“左”的前面,如果我尝试滚动它们,只有右会移动。那么我该如何同时将它们移动到一起呢?
请不要告诉我将堆栈放入
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'),
],)
),
])
)
}}
我相信这个问题之前已经在多个论坛上被问过,但根本没有人对此做出结论或解决方案。 (参见此处)
我刚刚面临同样的问题,现在有一个官方包:linked_scroll_controller。
LinkedScrollControllerGroup
来跟踪滚动偏移,然后它将通过ScrollController
为您提供单独的
LinkedScrollControllerGroup.addAndGet()
(保持同步)。
我成功地通过使用他们的
offset
来同步多个可滚动项,利用他们的 ScrollNotification
。
这是一个粗略的代码示例:
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
等)
感谢您的回答@Chris,我遇到了同样的问题,并在您的基础上构建了我的解决方案。它适用于多个小部件,并允许从“组”中的任何小部件同步滚动。
PSA:这似乎工作正常,但我才刚刚开始,这可能会以你能想象到的最美妙的方式打破
它使用
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
s 构造函数来完成此操作,或者稍后调用 registerScrollController
并将 ScrollController
作为参数传递给函数来完成此操作。processNotification
方法绑定到 NotificationListener
的事件处理程序。这可能全部在小部件本身中实现,但我还没有足够的经验。
使用该类看起来像这样:
为 scolControllers 创建字段
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 适用:)
如果您的 UI 很简单,请包装两个滚动小部件 - 比方说两个 Column - 和一个相反的小部件 - 例如 Row - 然后使用 SingleChildScrollView 像这样
SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Row(
children: [
//first column
Column(),
//second column
Column(),
],
),
)
如果比较复杂,你可以使用这样的滚动控制器
late final ScrollController scrollController1;
late final ScrollController scrollController2;
@override
void initState() {
scrollController1 = ScrollController();
scrollController2 = ScrollController();
scrollController1.addListener(() {
if (scrollController1.offset != scrollController2.offset) {
scrollController2.jumpTo(scrollController1.offset);
}
});
scrollController2.addListener(() {
if (scrollController1.offset != scrollController2.offset) {
scrollController1.jumpTo(scrollController2.offset);
}
});
super.initState();
}
然后将每个滚动控制器添加到您拥有的每个滚动小部件中
SingleChildScrollView(
scrollDirection: Axis.vertical,
controller: scrollController1,
child: Column(), // column 1
)
SingleChildScrollView(
scrollDirection: Axis.vertical,
controller: scrollController2,
child: Column(), // column 2
)
我知道这是一个古老的问题,但可能对某人有用.. 包 linked_scroll_controller 正是您正在寻找的,它的性能比
NotificationListener
或向您的scrollController 添加侦听器并使用 .jump
.. 好得多
late ScrollController controller1;
late ScrollController controller2;
late LinkedScrollControllerGroup parentController;
@override
void initState() {
super.initState();
parentController = LinkedScrollControllerGroup();
controller1 = parentController.addAndGet();
controller2 = parentController.addAndGet();
}
就是这样,将controller1连接到第一个可滚动小部件,将controller2连接到另一个,然后它们就同步了。
我找到了这个并为我工作
static final tracking = TrackingScrollController();
// implementation
child: ListView(
controller: tracking,
也可以处理多个综合浏览量。