ListTile中的Flutter CustomPainter动态大小

时间:2019-09-18 09:19:16

标签: flutter paint flutter-layout

我需要使用自定义绘画工具在ListTile中渲染我的自定义对象,以便绘制一些自定义文本。

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

在我的RowPainter内部,用选定的字体绘制文本。 当行太大时,它会自动换行并在给定的绘画尺寸之外进行绘制。

  void paint(Canvas canvas, Size size) {

我喜欢这种行为,但是如何调整绘画区域的高度呢?因为这是一个问题,因为它与下一个列表行重叠。 我知道CustomPaint具有可设置的属性Size,但我只知道使用TextPainter getBoxesForSelection的绘画函数内部的文本尺寸,但为时已晚。

如果文本自动换行,如何动态“调整”行高?

3 个答案:

答案 0 :(得分:2)

TL; DR

您无法动态调整自定义绘画工具的大小,但是,您的问题 可以使用CustomPaint来解决。
我将首先详细介绍动态大小调整,然后说明如何使用固定大小解决此问题。

动态尺寸

这实际上是CustomPaint有其限制的地方,因为它没有为您提供一种根据内容调整画家大小的方法。

执行此操作的正确方法是实现自己的RenderBox并重写performLayout以根据内容调整渲染对象的大小。
RenderBox documentation对此非常详细,但是,由于它与构建窗口小部件完全不同,因此您可能仍然难以进入。

恒定尺寸

您不需要上述所有 ,因为您的自定义绘画没有child
您只需将size parameter提供给CustomPaint,然后在父窗口小部件中计算所需的高度。

您可以使用LayoutBuilder来获取可用宽度:

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

现在,您甚至可以在输入自定义绘画之前使用TextPainter来检索所需的尺寸:

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,
    ...
  );
}

现在,您甚至可以直接将textPainter传递给自定义绘画工具,而不必传递样式参数。


您的逻辑可能会更复杂一些,但是要点是,您可以在创建CustomPaint之前计算大小,这可以设置大小。
如果您需要更复杂的内容,则可能必须实现自己的RenderBox

答案 1 :(得分:0)

我还没有测试过,但这可能有用:

首先,将CustomPaint包装到有状态的小部件(例如DynamicCustomPaint)中,以动态地操作您的小部件。

您为CustomPainter提供了一个功能onResize,当您知道它时,它将为您提供新的画布大小。

一旦知道画布的确切尺寸,就可以调用此函数。例如,使用this technique时,您无需绘制文本即可知道其大小。

将调用onResize函数时,您将获得画布的新大小,并在setState状态下调用DynamicCustomPaint

这可能看起来像这样:

class DynamicCustomPaint extends StatefulWidget {
  @override
  _DynamicCustomPaintState createState() => _DynamicCustomPaintState();
}

class _DynamicCustomPaintState extends State<DynamicCustomPaint> {
  Size canvasSize;

  @override
  Widget build(BuildContext context) {
    // Set inital size, maybe move this to initState function
    if (canvasSize == null) {
      // Decide what makes sense in your use-case as inital size
      canvasSize = MediaQuery.of(context).size;
    }
    return CustomPaint(
      size: canvasSize,
      painter: RowPainter.name(_titleFontSelected, _titleFont, text, index, currentRow, onResize: (size) {
        setState(() {
          canvasSize = size;
        });
      }),
    );
  }
}

typedef OnResize = void Function(Size size);

class RowPainter extends CustomPainter {
  RowPainter.name(
    this._titleFontSelected,
    this._titleFont,
    this.text,
    this.index,
    this.currentRow,
    { this.onResize },
  );

  final FontStyle _titleFontSelected;
  final FontStyle _titleFont;
  final String text;
  final int index;
  final int currentRow;
  final OnResize onResize;

  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint

    // call onResize somewhere in here
    // onResize(newSize);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

答案 2 :(得分:0)

改用 SingleChildRenderObjectWidgetRenderBox。带有动态调整大小的完整简单示例。

DartPad

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

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      body: Center(
        child: Column(
          children: [
            SizedBox(height: 100,),
            Text('I am above'),
            MyWidget(),
            Text('I am below')
          ],
        ),
      ),
    ),
  ));
}

class MyWidget extends SingleChildRenderObjectWidget {
  @override
  MyRenderBox createRenderObject(BuildContext context) {
    return MyRenderBox();
  }
}

class MyRenderBox extends RenderBox {
  double myHeight = 200;

  @override
  void paint(PaintingContext context, Offset offset) {
    Paint paint = Paint()
      ..color = Colors.black..style = PaintingStyle.fill;
    context.canvas.drawRect(
        Rect.fromLTRB(offset.dx, offset.dy,
          offset.dx + size.width, offset.dy + size.height,), paint);
  }

  @override
  void performLayout() {
    size = Size(
      constraints.constrainWidth(200),
      constraints.constrainHeight(myHeight),
    );
  }

  // Timer just an example to show dynamic behavior
  MyRenderBox(){
    Timer.periodic(Duration(seconds: 2), handleTimeout);
  }
  void handleTimeout(timer) {
    myHeight += 40;
    markNeedsLayoutForSizedByParentChange();
    layout(constraints);
  }
}

CustomPainter 只会调整到它的孩子的大小或传递给构造函数的初始值。 Documentation

<块引用>

定制画家通常会根据他们的孩子调整自己的尺寸。如果他们没有孩子,他们会尝试调整自己的大小,默认为 Size.zero。大小不能为空。

RenderBox 基础