在Flutter中将图像转换为灰度:ColorFiltered组件

背景

在最近的项目中,我遇到了一个需求:当满足特定条件时,需要将图像转换为灰度。例如,用户个人资料页面有一组用来显示个人社交信息,并展示相关社交平台图标,如果用设置了社交信息,则该社交平台的图标应处理点亮状态,如果没有设置,则应处于禁用状态,也就是是说,我需要把对应图标的颜色改为灰度模式。这看起来很简单,特别是对于那些单一颜色的图标,对吧?但实际上,有一些图标是彩色的而且色彩丰富,比如说instagram。

BlendMode

我首先研究了Flutter中的混合模式(BlendMode),结果发现它们确实效果很好 ------ 但这仅适用于没有透明度的JPEGPNG 图像。以下是我用于将彩色透明PNG图像显示为灰度图像的代码。

dart 复制代码
ColorFiltered(
        colorFilter: enabled ? ColorFilter.mode(
          Colors.transparent,
          BlendMode.multiply,
        ) : ColorFilter.mode(
          Colors.grey,
          BlendMode.saturation,
        ),
        child: Image.asset("assets/roundabout.png",),
      )

上面用的图片是一个带有透明的PNG图片,让我们看看效果:

再放一个没有经过处理的demo用的原图:

很显然,这样的效果很糟糕。混合模式(BlendMode)也会影响图像中的透明部分,并将它们也转换为灰色,但我们需要让这些透明区域也不受影响。

颜色矩阵

经过一番研究,我在Flutter文档的深处发现了这个内容。文档中提到了另一个构造函数,它可以用来定义一个矩阵,从而更精细地控制混合模式的应用方式。

于是,我用ColorFilter.matrix替换了ColorFilter.mode构造函数,并传入了这些神奇的数字。

dart 复制代码
ColorFilter.matrix(<double>[
 0.2126,0.7152,0.0722,0,0,
 0.2126,0.7152,0.0722,0,0,
 0.2126,0.7152,0.0722,0,0,
 0,0,0,1,0,
])

没错,这正是我一直在寻找的解决方案。文档中还提到了更多示例。大家可以随意尝试这些示例,并对这些数值进行调整。

** 反色(Inversion)** 效果的颜色矩阵:

dart 复制代码
const ColorFilter invert = ColorFilter.matrix(<double>[
   -1, 0, 0, 0, 255,
   0, -1, 0, 0, 255,
   0, 0, -1, 0, 255,
   0, 0, 0, 1, 0, ]);

** 复古棕褐色(Sepia Tone)** 效果的颜色矩阵

dart 复制代码
const ColorFilter sepia = ColorFilter.matrix(<double>[ 
   0.393, 0.769, 0.189, 0, 0, 
   0.349, 0.686, 0.168, 0, 0,
   0.272, 0.534, 0.131, 0, 0, 
   0, 0, 0, 1, 0, ]);

个人感觉这个效果适用于复古风格 UI、老照片效果、特定主题界面

灰度滤镜(Grayscale Color Filter) 效果矩阵

标准灰度滤镜矩阵(最常用)

dart 复制代码
const ColorFilter greyscale = ColorFilter.matrix(<double>[ 
  0.2126, 0.7152, 0.0722, 0, 0, // 红色通道转换
  0.2126, 0.7152, 0.0722, 0, 0, // 绿色通道转换
  0.2126, 0.7152, 0.0722, 0, 0, // 蓝色通道转换
  0, 0, 0, 1, 0, //透明度保持不变
  ]);

上面的的灰度算法是一个标准的灰度算法,还两种算法可供参考:

简化版灰度矩阵(平均法)

dart 复制代码
ColorFilter.matrix([ 
0.333, 0.333, 0.333, 0, 0, // 红色通道取平均值 
0.333, 0.333, 0.333, 0, 0, // 绿色通道取平均值 
0.333, 0.333, 0.333, 0, 0, // 蓝色通道取平均值 
0, 0, 0, 1, 0, // 透明度保持不变 
])

高对比度灰度矩阵

dart 复制代码
ColorFilter.matrix([ 
0.299, 0.587, 0.114, 0, 0, // 红色通道(电视标准算法) 
0.299, 0.587, 0.114, 0, 0, // 绿色通道 
0.299, 0.587, 0.114, 0, 0, // 蓝色通道 
0, 0, 0, 1, 0, // 透明度保持不变
])

矩阵原理

  • 人眼对红、绿、蓝三色的敏感度不同,公式 亮度 = 0.2126*R + 0.7152*G + 0.0722*B 可模拟真实灰度效果。
  • 矩阵中每行对应一个颜色通道(红、绿、蓝、透明度),每列对应输入颜色的分量(R、G、B、A、偏移量)。

让我们再回头看看BlendMode相关属性:

关键属性解析

属性 说明
colorFilter 必需参数,指定颜色滤镜。可通过 ColorFilter.modeColorFilter.matrix 创建。
BlendMode 混合模式(如 colorBurnsrcIn 等),影响颜色过滤的最终效果。
child 待处理的子组件(通常为 ImageIcon 等可视化组件)。

注意事项

  1. 性能影响

    • 复杂的 ColorFilter.matrix 可能对性能产生轻微影响,建议对静态图像提前处理为灰度图。
    • 动态切换时,避免在列表或高频率刷新场景中大量使用。
  2. 混合模式选择

    • BlendMode.color 可保留亮度,仅修改色相和饱和度,适合部分灰度效果。
    • BlendMode.luminosity 直接提取亮度,是最纯净的灰度转换模式。

最后

通过 ColorFiltered 组件,Flutter 提供了简洁高效的图像灰度处理方案。无论是静态效果还是动态交互,合理选择混合模式或矩阵运算,均可灵活实现所需视觉效果。

也希望大家关注我的微信公众号OpenFlutter

相关推荐
乱码三千10 分钟前
密码管理工具站正式上线啦
前端·html·github
IMPYLH15 分钟前
HTML 的 <rt> 元素
前端·html
单线程_0120 分钟前
从案例分析 Vue3 Tokenizer+Parser 源码五
前端·javascript·vue.js
mldong8 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排8 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60619 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角10 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!10 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰10 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi12 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js