Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Dynamically Size a CustomPainter

I need to render my custom object inside of a ListTile with custom painter in order to draw some custom text.

ListTile(
  title: CustomPaint(
    painter: RowPainter.name(
      _titleFontSelected,
      _titleFont,
      text,
      index,
      MediaQuery.of(context),
      currentRow,
    ),
  ),
);

Inside my RowPainter I draw the text with the font selected. When the row is too large, it automatically wraps and get drawn outside the given paint size.

void paint(Canvas canvas, Size size)

I like this behavior, but how can I resize the height of my paint area? Because this is a problem since this overlaps the next List row. I know that the CustomPaint has a property Size settable, but I know the text dimension only inside my paint function using the TextPainter getBoxesForSelection but it's too late.

How can I "resize" my row painter height dynamically if the text wraps?

like image 527
PsyKoWebMari Avatar asked Oct 15 '22 11:10

PsyKoWebMari


1 Answers

TL;DR

You cannot dynamically size a custom painter, however, your problem can be solved using a CustomPaint.
I will first elaborate on the dynamic sizing and then explain how to solve this problem using a constant size.

Dynamic size

This is essentially, where CustomPaint has its limits because it does not provide a way for you to size the painter based on the content.

The proper way of doing this is implementing your own RenderBox and overriding performLayout to size your render object based on the contents.
The RenderBox documentation is quite detailed on this, however, you might still find it difficult to get into it as it is quite different from building widgets.

Constant size

All of the above should not be needed in your case because you do not have a child for your custom paint.
You can simply supply the size parameter to your CustomPaint and calculate the required height in the parent widget.

You can use a LayoutBuilder to get the available width:

LayoutBuilder(
  builder: (context, constraints) {
    final maxWidth = constraints.maxWidth;
    ...
  }
)

Now, you can simply use a TextPainter to retrieve the required size before even entering your custom paint:

builder: (context, constraints) {
  ...
  final textPainter = TextPainter(
    text: TextSpan(
      text: 'Your text',
      style: yourTextStyle,
    ),
    textDirection: TextDirection.ltr,
  );
  textPainter.layout(maxWidth: maxWidth); // This will make the size available.

  return CustomPaint(
    size: textPainter.size,
    ...
  );
}

Now, you can even pass your textPainter to your custom painter directly instead of passing the style arguments.


Your logic might be a bit more complicated, however, the point is that you can calculate the size before creating the CustomPaint, which allows you to set the size.
If you need something more complicated, you will likely have to implement your own RenderBox.

like image 102
creativecreatorormaybenot Avatar answered Oct 19 '22 01:10

creativecreatorormaybenot