4

I am trying to achieve effect demonstrated in the image at the bottom of this question, essentially I have orc graphic (square) and a png for dark cloud/smoke that I want to use as a mask for my orc image.

Thus far I found options to clip images using bezier curves but nothing on using other images as a mask. Is this achievable?

enter image description here

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Ilja
  • 44,142
  • 92
  • 275
  • 498

1 Answers1

3

You can certainly do it with CustomPainter. Note that there are two different classes in Flutter called Image. The normal one is a Widget; the other one (part of the ui package) is closer to a bitmap. We'll be using the latter. I put two images in my assets folder (cute kitten and background with transparent hole). This shows how to load the graphics from assets, convert them to bitmaps, and how to draw those to a Canvas. End result is kitten showing through hole.

import 'dart:ui' as ui;
import 'dart:typed_data';
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;

class ImageOverlay extends StatefulWidget {
  @override
  State createState() => new ImageOverlayState();
}

class ImageOverlayState extends State<ImageOverlay> {
  ui.Image kitten;
  ui.Image hole;

  @override
  Widget build(BuildContext context) {
    return new SizedBox(
      width: 500.0,
      height: 500.0,
      child: new CustomPaint(
        painter: new OverlayPainter(hole, kitten),
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    load('assets/hole.png').then((i) {
      setState(() {
        hole = i;
      });
    });
    load('assets/kitty.jpg').then((i) {
      setState(() {
        kitten = i;
      });
    });
  }

  Future<ui.Image> load(String asset) async {
    ByteData data = await rootBundle.load(asset);
    ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
    ui.FrameInfo fi = await codec.getNextFrame();
    return fi.image;
  }
}

class OverlayPainter extends CustomPainter {
  ui.Image hole;
  ui.Image kitten;

  OverlayPainter(this.hole, this.kitten);

  @override
  void paint(Canvas canvas, Size size) {
    if (kitten != null) {
      canvas.drawImage(kitten, Offset(0.0, 0.0), new Paint());
    }
    if (hole != null) {
      canvas.drawImage(hole, Offset(0.0, 0.0), new Paint());
    }
  }

  @override
  bool shouldRepaint(OverlayPainter oldDelegate) {
    return hole != oldDelegate.hole || kitten != oldDelegate.kitten;
  }
}

When drawing the images to the Canvas you may need to deal with Transforms to scale the images correctly.

I didn't get a chance to try, but have you tried a Stack with two (widget) Images positioned on top of each other?

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Richard Heap
  • 48,344
  • 9
  • 130
  • 112