Skip to content

Instantly share code, notes, and snippets.

@flar
Last active February 26, 2025 14:12
Show Gist options
  • Save flar/e6258443da95ae0815a593959f5a701b to your computer and use it in GitHub Desktop.
Save flar/e6258443da95ae0815a593959f5a701b to your computer and use it in GitHub Desktop.
Example of how to fade the edges of a BackdropFilter
import 'dart:ui';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double horizontalFadeAmount = 0.0;
double verticalFadeAmount = 10.0;
double fadeBias = 1.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Column(
children: <Widget>[
Expanded(
child: Stack(
alignment: Alignment.center,
children: [
Text(
'xxx' * 1000,
),
ClipRect(
child: SizedBox(
width: 300,
height: 300,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
if (horizontalFadeAmount > 0.0 || verticalFadeAmount > 0.0)
CustomPaint(
painter: FadedEdgePainter(
horizontalFadeSize: horizontalFadeAmount,
verticalFadeSize: verticalFadeAmount,
bias: fadeBias,
),
size: const Size(400, 400),
),
const Text('BDF content'),
],
),
),
)
),
],
),
),
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('fade horizontal edges'),
Slider(
value: horizontalFadeAmount,
min: 0.0,
max: 20.0,
onChanged: (value) {
setState(() {
horizontalFadeAmount = value;
});
},),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('fade vertical edges'),
Slider(
value: verticalFadeAmount,
min: 0.0,
max: 20.0,
onChanged: (value) {
setState(() {
verticalFadeAmount = value;
});
},),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('fade bias'),
Slider(
value: fadeBias,
min: 0.0,
max: 1.0,
onChanged: (value) {
setState(() {
fadeBias = value;
});
},),
],
),
],
),
],
),
);
}
}
class FadedEdgePainter extends CustomPainter {
FadedEdgePainter({
this.horizontalFadeSize = 0.0,
this.verticalFadeSize = 0.0,
this.bias = 1.0,
});
final double horizontalFadeSize;
final double verticalFadeSize;
// 0.0 means no fade visible, 1.0 means full fade from 100% to 0% visible
final double bias;
@override
void paint(Canvas canvas, Size size) {
Paint p = Paint();
p.imageFilter = ImageFilter.blur(
sigmaX: horizontalFadeSize,
sigmaY: verticalFadeSize,
tileMode: TileMode.decal,
);
p.blendMode = BlendMode.dstIn;
// The color doesn't matter, only the alpha value...
p.color = Colors.purple;
// You need to use a smaller rect than the space you are trying to fill.
//
// With no inset then the fading at the edge will go from 100% to 50% as
// you approach the edge and then drop suddenly to 0% at the edge. That's
// because the blur is centered on the edge with half of it on the
// interior and half on the exterior, but you are clipped to the size,
// so you never see the ramp from 50% to 0%.
//
// By using a smaller rect, you can bring more of that final ramp to 0
// into the visible area. Or, maybe you want the blur to jump from
// 50% to 0% at the edge - play with the bias to check out different looks.
double hGap = horizontalFadeSize * (2.0 * bias - 1.0);
double vGap = verticalFadeSize * (2.0 * bias - 1.0);
Rect r = Rect.fromLTRB(hGap, vGap, size.width - hGap, size.height - vGap);
canvas.drawRect(r, p);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment