-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Open
Labels
P3Issues that are less important to the Flutter projectIssues that are less important to the Flutter projecta: typographyText rendering, possibly libtxtText rendering, possibly libtxtc: performanceRelates to speed or footprint issues (see "perf:" labels)Relates to speed or footprint issues (see "perf:" labels)engineflutter/engine related. See also e: labels.flutter/engine related. See also e: labels.frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.team-engineOwned by Engine teamOwned by Engine teamtriaged-engineTriaged by Engine teamTriaged by Engine team
Description
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()orparagraph.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()orparagraph.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
Code
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,
),
),
),
),
),
],
),
),
),
);
}
}- Enter number of paragraphs /
textpaintersto be generated and then clickCalculate 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
pawangjain, maxim-saplin, alexvoina, gottfired, s0nerik and 17 morefarshed
Metadata
Metadata
Assignees
Labels
P3Issues that are less important to the Flutter projectIssues that are less important to the Flutter projecta: typographyText rendering, possibly libtxtText rendering, possibly libtxtc: performanceRelates to speed or footprint issues (see "perf:" labels)Relates to speed or footprint issues (see "perf:" labels)engineflutter/engine related. See also e: labels.flutter/engine related. See also e: labels.frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.team-engineOwned by Engine teamOwned by Engine teamtriaged-engineTriaged by Engine teamTriaged by Engine team