Skip to content

Commit 19f999f

Browse files
authored
feat: optimize the performance (#442)
1 parent ad55171 commit 19f999f

File tree

80 files changed

+4639
-828
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+4639
-828
lines changed

codecov.yml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ignore:
2+
- "lib/src/flutter/"

example/lib/home_page.dart

+23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:async';
22
import 'dart:convert';
33
import 'dart:io';
4+
import 'dart:math';
45

56
import 'package:appflowy_editor/appflowy_editor.dart';
67
import 'package:example/pages/customize_theme_for_editor.dart';
@@ -51,6 +52,7 @@ class _HomePageState extends State<HomePage> {
5152
super.initState();
5253

5354
_jsonString = rootBundle.loadString('assets/example.json');
55+
5456
_widgetBuilder = (context) => Editor(
5557
jsonString: _jsonString,
5658
onEditorStateChange: (editorState) {
@@ -109,6 +111,20 @@ class _HomePageState extends State<HomePage> {
109111
final jsonString = rootBundle.loadString('assets/example.json');
110112
_loadEditor(context, jsonString);
111113
}),
114+
_buildListTile(context, 'With Large Document (10000+ lines)', () {
115+
final nodes = List.generate(
116+
10000,
117+
(index) =>
118+
paragraphNode(text: '$index ${generateRandomString(50)}'),
119+
);
120+
final editorState = EditorState(
121+
document: Document(root: pageNode(children: nodes)),
122+
);
123+
final jsonString = Future.value(
124+
jsonEncode(editorState.document.toJson()),
125+
);
126+
_loadEditor(context, jsonString);
127+
}),
112128
_buildListTile(context, 'With Example.html', () async {
113129
final htmlString =
114130
await rootBundle.loadString('assets/example.html');
@@ -315,3 +331,10 @@ class _HomePageState extends State<HomePage> {
315331
}
316332
}
317333
}
334+
335+
String generateRandomString(int len) {
336+
var r = Random();
337+
return String.fromCharCodes(
338+
List.generate(len, (index) => r.nextInt(33) + 89),
339+
);
340+
}

example/lib/main.dart

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1+
import 'package:appflowy_editor/appflowy_editor.dart';
12
import 'package:example/home_page.dart';
23
import 'package:flutter/material.dart';
3-
44
import 'package:flutter_localizations/flutter_localizations.dart';
55

6-
import 'package:appflowy_editor/appflowy_editor.dart';
7-
86
void main() {
97
runApp(const MyApp());
108
}

example/lib/pages/editor.dart

+12-8
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ class Editor extends StatelessWidget {
3737
onEditorStateChange(editorState);
3838
}
3939
});
40-
final scrollController = ScrollController();
40+
final editorScrollController = EditorScrollController(
41+
editorState: editorState,
42+
shrinkWrap: false,
43+
);
4144
if (PlatformExtension.isDesktopOrWeb) {
4245
return FloatingToolbar(
4346
items: [
@@ -54,11 +57,11 @@ class Editor extends StatelessWidget {
5457
...alignmentItems,
5558
],
5659
editorState: editorState,
57-
scrollController: scrollController,
60+
editorScrollController: editorScrollController,
5861
child: _buildDesktopEditor(
5962
context,
6063
editorState,
61-
scrollController,
64+
editorScrollController,
6265
),
6366
);
6467
} else if (PlatformExtension.isMobile) {
@@ -68,7 +71,7 @@ class Editor extends StatelessWidget {
6871
child: _buildMobileEditor(
6972
context,
7073
editorState,
71-
scrollController,
74+
editorScrollController,
7275
),
7376
),
7477
MobileToolbar(
@@ -99,19 +102,19 @@ class Editor extends StatelessWidget {
99102
Widget _buildMobileEditor(
100103
BuildContext context,
101104
EditorState editorState,
102-
ScrollController? scrollController,
105+
EditorScrollController? editorScrollController,
103106
) {
104107
return AppFlowyEditor(
105108
editorStyle: const EditorStyle.mobile(),
106109
editorState: editorState,
107-
scrollController: scrollController,
110+
editorScrollController: editorScrollController,
108111
);
109112
}
110113

111114
Widget _buildDesktopEditor(
112115
BuildContext context,
113116
EditorState editorState,
114-
ScrollController? scrollController,
117+
EditorScrollController? editorScrollController,
115118
) {
116119
final customBlockComponentBuilders = {
117120
...standardBlockComponentBuilderMap,
@@ -127,7 +130,8 @@ class Editor extends StatelessWidget {
127130
};
128131
return AppFlowyEditor(
129132
editorState: editorState,
130-
scrollController: scrollController,
133+
shrinkWrap: true,
134+
editorScrollController: editorScrollController,
131135
blockComponentBuilders: customBlockComponentBuilders,
132136
commandShortcutEvents: [
133137
customToggleHighlightCommand(

example/pubspec.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ dependencies:
4242
universal_html: ^2.0.8
4343
highlight: ^0.7.0
4444
http: ^0.13.5
45+
show_fps: ^1.0.6
4546

4647
dev_dependencies:
4748
flutter_test:

lib/src/core/document/node.dart

+24-10
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import 'dart:collection';
22

33
import 'package:appflowy_editor/appflowy_editor.dart';
44
import 'package:flutter/material.dart';
5-
import 'package:nanoid/nanoid.dart';
5+
import 'package:nanoid/non_secure.dart';
66

77
abstract class NodeExternalValues {
88
const NodeExternalValues();
@@ -39,8 +39,8 @@ final class Node extends ChangeNotifier with LinkedListEntry<Node> {
3939
),
4040
), // unlink the given children to avoid the error of "node has already a parent"
4141
_attributes = attributes,
42-
id = id ?? nanoid(10) {
43-
for (final child in this.children) {
42+
id = id ?? nanoid(6) {
43+
for (final child in children) {
4444
child.parent = this;
4545
}
4646
}
@@ -80,7 +80,12 @@ final class Node extends ChangeNotifier with LinkedListEntry<Node> {
8080

8181
/// The children of the node.
8282
final LinkedList<Node> _children;
83-
Iterable<Node> get children => _children.toList(growable: false);
83+
List<Node> get children {
84+
_cacheChildren ??= _children.toList(growable: false);
85+
return _cacheChildren!;
86+
}
87+
88+
List<Node>? _cacheChildren;
8489

8590
/// The attributes of the node.
8691
Attributes _attributes;
@@ -141,7 +146,9 @@ final class Node extends ChangeNotifier with LinkedListEntry<Node> {
141146

142147
entry.parent = this;
143148

144-
if (children.isEmpty) {
149+
_cacheChildren = null;
150+
151+
if (_children.isEmpty) {
145152
_children.add(entry);
146153
notifyListeners();
147154
return;
@@ -164,6 +171,8 @@ final class Node extends ChangeNotifier with LinkedListEntry<Node> {
164171
entry.parent = parent;
165172
super.insertAfter(entry);
166173

174+
parent?._cacheChildren = null;
175+
167176
// Notifies the new node.
168177
parent?.notifyListeners();
169178
}
@@ -173,6 +182,8 @@ final class Node extends ChangeNotifier with LinkedListEntry<Node> {
173182
entry.parent = parent;
174183
super.insertBefore(entry);
175184

185+
parent?._cacheChildren = null;
186+
176187
// Notifies the new node.
177188
parent?.notifyListeners();
178189
}
@@ -185,6 +196,8 @@ final class Node extends ChangeNotifier with LinkedListEntry<Node> {
185196
Log.editor.debug('delete Node $this from path $path');
186197
super.unlink();
187198

199+
parent?._cacheChildren = null;
200+
188201
parent?.notifyListeners();
189202
parent = null;
190203
}
@@ -230,12 +243,12 @@ final class Node extends ChangeNotifier with LinkedListEntry<Node> {
230243
}) {
231244
final node = Node(
232245
type: type ?? this.type,
233-
id: nanoid(10),
246+
id: nanoid(6),
234247
attributes: attributes ?? {...this.attributes},
235248
children: children ?? [],
236249
);
237-
if (children == null && this.children.isNotEmpty) {
238-
for (final child in this.children) {
250+
if (children == null && _children.isNotEmpty) {
251+
for (final child in _children) {
239252
node._children.add(
240253
child.copyWith()..parent = node,
241254
);
@@ -246,11 +259,12 @@ final class Node extends ChangeNotifier with LinkedListEntry<Node> {
246259
}
247260

248261
Path _computePath([Path previous = const []]) {
262+
final parent = this.parent;
249263
if (parent == null) {
250264
return previous;
251265
}
252-
final index = parent!.children.toList().indexOf(this);
253-
return parent!._computePath([index, ...previous]);
266+
final index = parent.children.indexOf(this);
267+
return parent._computePath([index, ...previous]);
254268
}
255269
}
256270

lib/src/core/document/path.dart

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'dart:math';
22

3+
import 'package:appflowy_editor/appflowy_editor.dart';
34
import 'package:flutter/foundation.dart';
45

56
typedef Path = List<int>;
@@ -105,4 +106,10 @@ extension PathExtensions on Path {
105106
}
106107
return true;
107108
}
109+
110+
bool inSelection(Selection? selection) {
111+
return selection != null &&
112+
selection.start.path <= this &&
113+
this <= selection.end.path;
114+
}
108115
}

0 commit comments

Comments
 (0)