flutter-高斯模糊效果
分享两种高斯模糊效果的实现
主要变量
double width = 1.0.sw - 32.w;
double top = 0.4.sh;
double height = 90.h;
double radius = 16.w;
前期思路
背景色透明
一开始想的是用Container中背景色的透明度
Container(
height: height,
width: width,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Color.fromRGBO(255, 255, 255, 0.1),
borderRadius: BorderRadius.all(Radius.circular(radius))
),
child: Text(
'高斯模糊',
style: TextStyle(
color: Colors.white
),
),
)
效果如下
额,透明度是有了,但模糊一点都不模糊
加上模糊
网上说可以通过BackdropFilter来实现高斯模糊,大家可以通过这这篇文章简单理解一下# Flutter BackdropFilter 使用介绍
BackdropFilter可以用于对背景图片进行高斯模糊设置或者矩阵变换,除了图片以外BackdropFilter还能对任何子widget进行高斯模糊设置。BackdropFilter的定义如下:
BackdropFilter({Key key,
@required ImageFilter filter,
Widget child })
OK,我们简单的套上试试
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0,sigmaY: 10.0),///整体模糊度
child: Container(
height: height,
width: width,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Color.fromRGBO(255, 255, 255, 0.1),///背景透明
borderRadius: BorderRadius.all(Radius.circular(radius))///圆角
),
child: Text(
'高斯模糊',
style: TextStyle(
color: Colors.white
),
),
),
)

结果整个屏幕全部模糊了,虽然模糊是模糊了,但不是我想要的效果,我只想子组件范围内的模糊,那是否可以通过限定宽高来确定范围呢?我们来试一试
SizedBox(
height: height,
width: width,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0,sigmaY: 10.0),///整体模糊度
child: Container(
height: height,
width: width,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Color.fromRGBO(255, 255, 255, 0.1),
borderRadius: BorderRadius.all(Radius.circular(radius))
),
child: Text(
'高斯模糊',
style: TextStyle(
color: Colors.white
),
),
),
),
)
约束
但效果和上面一样,没有任何缩小范围,看来不是可以通过外部简单约束就可以的,后来通过搜索资料,才知道外层必须加上Clip通过剪切,才可以约束到子组件范围。我们裁剪的同时稍微调整一下宽高和背景色,让质感更好一些
ClipRRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0,sigmaY: 10.0),///整体模糊度
child: Container(
height: height,
width: width,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Color.fromRGBO(175, 175, 175, 0.1),///大背景深色,组件颜色偏浅就会更好
borderRadius: BorderRadius.all(Radius.circular(radius))
),
child: Text(
'高斯模糊',
style: TextStyle(
color: Colors.white
),
),
),
),
)

