import 'dart:ui' as ui; import 'package:flutter/material.dart'; class MaskedTicketImage extends StatefulWidget { final String imagePath; final String maskPath; final double width; final double height; const MaskedTicketImage({ super.key, required this.imagePath, required this.maskPath, required this.width, required this.height, }); @override State createState() => _MaskedTicketImageState(); } class _MaskedTicketImageState extends State { ui.Image? image; ui.Image? mask; @override void initState() { super.initState(); _loadImages(); } Future _loadImages() async { final img = await _loadAsset(widget.imagePath); final maskImg = await _loadAsset(widget.maskPath); if (mounted) { setState(() { image = img; mask = maskImg; }); } } Future _loadAsset(String asset) async { final data = await DefaultAssetBundle.of(context).load(asset); final codec = await ui.instantiateImageCodec(data.buffer.asUint8List()); final frame = await codec.getNextFrame(); return frame.image; } @override Widget build(BuildContext context) { if (image == null || mask == null) { return SizedBox( width: widget.width, height: widget.height, child: const Center(child: CircularProgressIndicator(strokeWidth: 1.5)), ); } return CustomPaint( size: Size(widget.width, widget.height), painter: _MaskedPainter(image!, mask!), ); } } class _MaskedPainter extends CustomPainter { final ui.Image image; final ui.Image mask; _MaskedPainter(this.image, this.mask); @override void paint(Canvas canvas, Size size) { final paint = Paint(); // Fill background to avoid black transparency canvas.drawColor(Colors.transparent, BlendMode.srcOver); // Draw base image canvas.drawImageRect( image, Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble()), Rect.fromLTWH(0, 0, size.width, size.height), paint, ); // Apply mask paint.blendMode = BlendMode.dstIn; canvas.drawImageRect( mask, Rect.fromLTWH(0, 0, mask.width.toDouble(), mask.height.toDouble()), Rect.fromLTWH(0, 0, size.width, size.height), paint, ); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; }