Open Reinbowsaur opened 4 years ago
Same issue here.
Sometimes I see such thing when I add FadingEdgeScrollView and experiment with it's parameters using hot reload. After using hot restart it works properly.
If hot restart does not help - could you provide some source code example which reproduces the problem?
Same thing, there is some issue in the code on Singlechildscrollview. If you add gradient to the start and end they add on to each other so the fade becomes twice as long on the bottom of the scrollview. I would say this is because you are adding a container or box over the scrollview and fading the ends but that container height has gone to zero. Just a guess.
Same issue here, any news ?
I have this effect with a PageView as a child (created via PageView.builder). Once the page is scrolled left even a tiny bit the fading shows up.
I also have the same issue. Any solution to this problem?
Is there any solution already?
It can be reproduced on a physical iPhone device, but not on Simulator, in my case.
I was able to work around this by forcing a ScrollController callback only once at first after drawing.
useEffect(
() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
() async {
await Future.delayed(const Duration(milliseconds: 200));
scrollController.animateTo(
1,
duration: const Duration(milliseconds: 1),
curve: Curves.ease,
);
print(scrollController.position.maxScrollExtent);
}();
});
return null;
},
const [],
);
(This code assumes flutter_hooks. If it is a StatefulWidget, place it in initState.)
I had the same issue when I created the ScrollController directly in the constructor. Defining a Scrollcontroller outside my Build function and then passing that controller fixed it for me
Use this implementation. You simply need to wrap any scrollable widget (such as ListView
, GridView
, SingleChildScrollView
, etc.) with the FadingEdgeScrollView
widget and it will automatically determine the scroll direction and apply the fading effect. No ScrollController
is needed.
import 'package:flutter/material.dart';
class FadingEdgeScrollView extends StatefulWidget {
const FadingEdgeScrollView({
super.key,
required this.child,
this.gradientFractionOnStart = 0.1,
this.gradientFractionOnEnd = 0.1,
}) : assert(gradientFractionOnStart >= 0 && gradientFractionOnStart <= 1),
assert(gradientFractionOnEnd >= 0 && gradientFractionOnEnd <= 1);
final Widget child;
/// what part of screen on start half should be covered by fading edge gradient
/// [gradientFractionOnStart] must be 0 <= [gradientFractionOnStart] <= 1
/// 0 means no gradient,
/// 1 means gradients on start half of widget fully covers it
final double gradientFractionOnStart;
/// what part of screen on end half should be covered by fading edge gradient
/// [gradientFractionOnEnd] must be 0 <= [gradientFractionOnEnd] <= 1
/// 0 means no gradient,
/// 1 means gradients on start half of widget fully covers it
final double gradientFractionOnEnd;
@override
State<FadingEdgeScrollView> createState() => _FadingEdgeScrollViewState();
}
class _FadingEdgeScrollViewState extends State<FadingEdgeScrollView> {
bool? _isScrolledToStart;
bool? _isScrolledToEnd;
@override
Widget build(BuildContext context) {
return NotificationListener<ScrollUpdateNotification>(
onNotification: (ScrollUpdateNotification notification) {
final metrics = notification.metrics;
setState(() {
_isScrolledToStart = metrics.pixels <= metrics.minScrollExtent;
_isScrolledToEnd = metrics.pixels >= metrics.maxScrollExtent;
});
return false;
},
child: ShaderMask(
shaderCallback: (bounds) => LinearGradient(
begin: _gradientStart,
end: _gradientEnd,
stops: [
0,
widget.gradientFractionOnStart * 0.5,
1 - widget.gradientFractionOnEnd * 0.5,
1,
],
colors: _getColors(
widget.gradientFractionOnStart > 0 &&
!(_isScrolledToStart ?? true),
widget.gradientFractionOnEnd > 0 && !(_isScrolledToEnd ?? false)),
).createShader(
bounds.shift(Offset(-bounds.left, -bounds.top)),
textDirection: Directionality.of(context),
),
blendMode: BlendMode.dstIn,
child: widget.child,
),
);
}
AlignmentGeometry get _gradientStart =>
_scrollDirection == Axis.vertical ? _verticalStart : _horizontalStart;
AlignmentGeometry get _gradientEnd =>
_scrollDirection == Axis.vertical ? _verticalEnd : _horizontalEnd;
Alignment get _verticalStart =>
_reverse ? Alignment.bottomCenter : Alignment.topCenter;
Alignment get _verticalEnd =>
_reverse ? Alignment.topCenter : Alignment.bottomCenter;
AlignmentDirectional get _horizontalStart => _reverse
? AlignmentDirectional.centerEnd
: AlignmentDirectional.centerStart;
AlignmentDirectional get _horizontalEnd => _reverse
? AlignmentDirectional.centerStart
: AlignmentDirectional.centerEnd;
List<Color> _getColors(bool isStartEnabled, bool isEndEnabled) => [
(isStartEnabled ? Colors.transparent : Colors.white),
Colors.white,
Colors.white,
(isEndEnabled ? Colors.transparent : Colors.white)
];
/// The axis along which child view scrolls
///
/// Look for more documentation at [ScrollView.scrollDirection]
Axis get _scrollDirection {
final child = widget.child;
if (child is ScrollView) {
return child.scrollDirection;
} else if (child is SingleChildScrollView) {
return child.scrollDirection;
} else if (child is PageView) {
return child.scrollDirection;
} else if (child is AnimatedList) {
return child.scrollDirection;
} else {
return Axis.vertical;
}
}
/// Whether the scroll view scrolls in the reading direction.
///
/// Look for more documentation at [ScrollView.reverse]
bool get _reverse {
final child = widget.child;
if (child is ScrollView) {
return child.reverse;
} else if (child is SingleChildScrollView) {
return child.reverse;
} else if (child is PageView) {
return child.reverse;
} else if (child is AnimatedList) {
return child.reverse;
} else {
return false;
}
}
}
Thanks @milkyway044 for the implementation. However, in my case, when the content is small enought to fit without scrolling, it causes the fade to be displayed at the end anyway, which is quite sad.
bump
I've tested this package with 2 scrollviews I have in my app, the fade is only applying to the end of the scrollview, not the start.