阴影
到目前一切还好,但我还想加上阴影,于是自然而然就在Container的描述里添加阴影参数
ClipRRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0,sigmaY: 10.0),///整体模糊度
child: Container(
height: height,
width: width,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Color.fromRGBO(175, 175, 175, 0.1),
borderRadius: BorderRadius.all(Radius.circular(radius)),
boxShadow: [
BoxShadow
(
color: Color.fromRGBO(22, 23, 26, 0.5),
offset: Offset(2, 4), // 偏移量
blurRadius: 2
)]
),
child: Text(
'高斯模糊',
style: TextStyle(
color: Colors.white
),
),
),
),
)
就变成这样了
第一种方案
原来是因为透明度,所以本来被覆盖的阴影显示出来,而且因为外部裁剪,所以外部阴影又被裁掉,唉,再次查资料,发现了一种方法,就是通过自定义画笔,通过设定好的路径,画一条阴影出来。在这里贴出相关代码
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class CustomClipperOval extends CustomClipper<RRect> {
final double radius;
final double width;
final double height;
CustomClipperOval({
required this.radius,
required this.width,
required this.height,
});
///此处应和你想要高斯模糊组件的大小一致
@override
RRect getClip(Size size) {
///左、上、右、下,圆角的两个半径,坐标原点是屏幕左上角,坐标系往右下
///因为外部设置了左边距,所以左起点要有边距距离,顶部为0,右边要去掉左部分边距
return RRect.fromLTRBXY((1.0.sw - width)/2, 0, width - (1.0.sw - width)/2, height, radius, radius);
}
@override
bool shouldReclip(CustomClipper<RRect> oldClipper) {
return false;
}
}
class ClipOvalShadow extends StatelessWidget {
final Shadow shadow;
final CustomClipper<RRect> clipper;
final double radius;
final Widget child;
ClipOvalShadow({
required this.shadow,
required this.clipper,
required this.radius,
required this.child,
});
@override
Widget build(BuildContext context) {
///开始自定义绘制
return CustomPaint(
///画阴影
painter: _ClipOvalShadowPainter(
clipper: clipper,
shadow: shadow,
),
///切割内部
child: ClipRRect(clipper: clipper,borderRadius: BorderRadius.all(Radius.circular(radius)), child: child,),
);
}
}
class _ClipOvalShadowPainter extends CustomPainter {
final Shadow shadow;
final CustomClipper<RRect> clipper;
_ClipOvalShadowPainter({required this.shadow, required this.clipper});
@override
void paint(Canvas canvas, Size size) {
var paint = shadow.toPaint();
///根据路径画阴影
var clipRect = clipper.getClip(size).shift(Offset(0, 0));
canvas.drawRRect(clipRect, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
外部调用
ClipOvalShadow(
radius: radius,
///阴影设置
shadow: BoxShadow(
color: Color.fromRGBO(22, 23, 26, 0.5),
offset: Offset(1.0, 1.0),
blurRadius: 20,
blurStyle: BlurStyle.outer///外部阴影,内部不填充
),
///路径
clipper: CustomClipperOval(radius: radius,width: width,height: height),
child: Center(
child: ClipRRect(
child:BackdropFilter(///背景过滤器
filter: ImageFilter.blur(sigmaX: 10.0,sigmaY: 10.0),///整体模糊度
child: Container(///主体背景
width: width,
height: height,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Color.fromRGBO(175, 175, 175, 0.1),///背景透明度
borderRadius: BorderRadius.all(Radius.circular(radius)),
),
child: Text(
'高斯模糊',
style: TextStyle(
color: Colors.white
),
),
),
),
),
),
)
效果如下
这里特意说一下阴影类型blurStyle,如果不特意设置阴影类型,默认的情况就是这个效果
默认阴影类型 | 外部阴影,内部不填充 |
---|---|
![]() | ![]() |
第二种方案
我搞好后突然想到,子组件加阴影不行,我在父组件加是不是就可以?说干就干
Container(
height: height,
width: width,
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.all(Radius.circular(radius)),
boxShadow: [BoxShadow(
color: Color.fromRGBO(22, 23, 26, 0.5),
offset: Offset(1.0, 1.0),
blurRadius: 20,
blurStyle: BlurStyle.outer///外部阴影,内部不填充
)]
),
child: ClipRRect(
child: BackdropFilter(///背景过滤器
filter: ImageFilter.blur(sigmaX: 10.0,sigmaY: 10.0),///模糊度
child: Container(///主体背景
height: height,
width: width,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Color.fromRGBO(175, 175, 175, 0.1),
borderRadius: BorderRadius.all(Radius.circular(16.w)),
),
child: Text(
'高斯模糊',
style: TextStyle(
color: Colors.white
),
),
),
),
),
)
但效果不是很完美,圆角没有完全匹配,通过修改父组件底色可以看到圆角并没有生效
未改变颜色 | 改变底色 |
---|---|
![]() | ![]() |
这里也有两种方法,一个是父组件剪裁,一个是
第一种方法:父组件添加裁剪参数
clipBehavior: Clip.antiAlias,
第二种方法:ClipRRect自带的圆角设置
borderRadius: BorderRadius.all(Radius.circular(radius)),
两种效果是一样的

复用组件
为了方便复用,我把第二种高斯模糊方法单独做一个组件出来
import 'dart:ui';
import 'package:flutter/material.dart';
class Acrylic extends StatefulWidget {
///阴影颜色
final Color shadowColor;
///背景颜色
final Color bgColor;
///背景模糊度
final double blur;
///组件高度
final double height;
///组件宽度
final double width;
///组件是否有圆角
final BorderRadiusGeometry? borderRadius;
///阴影的高斯模糊
final double blurRadius;
///子组件
final Widget child;
const Acrylic({
Key? key,
this.shadowColor = const Color.fromRGBO(22, 23, 26, 0.5),
this.bgColor = const Color.fromRGBO(175, 175, 175, 0.1),
this.blur = 10.0,
required this.height,
required this.width,
this.borderRadius,
this.blurRadius = 20.0,
required this.child,
}) : super(key: key);
@override
State<Acrylic> createState() => _AcrylicState();
}
class _AcrylicState extends State<Acrylic> {
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
Container(
height: widget.height,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: widget.borderRadius,
boxShadow: [BoxShadow(
color: widget.shadowColor,
blurRadius: widget.blurRadius,
blurStyle: BlurStyle.outer///外部阴影,内部不填充
)]
),
child: ClipRRect(
child:BackdropFilter(///背景过滤器
filter: ImageFilter.blur(sigmaX: widget.blur,sigmaY: widget.blur),///模糊度
child: Container(///主体背景
width: widget.width,
height: widget.height,
decoration: BoxDecoration(
color: widget.bgColor,
borderRadius: widget.borderRadius
),
child: widget.child,
),
),
),
),
],
);
}
}
外部调用
Acrylic(
height: height,
width: width,
borderRadius: BorderRadius.all(Radius.circular(radius)),
child: Container(///主体内部不会模糊
alignment: Alignment.center,
child: Text(
'高斯模糊',
style: TextStyle(
color: Colors.white
),
),
)
)
最后
我把几种方法都放在一起,想偷懒的可以直接下载我的工具箱
转载自:https://juejin.cn/post/7211008551762067517