'How to apply Scroll controller for multiple SilverList or ListView items?
I have found the internet a way to hide arrow when scrolling horizontally through FlutterLogo
with CustomSrollView
. The functionality works and i want to place it inside a ListView.builder
or SilverList
so i have multilple widgets with the scrollfunctionality but once i put CustomScrollView
inside a list weither its ListView.builder
or SilverList
i get error:
ScrollController attached to multiple scroll views.
'package:flutter/src/widgets/scroll_controller.dart':
Failed assertion: line 108 pos 12: '_positions.length == 1'
This is the full code:
class AppView2 extends StatefulWidget {
const AppView2({
Key? key,
}) : super(key: key);
@override
_AppViewState createState() => _AppViewState();
}
class _AppViewState extends State<AppView2> {
late ScrollController _hideButtonController;
var _isVisible;
@override
void initState() {
_isVisible = true;
_hideButtonController = ScrollController();
_hideButtonController.addListener(() {
if (_hideButtonController.position.userScrollDirection ==
ScrollDirection.reverse) {
if (_isVisible == true) {
/* only set when the previous state is false
* Less widget rebuilds
*/
print("**** $_isVisible up"); //Move IO away from setState
setState(() {
_isVisible = false;
});
}
} else {
if (_hideButtonController.position.userScrollDirection ==
ScrollDirection.forward) {
if (_isVisible == false) {
setState(() {
_isVisible = true;
});
}
}
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text("Example app bar"),
),
body: CustomScrollView(slivers: <Widget>[
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return SizedBox(
height: 500,
child: Stack(
children: [
CustomScrollView(
controller: _hideButtonController,
scrollDirection: Axis.horizontal,
shrinkWrap: true,
slivers: <Widget>[
SliverPadding(
padding: const EdgeInsets.all(20.0),
sliver: SliverList(
delegate: SliverChildListDelegate(
<Widget>[
FlutterLogo(
size: 600,
)
],
),
),
),
],
),
Visibility(
visible: _isVisible,
child: Align(
alignment: Alignment.centerRight,
child: FloatingActionButton(
child: const Icon(Icons.arrow_forward_ios),
onPressed: () {}),
),
)
],
),
);
}, childCount: 10),
)
])));
}
}
How can i make sure that _hideButtonController
works with multiple widget inside SilverList
or ListView
?
Solution 1:[1]
It is not very clear what you are trying to accomplish, but here is the reason why the error is thrown, and maybe some suggestions to help.
The error is thrown because you use the position
getter of the ScrollController
inside your listener code, while having multiple positions attached. Here is a quote from the documentation:
Calling this is only valid when only a single position is attached.
https://api.flutter.dev/flutter/widgets/ScrollController/position.html
You could use positions
in your listener and check your conditions for any of the attached positions, although that is probably not what you want.
_hideButtonController.addListener(() {
if (_hideButtonController.positions.any((pos) => pos.userScrollDirection ==
ScrollDirection.reverse)) {
if (_isVisible == true) {
/* only set when the previous state is false
* Less widget rebuilds
*/
print("**** $_isVisible up"); //Move IO away from setState
setState(() {
_isVisible = false;
});
}
} else {
if (_hideButtonController.positions.any((pos) => pos.userScrollDirection ==
ScrollDirection.forward)) {
if (_isVisible == false) {
setState(() {
_isVisible = true;
});
}
}
}
});
If you only want to share the code for hiding the button when a condition is met for a singular horizontal scroller, your best bet is probably to write your own widget which holds its own ScrollController
with the same listener code you already have. This allows every child of the vertical list to have its own ScrollController
for the horizontal list and thus allows you to only hide the button for the affected controller:
class LogoHidingScrollView extends StatefulWidget {
const LogoHidingScrollView({
Key? key,
}) : super(key: key);
_LogoHidingScrollViewState createState() => _LogoHidingScrollViewState();
}
class _LogoHidingScrollViewState extends State<LogoHidingScrollView> {
final ScrollController _scrollController = ScrollController();
bool _isVisible = true;
void initState() {
_isVisible = true;
_scrollController.addListener(() {
if (_scrollController.position.userScrollDirection ==
ScrollDirection.reverse) {
if (_isVisible == true) {
/* only set when the previous state is false
* Less widget rebuilds
*/
print("**** $_isVisible up"); //Move IO away from setState
setState(() {
_isVisible = false;
});
}
} else {
if (_scrollController.position.userScrollDirection ==
ScrollDirection.forward) {
if (_isVisible == false) {
setState(() {
_isVisible = true;
});
}
}
}
});
super.initState();
}
Widget build(BuildContext context) {
return SizedBox(
height: 500,
child: Stack(
children: [
CustomScrollView(
controller: _scrollController,
scrollDirection: Axis.horizontal,
shrinkWrap: true,
slivers: <Widget>[
SliverPadding(
padding: const EdgeInsets.all(20.0),
sliver: SliverList(
delegate: SliverChildListDelegate(
<Widget>[
FlutterLogo(
size: 600,
)
],
),
),
),
],
),
Visibility(
visible: _isVisible,
child: Align(
alignment: Alignment.centerRight,
child: FloatingActionButton(
child: const Icon(Icons.arrow_forward_ios), onPressed: () {}),
),
)
],
),
);
}
}
If you truly want to synchronize the scrolling behaviour between all those horizontal scroll views you could have a look at: https://pub.dev/packages/linked_scroll_controller
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | puelo |