I have tried to implement a BlendModeWidget
component to mix two PNG images with BlendMode
, but the effect was not as good as expected.
Here is my code:
// main screen
class BlendModeDemo extends StatelessWidget {
const BlendModeDemo({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: BlendModeWidget(
blendMode: BlendMode.multiply,
src: "assets/radio_mix.png",
dst: Align(
alignment: const Alignment(-0.4, 0.8),
child: Image.asset(
"assets/radio.png",
fit: BoxFit.fitHeight,
height: 330,
),
),
),
);
}
}
class BlendModeWidget extends StatefulWidget {
final String src;
final Widget? dst;
final BlendMode blendMode;
const BlendModeWidget({
super.key,
this.dst,
required this.src,
required this.blendMode,
});
@override
State<BlendModeWidget> createState() => _BlendModeWidgetState();
}
class _BlendModeWidgetState extends State<BlendModeWidget> {
late Future<ui.Image> _future;
Future<ui.Image> loadImage(String imagePath) async {
final completer = Completer<ui.Image>();
final bytes = await rootBundle.load(imagePath);
ui.decodeImageFromList(bytes.buffer.asUint8List(), completer.complete);
return completer.future;
}
@override
void initState() {
_future = loadImage(widget.src);
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _future,
builder: (context, snapshot) {
return snapshot.hasData && snapshot.data != null
? CustomPaint(
foregroundPainter: BlendModePainter(
image: snapshot.data as ui.Image,
blendMode: widget.blendMode,
),
child: widget.dst,
)
: Container(child: null);
},
);
}
}
class BlendModePainter extends CustomPainter {
final ui.Image image;
final BlendMode blendMode;
BlendModePainter({
required this.image,
required this.blendMode,
});
@override
void paint(Canvas canvas, Size size) {
ui.Image img = image;
// 计算图像的宽高比
double imageWidth = img.width.toDouble();
double imageHeight = img.height.toDouble();
// 计算绘制区域的宽度和高度,以适应CustomPaint的宽度
double paintWidth = size.width;
double paintHeight = size.height;
// 容器宽高比
double ratio = paintHeight / paintWidth;
double offset = imageHeight - imageWidth * ratio;
// 创建绘制区域
Rect srcRect =
Rect.fromPoints(Offset(0, offset), Offset(imageWidth, imageHeight));
Rect destRect =
Rect.fromPoints(Offset.zero, Offset(paintWidth, paintHeight));
// 绘制图片
Paint paint = Paint()
..filterQuality = FilterQuality.high
..blendMode = blendMode;
canvas.drawImageRect(img, srcRect, destRect, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
These are the pictures:
The effect of running the above code:
I have tried to modify the code:
class BlendModeDemo extends StatelessWidget {
const BlendModeDemo({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
// Wrap it with a ColorFiltered,it`s working on ios, howerver not work on mac or android!
child: ColorFiltered(
colorFilter: ColorFilter.mode(
Colors.white.withOpacity(0.01),
BlendMode.srcATop,
),
child: BlendModeWidget(
blendMode: BlendMode.multiply,
src: "assets/bg_mix.png",
dst: Align(
alignment: const Alignment(-0.4, 0.8),
child: Image.asset(
"assets/音响.png",
fit: BoxFit.fitHeight,
height: 330,
),
),
),
),
);
}
}
Wrap it with a ColorFiltered, it`s working on iOS, but it does not work on Mac or Android!