Repository: flutter
Issue: paragraph.layout() seems very expensive
Labels: framework,engine,c: performance,a: typography,P3,team-engine,triaged-engine

### My Requirements
- Show large amount of text which goes between 1MB to 2MB with custom rich text just like markdown
- This is just read only and no need for editing support
- Draw only onscreen paragraphs for better performance and keep the entire document model/text spans in memory
- I tried possible packages from pub.dev but probably none of them are meant for showing this much amount of text
- For drawing only onscreen paragraphs in my scrollview I need to know size/height of each paragraph and total document height which can only be done via either `textPainter.layout()` or `paragraph.layout()`

### My Problem
- My all mechanism works very well except calculating the size.
- I also took benchmarks for different methods for conformation. What I found is `textPainter.layout()` or `paragraph.layout()` is the only method which takes very long time for large amount of text. Even if document is calculated whole at a time or in small paragraph chunks it takes too long time.

### Minimal Reproducible Sample
<details><summary>Code</summary>
<p>

```dart
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({ Key? key }) : super(key: key);

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final TextEditingController _textEditingController1 = TextEditingController();
  final TextEditingController _textEditingController2 = TextEditingController();

  String _dummyText = '''This is sample Paragraph with some dummy text. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.''';
  int _numberOfTextPaintersToBeGenerated = 3500;

  String _logText = '';

  @override
  void initState() {
    _textEditingController1.text = _dummyText;
    _textEditingController2.text = _numberOfTextPaintersToBeGenerated.toString();
    super.initState();
  }

  void _calculateTextPainters() {
    _dummyText = _textEditingController1.text;
    _numberOfTextPaintersToBeGenerated = int.tryParse(_textEditingController2.text)??0;

    if (_numberOfTextPaintersToBeGenerated.toString() != _textEditingController2.text) {
      _logText = 'Please enter numeric input only';
      setState(() {});
      return;
    }

    FocusScope.of(context).requestFocus(new FocusNode());

    _logText = 'Calculating $_numberOfTextPaintersToBeGenerated textPainters\n';

    final DateTime dateTime = DateTime.now();

    for (int i = 0; i < _numberOfTextPaintersToBeGenerated; i++) {
      TextPainter textPainter = TextPainter(
        maxLines: null,
        textDirection: TextDirection.ltr,
        textAlign: TextAlign.justify,
        textScaleFactor: 1,
        text: TextSpan(
          text: _dummyText,
          style: TextStyle(
            fontSize: 20,
            fontWeight: FontWeight.normal,
            color: Colors.black,
          ),
        ),
      );
      textPainter.layout(minWidth: 0, maxWidth: 500);
    }

    _logText += 'All textPainters calculated\n';

    final Duration duration = DateTime.now().difference(dateTime);

    _logText += 'Time taken is ${duration.inMilliseconds} ms\n';

    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(title: Text("Demo")),
        body: Container(
          color: Colors.white,
          constraints: BoxConstraints.expand(),
          child: Column(
            children: [
              Expanded(
                flex: 2,
                child: Padding(
                  padding: EdgeInsets.all(4),
                  child: Container(
                    padding: EdgeInsets.all(4),
                    decoration: BoxDecoration(
                      color: Colors.white,
                      border: Border.all(color: Colors.black)
                    ),
                    constraints: BoxConstraints.expand(),
                    child: Scrollbar(
                      child: TextField(
                        controller: _textEditingController1,
                        maxLines: null,
                        style: TextStyle(
                          fontSize: 20
                        ),
                        decoration: InputDecoration(
                          isDense: true,
                          border: InputBorder.none,
                          labelText: 'Dummy text',
                          labelStyle: TextStyle(color: Colors.blue),
                          focusedBorder: InputBorder.none,
                          enabledBorder: InputBorder.none,
                          errorBorder: InputBorder.none,
                          disabledBorder: InputBorder.none,
                          contentPadding: EdgeInsets.all(5),
                        ),
                      ),
                    ),
                  ),
                ),
              ),
              SizedBox(height: 20,),
              Row(
                mainAxisSize: MainAxisSize.min,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Container(
                    width: 100,
                    height: 34,
                    child: TextField(
                      controller: _textEditingController2,
                      style: TextStyle(
                        fontSize: 20
                      ),
                    ),
                  ),
                  SizedBox(width: 50,),
                  ElevatedButton(
                    onPressed: _calculateTextPainters,
                    child: Text(
                      'Calculate TextPainters'
                    ),
                  ),
                ],
              ),
              SizedBox(height: 20,),
              Expanded(
                flex: 1,
                child: Padding(
                  padding: EdgeInsets.all(4),
                  child: Container(
                    padding: EdgeInsets.all(4),
                    decoration: BoxDecoration(
                      color: Colors.white,
                      border: Border.all(color: Colors.black)
                    ),
                    constraints: BoxConstraints.expand(),
                    child: Text(
                      _logText,
                      softWrap: true,
                      maxLines: null,
                      style: TextStyle(
                        fontSize: 20,
                        height: 1.4,
                      ),
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
```
</p>
</details>

- Enter number of paragraphs / `textpainters` to be generated and then click `Calculate TextPainters`
- If number is 2000 paragraphs with already available dummy text then approx total text size is 1.2 MB. If the number is 3500 then approx total text size will be 2 MB
- Below Text widget will show the time taken for calculating textPainters
- Just disable the below line like comment and check the difference
    `//textPainter.layout(minWidth: 0, maxWidth: 500);`
- If you need more details about my use case then let me know. I will post details and whole story here.

Regards
Sagar