I have a layout where I have two scroll views, one is a PageView
which is used to display a list of pictures horizontally, the second is a SingleChildScrollView
(maybe should be something else?), this scroll view is partially above the first one as initial position and can go completely over. Some screenshots to image this:
The problem is, if I make the 2nd scroll view take all page and add internal padding to it to make it the good height, the 2nd scroll view work as expected but the first one (witch is behind) isn't working anymore. The 2nd solution would be to wrap the SingleChildScrollView
with a Padding
widget to make it the good height and add Clip.none
parameter to the scroll view, but in this case the scroll view cannot be scrolled in PageView
zone even if the SingleChildScrollView
is over.
I wonder if someone already encountered a behavior like this and what is your solution.
Here my actual code with second solution:
Widget body() {
return LayoutBuilder(
builder: (context, boxConstraints) {
this.boxConstraints = boxConstraints;
uiHeight = boxConstraints.maxHeight -
boxConstraints.maxWidth * 9 / 16 +
scrollableUIBorderRadius;
return Stack(
children: [
coverWidget(),
scrollableUI(),
],
);
},
);
}
Widget coverWidget() { //This is the PageView containing pictures
return Positioned.fill(
bottom: uiHeight - scrollableUIBorderRadius,
child: global.coverWidget(
placeId: widget.place.id,
galleryItemsLinks: widget.galleryItemsLinks!,
initialIndex: widget.coverIndex,
onIndexUpdate: widget.onUpdateIndex,
circleIndicatorsDistanceFromBottom:
(boxConstraints.maxHeight - uiHeight) / 15 +
scrollableUIBorderRadius,
));
}
Widget scrollableUI() {
return Positioned.fill(
top: boxConstraints.maxHeight - uiHeight,
child: SingleChildScrollView(
clipBehavior: Clip.none,
controller: _scrollController,
physics: AlwaysScrollableScrollPhysics(parent: BouncingScrollPhysics()),
scrollDirection: Axis.vertical,
child: SizedBox(
height: uiHeight * 2,
child: Stack(
children: [
Positioned.fill(
child: global.containerWidget(
placeId: widget.place.id,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(scrollableUIBorderRadius),
topRight: Radius.circular(scrollableUIBorderRadius)),
),
),
],
),
),
),
);
}
NB: if possible I want to conserve those BouncingScrollPhysics
easily so a solution with AnimationBuilder
&& Transform
&& GestureDetector
is the last resort.
Solution 1: Milvintsiss
I found a little hack to do this, I passed the PageView
containing the photos inside the the SingleChildScrollView
so the PageView
is on top and can receive touch events, to keep the PageView
fixed to the top of the screen I use a Transform
widget that reverse the scrollOffset
of the SingleChildScrollView
.
My implementation:
(here the Transform
widget is also used to resize the PageView
containing photos when the scrollOffset
is under 0)
[...]
late ScrollController _scrollController = ScrollController()
..addListener(_scrollListener);
double _scrollOffset = 0.0;
[...]
_scrollListener() {
setState(() {
_scrollOffset = _scrollController.offset;
});
}
[...]
Widget body() {
return LayoutBuilder(builder: (context, boxConstraints) {
this.boxConstraints = boxConstraints;
coverHeight = boxConstraints.maxWidth * 9 / 16;
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(parent: BouncingScrollPhysics()),
controller: _scrollController,
child: Column(
children: [
Transform(
transform: Matrix4.compose(
vector.Vector3(
_scrollOffset > 0 ? 0 : _scrollOffset, _scrollOffset, 0),
vector.Quaternion.euler(0.0, 0.0, 0.0),
vector.Vector3(
_scrollOffset > 0
? 1.0
: 1.0 - _scrollOffset / boxConstraints.maxHeight * 4,
_scrollOffset > 0
? 1.0
: 1.0 - _scrollOffset / boxConstraints.maxHeight * 4,
1.0)),
child: SizedBox(
height: coverHeight,
child: global.coverWidget(
placeId: widget.place.id,
galleryItemsLinks: widget.galleryItemsLinks!,
initialIndex: widget.coverIndex,
onIndexUpdate: widget.onUpdateIndex,
circleIndicatorsDistanceFromBottom:
coverHeight / 15 + scrollableUIBorderRadius,
),
),
),
Stack(
clipBehavior: Clip.none,
children: [
Positioned.fill(
top: -scrollableUIBorderRadius,
child: global.containerWidget(
placeId: widget.place.id,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(scrollableUIBorderRadius),
topRight: Radius.circular(scrollableUIBorderRadius)),
),
),
Column(
children: [
[...]
],
),
],
),
],
),
);
});
}