Reputation: 457
has anyone come across something like fadingEdgeLength in Android for Flutter so that when you scroll up items start fading into the top of the screen?
Below is my interface built up of the Widgets.
If it helps these are the properties I'm referring to:
android:fadingEdgeLength="10dp"
android:requiresFadingEdge="horizontal">
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('CMS Users'),
),
body: ListView.builder(
padding: EdgeInsets.only(top: 20.0, left: 4.0),
itemExtent: 70.0,
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
return Card(
elevation: 10.0,
child: InkWell(
onTap: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) =>
new PeopleDetails("Profile Page", profiles[index]),
));
},
child: ListTile(
leading: CircleAvatar(
child: Text(profiles[index].getInitials()),
backgroundColor: Colors.deepPurple,
radius: 30.0,
),
title: Text(
data[index]["firstname"] + "." + data[index]["lastname"]),
subtitle: Text(
data[index]["email"] + "\n" + data[index]["phonenumber"]),
),
),
);
}),
);
}
}
Upvotes: 32
Views: 30184
Reputation: 1
I prefer to use stack with gradient to achieve this, also you should give top padding to your ListView, so when there is no scroll required gradient will not cover your first item. But ofc this only works if you have some background color under your ListView.
Stack(
children: [
ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 550),
child: ListView.separated(
shrinkWrap: true,
padding: const EdgeInsets.only(bottom: 16, top: 16),
itemBuilder: (context, i) {
return _ReferralCard(
name: 'SomeName${Random().nextInt(1000000)}',
avatarUri:
'https://picsum.photos/id/${Random().nextInt(600)}/200',
);
},
separatorBuilder: (context, i) => const SizedBox(
height: 8,
),
itemCount: 2000),
),
Positioned(
top: 0,
left: 0,
right: 0,
height: 20,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppPalette.surfacePrimary,
AppPalette.surfacePrimary.withAlpha(0),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
),
),
],
)
Upvotes: 0
Reputation: 61
I combined the answer from Krisztián Ódor and a result from ChatGPT to get a widget that fades the content of a ListView as the user scrolls through the content. When the ListView is on either side, on this side there is no fading. I have done this because in the previous version part of my content was not visible due to the fading. The widget works for both scroll directions and handles reverse: true
. To get more or less fading adjust the stops in the LinearGradient.
import 'package:flutter/material.dart';
class FadingListView extends StatefulWidget {
const FadingListView({required this.child, super.key});
final ListView child;
@override
State<FadingListView> createState() => _FadingListViewState();
}
class _FadingListViewState extends State<FadingListView> {
double _stopStart = 0;
double _stopEnd = 1;
@override
Widget build(BuildContext context) {
return NotificationListener<ScrollNotification>(
onNotification: (scrollNotification) {
setState(() {
_stopStart = scrollNotification.metrics.pixels / 10;
_stopEnd = (scrollNotification.metrics.maxScrollExtent -
scrollNotification.metrics.pixels) /
10;
_stopStart = _stopStart.clamp(0.0, 1.0);
_stopEnd = _stopEnd.clamp(0.0, 1.0);
});
return true;
},
child: ShaderMask(
shaderCallback: (Rect rect) {
return LinearGradient(
begin: widget.child.scrollDirection == Axis.horizontal
? Alignment.centerLeft
: Alignment.topCenter,
end: widget.child.scrollDirection == Axis.horizontal
? Alignment.centerRight
: Alignment.bottomCenter,
colors: const [
Colors.black,
Colors.transparent,
Colors.transparent,
Colors.black
],
stops: widget.child.reverse
? [0.0, 0.05 * _stopEnd, 1 - 0.05 * _stopStart, 1.0]
: [0.0, 0.05 * _stopStart, 1 - 0.05 * _stopEnd, 1.0],
).createShader(rect);
},
blendMode: BlendMode.dstOut,
child: Padding(
padding: const EdgeInsets.all(1.0),
child: widget.child,
),
),
);
}
}
Upvotes: 6
Reputation: 1208
As others have mentioned, you can put the ListView under a ShaderMask, but with minor extra parameterizations you can get much better results - at least if you want to achieve what I wanted.
Optionally you can set the [stops] list for the LinearGradient:
The [stops] list, if specified, must have the same length as [colors]. It specifies fractions of the vector from start to end, between 0.0 and 1.0, for each color.
Plus: There are blend modes, where the color channels of the source are ignored, only the opacity has an effect. BlendMode.dstOut is also such in the example below. As you can see in the screenshot, the purple color is not used concretely, only for the fractions of the vector.
You can play with the different [blendMode] settings, there are quite a few of them.
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: FadingListViewWidget(),
),
),
);
}
class FadingListViewWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
height: 320,
child: ShaderMask(
shaderCallback: (Rect rect) {
return LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.purple, Colors.transparent, Colors.transparent, Colors.purple],
stops: [0.0, 0.1, 0.9, 1.0], // 10% purple, 80% transparent, 10% purple
).createShader(rect);
},
blendMode: BlendMode.dstOut,
child: ListView.builder(
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return Card(
color: Colors.orangeAccent,
child: ListTile(
title: Text('test test test test test test'),
),
);
},
),
),
),
);
}
}
Upvotes: 58
Reputation: 81
Wrap the Listview with Stack, add the Listview as the first child, the second is Positioned Container with LinearGradient. Sample from my code:
Stack:
return Stack(
children: <Widget>[
ListView(
scrollDirection: Axis.horizontal,
children: _myListOrderByDate,
),
FadeEndListview(),
],
);
The overlay class:
class FadeEndListview extends StatelessWidget {
const FadeEndListview({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Positioned(
right: 0,
width: 8.0,
height: kYoutubeThumbnailsHeight,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerRight,
end: Alignment.centerLeft,
stops: [0.0, 1.0],
colors: [
Theme.of(context).scaffoldBackgroundColor,
Theme.of(context).scaffoldBackgroundColor.withOpacity(0.0),
],
),
),
),
);
}
}
And it will look something like this:
Upvotes: 6
Reputation: 2711
I had similar request so I created a library for this task. You can find it here: fading_edge_scrollview
To use it you need to add a ScrollController
to your ListView
and then pass this ListView
as child to FadingEdgeScrollView.fromScrollView
constructor
Upvotes: 11
Reputation: 297
You could apply a ShaderMask
on top of ListView
and use BlendMode
to get what you want.
Widget animationsList() {
return Expanded(
child: ShaderMask(
shaderCallback: (Rect bounds) {
return LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[Colors.transparent,Colors.red],
).createShader(bounds);
},
child: Container(height: 200.0, width: 200.0, color: Colors.blue,),
blendMode: BlendMode.dstATop,
),
);
Upvotes: 29
Reputation: 1228
Try to use
Text(
'This is big text, I am using Flutter and trying to fade text',
overflow: TextOverflow.fade,
maxLines: 1,
),
Upvotes: 0