简体   繁体   English

如何在flutter中获取Text Widget的大小

[英]How can I get the size of the Text Widget in flutter

I've painted a shape for the background of my content of Text .我已经为Text内容的背景绘制了一个形状。

I want the background autoscale the Text, even the softWrap being true.我希望背景自动缩放文本,即使是softWrap也是如此。

So, I need to get the width and height of my Text Widget before Widget build(BuildContext context) .所以,我需要在Widget build(BuildContext context)之前获取我的 Text Widget 的宽度和高度。

Actually, I am simulating the chat bubble effect like iOS message using flutter.实际上,我正在使用 flutter 模拟 iOS 消息等聊天气泡效果。 Here is the iOS version tutorial.这是iOS版教程。 Creating a Chat Bubble . 创建聊天气泡

The core code below:核心代码如下:

let label =  UILabel()
label.numberOfLines = 0
label.font = UIFont.systemFont(ofSize: 18)
label.textColor = .white
label.text = text

let constraintRect = CGSize(width: 0.66 * view.frame.width,
                            height: .greatestFiniteMagnitude)
let boundingBox = text.boundingRect(with: constraintRect,
                                    options: .usesLineFragmentOrigin,
                                    attributes: [.font: label.font],
                                    context: nil)
label.frame.size = CGSize(width: ceil(boundingBox.width),
                          height: ceil(boundingBox.height))

let bubbleSize = CGSize(width: label.frame.width + 28,
                             height: label.frame.height + 20)

let width = bubbleSize.width
let height = bubbleSize.height

========================================= ==========================================
SOLUTION解决方案
Here is my solution.这是我的解决方案。

bubble.dart:泡泡.dart:

// Define a CustomPainter to paint the bubble background.
class BubblePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Color(0xff188aff)
      ..style = PaintingStyle.fill;
    final Path bubble = Path()
      ..moveTo(size.width - 22.0, size.height)
      ..lineTo(17.0, size.height)
      ..cubicTo(
          7.61, size.height, 0.0, size.height - 7.61, 0.0, size.height - 17.0)
      ..lineTo(0.0, 17.0)
      ..cubicTo(0.0, 7.61, 7.61, 0.0, 17.0, 0.0)
      ..lineTo(size.width - 21, 0.0)
      ..cubicTo(size.width - 11.61, 0.0, size.width - 4.0, 7.61,
          size.width - 4.0, 17.0)
      ..lineTo(size.width - 4.0, size.height - 11.0)
      ..cubicTo(size.width - 4.0, size.height - 1.0, size.width, size.height,
          size.width, size.height)
      ..lineTo(size.width + 0.05, size.height - 0.01)
      ..cubicTo(size.width - 4.07, size.height + 0.43, size.width - 8.16,
          size.height - 1.06, size.width - 11.04, size.height - 4.04)
      ..cubicTo(size.width - 16.0, size.height, size.width - 19.0, size.height,
          size.width - 22.0, size.height)
      ..close();
    canvas.drawPath(bubble, paint);
  }

  @override
  bool shouldRepaint(BubblePainter oldPainter) => true;
}

// This is my custom RenderObject.
class BubbleMessage extends SingleChildRenderObjectWidget {
  BubbleMessage({
    Key key,
    this.painter,
    Widget child,
  }) : super(key: key, child: child);

  final CustomPainter painter;

  @override
  RenderCustomPaint createRenderObject(BuildContext context) {
    return RenderCustomPaint(
      painter: painter,
    );
  }

  @override
  void updateRenderObject(
      BuildContext context, RenderCustomPaint renderObject) {
    renderObject..painter = painter;
  }
}

Use the BubbleMessage Widget like this:像这样使用BubbleMessage小部件:

import 'bubble.dart' 

...code ... 

BubbleMessage(
  painter: BubblePainter(),
  child: Container(
    constraints: BoxConstraints(
      maxWidth: 250.0,
      minWidth: 50.0,
    ),
    padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 6.0),
    child: Text(
      'your text variable',
      softWrap: true,
      style: TextStyle(
        fontSize: 16.0,
      ),
    ),
  ),
),

...code ...

The bubble effect:气泡效果:

在此处输入图片说明

My apologies.我很抱歉。 This is not a direct answer on the topic's question!这不是对该主题问题的直接回答! But If someone needs to get the size of a Text widget — this method can help.但是如果有人需要获取 Text 小部件的大小 - 这种方法可以提供帮助。 It helped me in creation of custom menu widget.它帮助我创建了自定义菜单小部件。

