title: Finding the exact bounds of text
body: <p>I need to know the exact bounds a piece of text -- the equivalent of <a href="https://developer.android.com/reference/android/graphics/Paint.html" rel="noreferrer">getTextBounds</a> for Android. I realize this goes somewhat counter to Flutter's design, but I am using text in a non-traditional way (as if the text were, say, embedded into an artistic picture at a precise location and size).</p>
  
  <p>I've tried three methods:</p>
  
  <ul>
  <li><p><code>TextPainter</code>'s <code>minIntrinsicWidth</code> and <code>height</code>. Source below. This produces a bounding box with space on all sides:</p>
  
  <p><a href="https://i.stack.imgur.com/jYSn3.png" rel="noreferrer"><img src="https://i.stack.imgur.com/jYSn3.png" alt="enter image description here"></a></p>
  
  <p>I need the sides of that rectangle to be right up against the black pixels of the '8'. (In this particular case, <code>width</code> and <code>maxIntrinsicWidth</code> give the same value as <code>minIntrinsicWidth</code>; also, <code>preferredLineHeight</code> gives the same value as <code>height</code>.)</p></li>
  <li><p><code>Paragraph</code>'s <code>getBoxesForRange</code> -- basically the same result as with TextPainter.</p></li>
  <li><p><code>FittedBox</code> -- also leaves spaces, no matter the <code>BoxFit</code> enum value. I used <a href="https://stackoverflow.com/a/51680076/10234868">this answer</a> as a starting point.</p></li>
  </ul>
  
  <p><code>TextPainter</code> code follows. (I realize this is inefficient; I don't care until I get the needed bounding box. I used Scene/Picture/etc in hopes of finding appropriate functionality in the lower levels.)</p>
  
  <pre><code>import 'dart:ui';
  import 'dart:typed_data';
  import 'package:flutter/painting.dart';
  
  const black = Color(0xff000000);
  const white = Color(0xffffffff);
  
  final identityTransform = new Float64List(16)
    ..[0] = 1.0
    ..[5] = 1.0
    ..[10] = 1.0
    ..[15] = 1.0;
  
  TextPainter createTextPainter() {
    return new TextPainter(
      text: new TextSpan(
        text: '8',
        style: new TextStyle(
          color: black,
          fontSize: 200.0,
          fontFamily: "Roboto",
        ),
      ),
      textDirection: TextDirection.ltr,
    )..layout();
  }
  
  void drawBackground(Canvas canvas, Rect screen) {
    canvas.drawRect(
        screen,
        new Paint()
          ..color = white
          ..style = PaintingStyle.fill);
  }
  
  Picture createPicture() {
    final recorder = new PictureRecorder();
    final screen = Offset.zero &amp; window.physicalSize;
    final canvas = new Canvas(recorder, screen);
    final offset = screen.center;
    final painter = createTextPainter();
    final bounds = offset &amp; Size(painter.minIntrinsicWidth, painter.height);
    drawBackground(canvas, screen);
    painter.paint(canvas, offset);
    canvas.drawRect(
        bounds,
        new Paint()
          ..color = black
          ..style = PaintingStyle.stroke
          ..strokeWidth = 3.0);
    return recorder.endRecording();
  }
  
  Scene createScene() {
    final builder = new SceneBuilder()
      ..pushTransform(identityTransform)
      ..addPicture(Offset.zero, createPicture())
      ..pop();
    return builder.build();
  }
  
  void beginFrame(Duration timeStamp) {
    window.render(createScene());
  }
  
  void main() {
    window.onBeginFrame = beginFrame;
    window.scheduleFrame();
  }
  </code></pre>