• Runtimes
  • How to render a background png with spine_flutter

I use onBeforePaint: (controller, canvas) lifecycle and use canvas.drawImageRect, not working

Related Discussions
...

To render a background PNG with spine_flutter, you can use the onBeforePaint callback to draw the image before the skeleton is rendered. Here is a step-by-step guide to achieve this:

  1. Load the Image: Ensure you have loaded the image you want to use as the background. You can use Flutter's Image class to load the image.

  2. Draw the Image in onBeforePaint: Use the canvas.drawImageRect method within the onBeforePaint callback to draw the image. Make sure the coordinates and size are set correctly.

Here is an example code snippet:

import 'package:flutter/material.dart';
import 'package:spine_flutter/spine_flutter.dart';

class SpineBackgroundExample extends StatefulWidget {
  @override
  _SpineBackgroundExampleState createState() => _SpineBackgroundExampleState();
}

class _SpineBackgroundExampleState extends State<SpineBackgroundExample> {
  late SpineWidgetController _controller;
  late Image _backgroundImage;

  @override
  void initState() {
    super.initState();
    _controller = SpineWidgetController();
    _backgroundImage = Image.asset('assets/mybackground.png');
  }

  @override
  Widget build(BuildContext context) {
    return SpineWidget(
      controller: _controller,
      skeletonPath: 'assets/skeleton.json',
      atlasPath: 'assets/skeleton.atlas',
      onBeforePaint: (controller, canvas) {
        // Draw the background image
        final paint = Paint();
        final src = Rect.fromLTWH(0, 0, _backgroundImage.width.toDouble(), _backgroundImage.height.toDouble());
        final dst = Rect.fromLTWH(0, 0, canvas.size.width, canvas.size.height);
        canvas.drawImageRect(_backgroundImage, src, dst, paint);
      },
    );
  }
}

In this example:

  • The Image.asset method is used to load the background image.
  • The onBeforePaint callback is used to draw the image on the canvas before the skeleton is rendered.

Ensure that the image path and the coordinates in drawImageRect are correct. If the image is not displaying, double-check the image loading and the coordinates used for drawing.

    I was tried to excute canvas.restore() before drawImageRect , png background would be rendered. But spine file cannot render correctly!!

    When rendering onBeforePaint(), the canvas transformation matrix is set up such, that the skeleton will be rendered according to the alignment and fit parameters set on the SpineWidget. This also means that when you draw in onBeforePaint(), you drawing code will operate in the coordinate system of the skeleton being rendered.

    If you want to work in the coordinate system of the canvas, you have to temporarily "undo" the transformation SpineWidget sets on the canvas. E.g.

    onBeforePaint: (controller, canvas) {
      // Save the current transform and other canvas state
      canvas.save();
    
      // Get the current canvas transform an invert it, so we can work in the
      // canvas coordinate system.
      final currentMatrix = canvas.getTransform();
      final invertedMatrix = Matrix4.tryInvert(Matrix4.fromFloat64List(currentMatrix));
      if (invertedMatrix != null) {
        canvas.transform(invertedMatrix.storage);
      }
    
      // Draw something.
      final Paint paint = Paint()
        ..color = Colors.black
        ..strokeWidth = 2.0;
    
      canvas.drawLine(
        Offset(0, 0),
        Offset(canvas.getLocalClipBounds().width, canvas.getLocalClipBounds().height),
        paint,
      );
    
      // Restore the old transform and canvas state
      canvas.restore();
    }

    Alternatively, you can make the SpineWidget have a transparent background, then stack it on top of a Flutter Image widget that displays your background.

      Mario Thanks. By the way, spine_flutter seems not support render multiple spine files simultaneously, will there be plans for support? I have currently rewritten the SpineWidget class to support this feature, it's essential for my project.

      How do you mean? Render multiple skeletons in a single SpineWidget? Or rendering multiple SpineWidget instances each with their own skeleton? The later should work. If it doesn't, please file an issue on GitHub and if you have rewritten the class, sharing your changes could accelerate fixing the bug.

        Mario Render multiple skeletons in a single SpineWidget.