How to add an image as a background with customPaint?
Asked Answered
I

2

9

I have an app for painting using custom paint, and I wanted to add an image in the background so it can be painted over it but unfortunately I can't seem to figure out how to do that,I tried using a stack but every time I do the custom paint will only paint on the container but never on the image for some reason I would really appreciate any help on this matter
Here's the code :

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class DrawingArea {
  Offset point;
  Paint areaPaint;

  DrawingArea({this.point, this.areaPaint});
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<DrawingArea> points = [];
  Color selectedColor;
  double strokeWidth;

  @override
  void initState() {
    super.initState();
    selectedColor = Colors.black;
    strokeWidth = 2.0;
  }

  @override
  Widget build(BuildContext context) {
    final double width = MediaQuery.of(context).size.width;
    final double height = MediaQuery.of(context).size.height;

    void selectColor() {
      showDialog(
        context: context,
        child: AlertDialog(
          title: const Text('Color Chooser'),
          content: SingleChildScrollView(
            child: BlockPicker(
              pickerColor: selectedColor,
              onColorChanged: (color) {
                this.setState(() {
                  selectedColor = color;
                });
              },
            ),
          ),
          actions: <Widget>[
            FlatButton(
                onPressed: () {
                  Navigator.of(context).pop();
                },
                child: Text("Close"))
          ],
        ),
      );
    }

    return Scaffold(
      body: Stack(
        children: <Widget>[
          Container(
            decoration: BoxDecoration(
                gradient: LinearGradient(
                    begin: Alignment.topCenter,
                    end: Alignment.bottomCenter,
                    colors: [
                  Color.fromRGBO(138, 35, 135, 1.0),
                  Color.fromRGBO(233, 64, 87, 1.0),
                  Color.fromRGBO(242, 113, 33, 1.0),
                ])),
          ),
          Center(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Container(
                    width: width * 0.80,
                    height: height * 0.80,
                    decoration: BoxDecoration(
                        borderRadius: BorderRadius.all(Radius.circular(20.0)),
                        boxShadow: [
                          BoxShadow(
                            color: Colors.black.withOpacity(0.4),
                            blurRadius: 5.0,
                            spreadRadius: 1.0,
                          )
                        ]),
                    child: GestureDetector(
                      onPanDown: (details) {
                        this.setState(() {
                          points.add(DrawingArea(
                              point: details.localPosition,
                              areaPaint: Paint()
                                ..strokeCap = StrokeCap.round
                                ..isAntiAlias = true
                                ..color = selectedColor
                                ..strokeWidth = strokeWidth));
                        });
                      },
                      onPanUpdate: (details) {
                        this.setState(() {
                          points.add(DrawingArea(
                              point: details.localPosition,
                              areaPaint: Paint()
                                ..strokeCap = StrokeCap.round
                                ..isAntiAlias = true
                                ..color = selectedColor
                                ..strokeWidth = strokeWidth));
                        });
                      },
                      onPanEnd: (details) {
                        this.setState(() {
                          points.add(null);
                        });
                      },
                      child: SizedBox.expand(
                        child: ClipRRect(
                          borderRadius: BorderRadius.all(Radius.circular(20.0)),
                          child: CustomPaint(
                            painter: MyCustomPainter(points: points),
                          ),
                        ),
                      ),
                    ),
                  ),
                ),
                Container(
                  width: width * 0.80,
                  decoration: BoxDecoration(
                      color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(20.0))),
                  child: Row(
                    children: <Widget>[
                      IconButton(
                          icon: Icon(
                            Icons.color_lens,
                            color: selectedColor,
                          ),
                          onPressed: () {
                            selectColor();
                          }),

                      Expanded(
                        child: Slider(
                          min: 1.0,
                          max: 5.0,
                          label: "Stroke $strokeWidth",
                          activeColor: selectedColor,
                          value: strokeWidth,
                          onChanged: (double value) {
                            this.setState(() {
                              strokeWidth = value;
                            });
                          },
                        ),
                      ),

                      IconButton(
                          icon: Icon(
                            Icons.layers_clear,
                            color: Colors.black,
                          ),
                          onPressed: () {
                            this.setState((){
                              points.clear();
                            });
                          }),
                    ],
                  ),
                )
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class MyCustomPainter extends CustomPainter {
  List<DrawingArea> points;

  MyCustomPainter({@required this.points});

  @override
  void paint(Canvas canvas, Size size) {
    Paint background = Paint()..color = Colors.white;
    Rect rect = Rect.fromLTWH(0, 0, size.width, size.height);
    canvas.drawRect(rect, background);
    canvas.clipRect(rect);

    for (int x = 0; x < points.length - 1; x++) {
      if (points[x] != null && points[x + 1] != null) {
        canvas.drawLine(points[x].point, points[x + 1].point, points[x].areaPaint);
      } else if (points[x] != null && points[x + 1] == null) {
        canvas.drawPoints(PointMode.points, [points[x].point], points[x].areaPaint);
      }
    }
  }

  @override
  bool shouldRepaint(MyCustomPainter oldDelegate) {
    return oldDelegate.points != points;
  }
}
Instantaneity answered 22/6, 2020 at 13:26 Comment(0)
I
8

A canvas doesn't understand the Image type given by Flutter but instead it works with the Image class inside the dart:ui package. Start by importing this:

import 'dart:ui' as ui show Image;

It can be used as argument of drawImage inside the paint method, like this:

class MyCustomPainter extends CustomPainter {
  final ui.Image myBackground;
  const MyCustomPainter(this.myBackground);

  @override
  void paint(Canvas canvas, Size size) {    
    canvas.drawImage(myBackground, Offset.zero, Paint());
  }
}

This will set the given image as background of your canvas. If you're wondering how to convert from Image to ui.Image, look at this simple example which uses decodeImageFromList from dart:ui.

Future<ui.Image> myBackground = await decodeImageFromList(imageFile.readAsBytes())

where imageFile is of type File.

Iodate answered 22/6, 2020 at 14:6 Comment(3)
Thank you very much for your answer this is really helpful, but I'm still new to flutter if you can help me showing me how can I use this line of code Future<ui.Image> myBackground = await decodeImageFromList(imageFile.readAsBytes())Instantaneity
I cannot because it would be too long to explain. Read here: dart.dev/codelabs/async-await it has everything you need!Iodate
thank you very much again for your answer, I will definitely check the link that you have providedInstantaneity
S
0

enter image description here

You should use canvas.clipPath() and paintImage().

import 'dart:ui' as ui;

class ExamplePaint extends CustomPainter {
  final ui.Image? image;
  ExamplePaint({super.repaint, this.image});

  @override
  void paint(Canvas canvas, Size size) {
    ///Drawing line
    Path path_0 = Path();
    // ... - skipped code of lines
    path_0.close();

    ///Background color without image
    Paint paint_0_fill = Paint()..style = PaintingStyle.fill;
    paint_0_fill.color = Colors.black;
    canvas.drawPath(path_0, paint_0_fill);


    ///Drawing image
    if(image!=null) {
      canvas.clipPath(path_0);

      paintImage(
        canvas: canvas,
        rect: Rect.fromLTWH(0, 0, size.width, size.height),
        image: image!,
        fit: BoxFit.cover,
      );
    }

  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }

}

Here is full example:

class Test extends StatefulWidget {
  const Test({
    super.key,
  });

  @override
  State<Test> createState() => _TestState();
}

class _TestState extends State<Test> {
  ui.Image? image;
  Future<void> loadImage() async {
    ///Change asset path
    const keyName = 'assets/anime.png';
    final data = (await rootBundle.load(keyName));
    final bytes = data.buffer.asUint8List();
    image = await decodeImageFromList(bytes);
    setState(() {});
  }

  @override
  void initState() {
    loadImage();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      size: Size(189, (189 * 0.38095238095238093).toDouble()),
      painter: ExamplePaint(color: Colors.white, image: image),
    );
  }
}


class ExamplePaint extends CustomPainter {
  final ui.Image? image;
  final Color color;

  ExamplePaint({super.repaint, required this.color,  this.image});

  @override
  void paint(Canvas canvas, Size size) {
    ///Drawing lines
    Path path_0 = Path();
    path_0.moveTo(size.width * 0.001244926, size.height * 0.5288806);
    path_0.cubicTo(
        size.width * 0.0003297307,
        size.height * 0.5211472,
        size.width * 0.002617720,
        size.height * 0.5404806,
        size.width * 0.002396688,
        size.height * 0.5384292);
    path_0.cubicTo(
        size.width * 0.001680196,
        size.height * 0.5317792,
        size.width * 0.00008811005,
        size.height * 0.5042403,
        size.width * 0.00002077291,
        size.height * 0.4973319);
    path_0.cubicTo(0, size.height * 0.4952000, 0, size.height * 0.5032000, 0,
        size.height * 0.5000000);
    path_0.lineTo(0, size.height * 0.5000000);
    path_0.cubicTo(0, size.height * 0.4968000, 0, size.height * 0.5048000,
        size.width * 0.00002077286, size.height * 0.5026681);
    path_0.cubicTo(
        size.width * 0.00008811005,
        size.height * 0.4957597,
        size.width * 0.001680196,
        size.height * 0.4682208,
        size.width * 0.002396688,
        size.height * 0.4615708);
    path_0.cubicTo(
        size.width * 0.002617720,
        size.height * 0.4595194,
        size.width * 0.0003297307,
        size.height * 0.4788528,
        size.width * 0.001244926,
        size.height * 0.4711194);
    path_0.cubicTo(
        size.width * 0.005930635,
        size.height * 0.4315278,
        size.width * 0.01999651,
        size.height * 0.4027778,
        size.width * 0.03662042,
        size.height * 0.4027778);
    path_0.cubicTo(
        size.width * 0.04464069,
        size.height * 0.4027778,
        size.width * 0.05206550,
        size.height * 0.4094694,
        size.width * 0.05812857,
        size.height * 0.4208417);
    path_0.cubicTo(
        size.width * 0.05832487,
        size.height * 0.4212097,
        size.width * 0.05850847,
        size.height * 0.4216194,
        size.width * 0.05868095,
        size.height * 0.4220597);
    path_0.lineTo(size.width * 0.06187302, size.height * 0.4302125);
    path_0.cubicTo(
        size.width * 0.06194868,
        size.height * 0.4304069,
        size.width * 0.06206878,
        size.height * 0.4304181,
        size.width * 0.06214974,
        size.height * 0.4302361);
    path_0.lineTo(size.width * 0.06214974, size.height * 0.4302361);
    path_0.cubicTo(
        size.width * 0.06223016,
        size.height * 0.4300569,
        size.width * 0.06234974,
        size.height * 0.4300667,
        size.width * 0.06242540,
        size.height * 0.4302597);
    path_0.cubicTo(
        size.width * 0.06335820,
        size.height * 0.4326389,
        size.width * 0.06424339,
        size.height * 0.4351444,
        size.width * 0.06507672,
        size.height * 0.4377681);
    path_0.cubicTo(
        size.width * 0.06508519,
        size.height * 0.4377944,
        size.width * 0.06510106,
        size.height * 0.4377792,
        size.width * 0.06510106,
        size.height * 0.4377458);
    path_0.lineTo(size.width * 0.06510106, size.height * 0.4377458);
    path_0.cubicTo(
        size.width * 0.06510106,
        size.height * 0.4377111,
        size.width * 0.06511746,
        size.height * 0.4376972,
        size.width * 0.06512540,
        size.height * 0.4377222);
    path_0.cubicTo(
        size.width * 0.06733862,
        size.height * 0.4446792,
        size.width * 0.07032698,
        size.height * 0.4481639,
        size.width * 0.07336032,
        size.height * 0.4481958);
    path_0.cubicTo(
        size.width * 0.07491905,
        size.height * 0.4482125,
        size.width * 0.07598677,
        size.height * 0.4444861,
        size.width * 0.07598677,
        size.height * 0.4403944);
    path_0.lineTo(size.width * 0.07598677, size.height * 0.009615389);
    path_0.cubicTo(size.width * 0.07598677, size.height * 0.004304958,
        size.width * 0.07434709, 0, size.width * 0.07232381, 0);
    path_0.lineTo(size.width * 0.8730159, 0);
    path_0.cubicTo(size.width * 0.9328783, 0, size.width * 0.9628095, 0,
        size.width * 0.9814021, size.height * 0.04881556);
    path_0.cubicTo(size.width, size.height * 0.09763111, size.width,
        size.height * 0.1761986, size.width, size.height * 0.3333333);
    path_0.lineTo(size.width, size.height * 0.6666667);
    path_0.cubicTo(
        size.width,
        size.height * 0.8238014,
        size.width,
        size.height * 0.9023694,
        size.width * 0.9814021,
        size.height * 0.9511847);
    path_0.cubicTo(size.width * 0.9628095, size.height, size.width * 0.9328783,
        size.height, size.width * 0.8730159, size.height);
    path_0.lineTo(size.width * 0.07232381, size.height);
    path_0.cubicTo(
        size.width * 0.07434709,
        size.height,
        size.width * 0.07598677,
        size.height * 0.9956944,
        size.width * 0.07598677,
        size.height * 0.9903847);
    path_0.lineTo(size.width * 0.07598677, size.height * 0.5548083);
    path_0.cubicTo(
        size.width * 0.07598677,
        size.height * 0.5503972,
        size.width * 0.07468254,
        size.height * 0.5465667,
        size.width * 0.07303862,
        size.height * 0.5474819);
    path_0.cubicTo(
        size.width * 0.07037460,
        size.height * 0.5489625,
        size.width * 0.06768148,
        size.height * 0.5533986,
        size.width * 0.06535291,
        size.height * 0.5613847);
    path_0.cubicTo(
        size.width * 0.06535132,
        size.height * 0.5613889,
        size.width * 0.06534868,
        size.height * 0.5613861,
        size.width * 0.06534868,
        size.height * 0.5613806);
    path_0.lineTo(size.width * 0.06534868, size.height * 0.5613806);
    path_0.cubicTo(
        size.width * 0.06534868,
        size.height * 0.5613764,
        size.width * 0.06534656,
        size.height * 0.5613736,
        size.width * 0.06534497,
        size.height * 0.5613778);
    path_0.cubicTo(
        size.width * 0.05855397,
        size.height * 0.5832514,
        size.width * 0.04820868,
        size.height * 0.5972222,
        size.width * 0.03662042,
        size.height * 0.5972222);
    path_0.cubicTo(
        size.width * 0.01999651,
        size.height * 0.5972222,
        size.width * 0.005930635,
        size.height * 0.5684722,
        size.width * 0.001244926,
        size.height * 0.5288806);
    path_0.close();

    ///Background color without image
    Paint paint_0_fill = Paint()..style = PaintingStyle.fill;
    paint_0_fill.color = color;
    canvas.drawPath(path_0, paint_0_fill);

    ///Drawing image
    if(image!=null) {
      canvas.clipPath(path_0);

      paintImage(
        canvas: canvas,
        rect: Rect.fromLTWH(0, 0, size.width, size.height),
        image: image!,
        fit: BoxFit.cover,
      );
    }

  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }

}
Sang answered 15/5 at 15:9 Comment(1)
f****g weebs...Unveiling

© 2022 - 2024 — McMap. All rights reserved.