1

This time I wish to have a gradient blur effect over the limits of my ScrollingView. So, I have the blur effect at the right position, but I wish the effect grows slowly until the max blur. I don't want the gap between the non-blurred part and one blurred.

enter image description here

This image shows my problem. I don't want that horrible line caused by the effect. It is too evident ...

I've been looking on the internet for some solution, but none of them matches my needs. I put below the code that I'm having right now ...

return Scaffold(
 body: Stack(
 fit: StackFit.expand,
 children: [
 SingleChildScrollView(
 //fancy content
 ),
 Positioned(
 top: 0,
 child: ClipRRect(
 child: BackdropFilter(
 filter: ui.ImageFilter.blur(
 sigmaX: 5,
 sigmaY: 5,
 ),
 child: Container(width: width, height: height*0.15, color: Colors.transparent,)
 ),
 ),
 ),
 ],
 ),
 ); 

Thanks :)

asked Oct 4, 2024 at 10:33
4
  • You can use Gradients to achieve this, Linear Gradient with Shader Mask can be used to fix this. Also you can with different different Opacity Values to make blur gradually. Commented Oct 4, 2024 at 12:21
  • Check this question: stackoverflow.com/questions/60311065/… Commented Oct 4, 2024 at 13:47
  • @Lynxi this should be ok with an Image ... but I don't have to Blur and image, but a Widget Commented Oct 11, 2024 at 8:25
  • @Devraj do you have an example? The way you wrote is quite vague. Commented Oct 11, 2024 at 8:27

1 Answer 1

0

The issue is that BackdropFilter applies uniform blur, causing a hard edge. To create a gradual gradient blur, layer multiple BackdropFilter widgets with increasing blur values and decreasing opacity. Here's a solution:

return Scaffold(
 body: Stack(
 fit: StackFit.expand,
 children: [
 SingleChildScrollView(
 // fancy content
 ),
 // Gradient blur using multiple layers
 Positioned(
 top: 0,
 child: Container(
 width: width,
 height: height * 0.15,
 child: Stack(
 children: List.generate(10, (index) {
 final blurFactor = (index + 1) * 0.5; // 0.5, 1.0, 1.5, ..., 5.0
 final opacity = 1.0 / 10; // Each layer contributes equally
 final layerHeight = (height * 0.15) / 10;
 
 return Positioned(
 top: index * layerHeight,
 left: 0,
 right: 0,
 child: ClipRect(
 child: BackdropFilter(
 filter: ui.ImageFilter.blur(
 sigmaX: blurFactor,
 sigmaY: blurFactor,
 ),
 child: Container(
 height: layerHeight * (index + 1),
 color: Colors.transparent.withOpacity(opacity),
 ),
 ),
 ),
 );
 }),
 ),
 ),
 ),
 ],
 ),
);

A more efficient approach using a ShaderMask with BackdropFilter:

import 'dart:ui' as ui;
return Scaffold(
 body: Stack(
 fit: StackFit.expand,
 children: [
 SingleChildScrollView(
 // fancy content
 ),
 Positioned(
 top: 0,
 child: ShaderMask(
 shaderCallback: (Rect bounds) {
 return ui.Gradient.linear(
 Offset(0, 0),
 Offset(0, bounds.height),
 [Colors.transparent, Colors.black],
 [0.0, 1.0],
 );
 },
 blendMode: BlendMode.dstIn,
 child: ClipRect(
 child: BackdropFilter(
 filter: ui.ImageFilter.blur(
 sigmaX: 5,
 sigmaY: 5,
 ),
 child: Container(
 width: width,
 height: height * 0.15,
 color: Colors.transparent,
 ),
 ),
 ),
 ),
 ),
 ],
 ),
);

Most effective: combine multiple blur layers with a gradient mask:

import 'dart:ui' as ui;
return Scaffold(
 body: Stack(
 fit: StackFit.expand,
 children: [
 SingleChildScrollView(
 // fancy content
 ),
 Positioned(
 top: 0,
 child: ClipRect(
 child: Stack(
 children: [
 // Base blur layer with full strength
 BackdropFilter(
 filter: ui.ImageFilter.blur(
 sigmaX: 5,
 sigmaY: 5,
 ),
 child: Container(
 width: width,
 height: height * 0.15,
 color: Colors.transparent,
 ),
 ),
 // Gradient mask to fade the blur
 ShaderMask(
 shaderCallback: (Rect bounds) {
 return ui.Gradient.linear(
 Offset(0, 0),
 Offset(0, bounds.height),
 [
 Colors.transparent,
 Colors.white,
 ],
 [0.0, 1.0], // Start transparent at top, fully opaque at bottom
 );
 },
 blendMode: BlendMode.dstIn,
 child: Container(
 width: width,
 height: height * 0.15,
 color: Colors.white,
 ),
 ),
 ],
 ),
 ),
 ),
 ],
 ),
);

Note: ShaderMask with BlendMode.dstIn masks the blur itself. Since BackdropFilter applies to what's behind it, the mask affects how much of the blurred area is visible.

Best solution: use stacked blur layers with varying intensities:

import 'dart:ui' as ui;
Widget _buildGradientBlur(double width, double height) {
 const blurHeight = 0.15;
 final totalHeight = height * blurHeight;
 const layers = 15; // More layers = smoother gradient
 
 return Positioned(
 top: 0,
 child: ClipRect(
 child: Stack(
 children: List.generate(layers, (index) {
 // Blur increases from top to bottom
 final blurProgress = (index + 1) / layers; // 0 to 1
 final blurSigma = 5.0 * blurProgress;
 
 // Each layer covers from top to its position
 final layerTop = 0.0;
 final layerHeight = totalHeight * blurProgress;
 
 return Positioned(
 top: layerTop,
 left: 0,
 right: 0,
 child: BackdropFilter(
 filter: ui.ImageFilter.blur(
 sigmaX: blurSigma,
 sigmaY: blurSigma,
 ),
 child: Container(
 height: layerHeight,
 color: Colors.transparent,
 ),
 ),
 );
 }),
 ),
 ),
 );
}
// In your Scaffold:
return Scaffold(
 body: Stack(
 fit: StackFit.expand,
 children: [
 SingleChildScrollView(
 // fancy content
 ),
 _buildGradientBlur(width, height),
 ],
 ),
);

Most performant: a single BackdropFilter with an opacity gradient:

import 'dart:ui' as ui;
return Scaffold(
 body: Stack(
 fit: StackFit.expand,
 children: [
 SingleChildScrollView(
 // fancy content
 ),
 Positioned(
 top: 0,
 child: ClipRect(
 child: BackdropFilter(
 filter: ui.ImageFilter.blur(
 sigmaX: 5,
 sigmaY: 5,
 ),
 child: Container(
 width: width,
 height: height * 0.15,
 decoration: BoxDecoration(
 gradient: LinearGradient(
 begin: Alignment.topCenter,
 end: Alignment.bottomCenter,
 colors: [
 Colors.transparent,
 Colors.white.withOpacity(0.3),
 ],
 ),
 ),
 ),
 ),
 ),
 ),
 ],
 ),
);

It is recommended to start with the stacked layers approach (most control). Adjust the number of layers (15–20) and blur values to fine-tune the gradient. The key is overlapping layers with increasing blur intensity from top to bottom to avoid the hard line.

answered Nov 3, 2025 at 6:40
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.