class TextSized extends StatelessWidget {
  const TextSized({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final String text = "Text in one line";
    final TextStyle textStyle = TextStyle(
      fontSize: 30,
      color: Colors.white,
    );
    final Size txtSize = _textSize(text, textStyle);

    // This kind of use - meaningless. It's just an example.
    return Container(
      color: Colors.blueGrey,
      width: txtSize.width,
      height: txtSize.height,
      child: Text(
        text,
        style: textStyle,
        softWrap: false,
        overflow: TextOverflow.clip,
        maxLines: 1,
      ),
    );
  }

  // Here it is!
  Size _textSize(String text, TextStyle style) {
    final TextPainter textPainter = TextPainter(
        text: TextSpan(text: text, style: style), maxLines: 1, textDirection: TextDirection.ltr)
      ..layout(minWidth: 0, maxWidth: double.infinity);
    return textPainter.size;
  }
}

I found another method without using the context :我找到了另一种不使用context方法:

final constraints = BoxConstraints(
  maxWidth: 800.0, // maxwidth calculated
  minHeight: 0.0,
  minWidth: 0.0,
);

RenderParagraph renderParagraph = RenderParagraph(
  TextSpan(
    text: text,
    style: TextStyle(
      fontSize: fontSize,
    ),
  ),
  textDirection: ui.TextDirection.ltr,
  maxLines: 1,
);
renderParagraph.layout(constraints);
double textlen = renderParagraph.getMinIntrinsicWidth(fontSize).ceilToDouble();

Problem with other answers is that if you use Text widget to display your text and constraint it with measurements result without considering default font family and scale factor, then you will get wrong results because Text widget is using device's textScaleFactor by default and passing it to RichText widget inside of it.其他答案的问题是,如果您使用Text小部件显示您的文本并使用测量结果对其进行约束而不考虑默认字体系列和比例因子,那么您将得到错误的结果,因为Text小部件默认使用设备的textScaleFactor并将其传递给RichText它里面的小部件。 This is the correct code to measure text size:这是测量文本大小的正确代码:

final Size size = (TextPainter(
        text: TextSpan(text: text, style: textStyle),
        maxLines: 1,
        textScaleFactor: MediaQuery.of(context).textScaleFactor,
        textDirection: TextDirection.ltr)
      ..layout())
    .size;

A simple example:一个简单的例子:

For how it works see inline comments.有关它的工作原理,请参阅内联注释。

Inspiration from https://github.com/flutter/flutter/issues/23247灵感来自https://github.com/flutter/flutter/issues/23247

在此处输入图片说明

import 'package:flutter/material.dart';

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

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

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const String loremIpsum =
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod '
      'tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim '
      'veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea '
      'commodo consequat. Duis aute irure dolor in reprehenderit in voluptate '
      'velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint '
      'occaecat cupidatat non proident, sunt in culpa qui officia deserunt '
      'mollit anim id est laborum.';

