@@ -332,6 +332,10 @@ class RawEditorState extends EditorState
332
332
333
333
TextDirection get _textDirection => Directionality .of (context);
334
334
335
+ @override
336
+ bool get dirty => _dirty;
337
+ bool _dirty = false ;
338
+
335
339
@override
336
340
void insertContent (KeyboardInsertedContent content) {
337
341
assert (widget.contentInsertionConfiguration? .allowedMimeTypes
@@ -855,6 +859,7 @@ class RawEditorState extends EditorState
855
859
final currentSelection = controller.selection.copyWith ();
856
860
final attribute = value ? Attribute .checked : Attribute .unchecked;
857
861
862
+ _markNeedsBuild ();
858
863
controller
859
864
..ignoreFocusOnTextChange = true
860
865
..formatText (offset, 0 , attribute)
@@ -929,9 +934,11 @@ class RawEditorState extends EditorState
929
934
930
935
clearIndents = false ;
931
936
} else {
937
+ _dirty = false ;
932
938
throw StateError ('Unreachable.' );
933
939
}
934
940
}
941
+ _dirty = false ;
935
942
return result;
936
943
}
937
944
@@ -1170,6 +1177,17 @@ class RawEditorState extends EditorState
1170
1177
_selectionOverlay? .updateForScroll ();
1171
1178
}
1172
1179
1180
+ /// Marks the editor as dirty and trigger a rebuild.
1181
+ ///
1182
+ /// When the editor is dirty methods that depend on the editor
1183
+ /// state being in sync with the controller know they may be
1184
+ /// operating on stale data.
1185
+ void _markNeedsBuild () {
1186
+ setState (() {
1187
+ _dirty = true ;
1188
+ });
1189
+ }
1190
+
1173
1191
void _didChangeTextEditingValue ([bool ignoreFocus = false ]) {
1174
1192
if (kIsWeb) {
1175
1193
_onChangeTextEditingValue (ignoreFocus);
@@ -1184,10 +1202,9 @@ class RawEditorState extends EditorState
1184
1202
} else {
1185
1203
requestKeyboard ();
1186
1204
if (mounted) {
1187
- setState (() {
1188
- // Use controller.value in build()
1189
- // Trigger build and updateChildren
1190
- });
1205
+ // Use controller.value in build()
1206
+ // Mark widget as dirty and trigger build and updateChildren
1207
+ _markNeedsBuild ();
1191
1208
}
1192
1209
}
1193
1210
@@ -1222,10 +1239,9 @@ class RawEditorState extends EditorState
1222
1239
_updateOrDisposeSelectionOverlayIfNeeded ();
1223
1240
});
1224
1241
if (mounted) {
1225
- setState (() {
1226
- // Use controller.value in build()
1227
- // Trigger build and updateChildren
1228
- });
1242
+ // Use controller.value in build()
1243
+ // Mark widget as dirty and trigger build and updateChildren
1244
+ _markNeedsBuild ();
1229
1245
}
1230
1246
}
1231
1247
@@ -1258,6 +1274,11 @@ class RawEditorState extends EditorState
1258
1274
}
1259
1275
1260
1276
void _handleFocusChanged () {
1277
+ if (dirty) {
1278
+ SchedulerBinding .instance
1279
+ .addPostFrameCallback ((_) => _handleFocusChanged ());
1280
+ return ;
1281
+ }
1261
1282
openOrCloseConnection ();
1262
1283
_cursorCont.startOrStopCursorTimerIfNeeded (_hasFocus, controller.selection);
1263
1284
_updateOrDisposeSelectionOverlayIfNeeded ();
@@ -1272,10 +1293,9 @@ class RawEditorState extends EditorState
1272
1293
1273
1294
void _onChangedClipboardStatus () {
1274
1295
if (! mounted) return ;
1275
- setState (() {
1276
- // Inform the widget that the value of clipboardStatus has changed.
1277
- // Trigger build and updateChildren
1278
- });
1296
+ // Inform the widget that the value of clipboardStatus has changed.
1297
+ // Trigger build and updateChildren
1298
+ _markNeedsBuild ();
1279
1299
}
1280
1300
1281
1301
Future <LinkMenuAction > _linkActionPicker (Node linkNode) async {
0 commit comments