  @override
  Widget build(BuildContext context) {
    final mq = MediaQuery.of(context);
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            SizedBox(
              height: mq.size.height,
              width: 240.0,
              child: ListView(
                padding: EdgeInsets.all(4.0),
                children: <Widget>[
                  Container(
                    decoration: BoxDecoration(
                      border: Border.all(color: Colors.orange),
                    ),
                    child: Bubble(
                      text: TextSpan(
                        text: loremIpsum,
                        style: Theme.of(context).textTheme.body1,
                      ),
                    ),
                  ),
                  Container(
                    decoration: BoxDecoration(
                      border: Border.all(color: Colors.orange, width: 2.0),
                    ),
                    padding: EdgeInsets.symmetric(horizontal: 2.0),
                    child: Bubble(
                      text: TextSpan(
                        text: loremIpsum,
                        style: Theme.of(context).textTheme.body1,
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class Bubble extends StatefulWidget {
  Bubble({@required this.text});

  final TextSpan text;

  @override
  _BubbleState createState() => new _BubbleState();
}

class _BubbleState extends State<Bubble> {
  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (context, constraints) {
      // The text to render
      final textWidget = Text.rich(widget.text);

      // Calculate the left, top, bottom position of the end of the last text
      // line.
      final lastBox = _calcLastLineEnd(context, constraints);

      // Calculate whether the timestamp fits into the last line or if it has
      // to be positioned after the last line.
      final fitsLastLine =
          constraints.maxWidth - lastBox.right > Timestamp.size.width + 10.0;

      return Stack(
        children: [
          // Ensure the stack is big enough to render the text and the
          // timestamp.
          SizedBox.fromSize(
              size: Size(
                constraints.maxWidth,
                (fitsLastLine ? lastBox.top : lastBox.bottom) +
                    10.0 +
                    Timestamp.size.height,
              ),
              child: Container()),
          // Render the text.
          textWidget,
          // Render the timestamp.
          Positioned(
            left: constraints.maxWidth - (Timestamp.size.width + 10.0),
            top: (fitsLastLine ? lastBox.top : lastBox.bottom) + 5.0,
            child: Timestamp(DateTime.now()),
          ),
        ],
      );
    });
  }

  // Calculate the left, top, bottom position of the end of the last text
  // line.
  TextBox _calcLastLineEnd(BuildContext context, BoxConstraints constraints) {
    final richTextWidget = Text.rich(widget.text).build(context) as RichText;
    final renderObject = richTextWidget.createRenderObject(context);
    renderObject.layout(constraints);
    final lastBox = renderObject
        .getBoxesForSelection(TextSelection(
            baseOffset: 0, extentOffset: widget.text.toPlainText().length))
        .last;
    return lastBox;
  }
}

class Timestamp extends StatelessWidget {
  Timestamp(this.timestamp);

  final DateTime timestamp;

  /// This size could be calculated similarly to the way the text size in
  /// [Bubble] is calculated instead of using magic values.
  static final Size size = Size(60.0, 25.0);

  @override
  Widget build(BuildContext context) => Container(
        padding: EdgeInsets.all(3.0),
        decoration: BoxDecoration(
          color: Colors.greenAccent,
          border: Border.all(color: Colors.yellow),
        ),
        child:
            Text('${timestamp.hour}:${timestamp.minute}:${timestamp.second}'),
      );
}

在此处输入图片说明

from inspiring of the Günter Zöchbauer来自 Günter Zöchbauer 的启发

List<bool> _calcLastLineEnd(String msg) {
  // self-defined constraint
  final constraints = BoxConstraints(
    maxWidth: 800.0, // maxwidth calculated
    minHeight: 30.0,
    minWidth: 80.0,
  );
  final richTextWidget =
      Text.rich(TextSpan(text: msg)).build(context) as RichText;
  final renderObject = richTextWidget.createRenderObject(context);
  renderObject.layout(constraints);
  final boxes = renderObject.getBoxesForSelection(TextSelection(
      baseOffset: 0, extentOffset: TextSpan(text: msg).toPlainText().length));
  bool needPadding = false, needNextline = false;
  if (boxes.length < 2 && boxes.last.right < 630) needPadding = true;
  if (boxes.length < 2 && boxes.last.right > 630) needNextline = true;
  if (boxes.length > 1 && boxes.last.right > 630) needNextline = true;
  return [needPadding, needNextline];
}

Multiline text height (modified variant of Dmitry_Kovalov)多行文本高度(Dmitry_Kovalov 的修改变体)

import 'package:flutter/cupertino.dart';

extension StringExtension on String {
  double textHeight(TextStyle style, double textWidth) {
    final TextPainter textPainter = TextPainter(
      text: TextSpan(text: this, style: style),
      textDirection: TextDirection.ltr,
      maxLines: 1,
    )..layout(minWidth: 0, maxWidth: double.infinity);

    final countLines = (textPainter.size.width / textWidth).ceil();
    final height = countLines * textPainter.size.height;
    return height;
  }
}

Modified variant of Günter Zöchbauer solution wich can use text overflow可以使用文本溢出的 Günter Zöchbauer 解决方案的修改变体

TextBox textBox(BuildContext context, String text, TextStyle textStyle,
    int maxLines, TextOverflow overflow, BoxConstraints constraints) {
  final textSpan = TextSpan(
    text: text,
    style: textStyle,
  );
  final richTextWidget = Text.rich(
    textSpan,
    maxLines: maxLines,
    overflow: overflow,
  ).build(context) as RichText;
  final renderObject = richTextWidget.createRenderObject(context);
  renderObject.layout(constraints);
  final boxesForSelection = renderObject.getBoxesForSelection(TextSelection(
      baseOffset: 0, extentOffset: richTextWidget.text.toPlainText().length));
  if (boxesForSelection.length == 0)
    return TextBox.fromLTRBD(0.0, 0.0, 0.0, 0.0, TextDirection.ltr);
  final List<double> widths = List();
  boxesForSelection.forEach((box) {
    widths.add(box.right);
  });
  widths.sort((a, b) => a.compareTo(b));
  return TextBox.fromLTRBD(
      0.0, 0.0, widths.last, boxesForSelection.last.bottom, TextDirection.ltr);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM