diff --git a/lib/src/code_editor.dart b/lib/src/code_editor.dart index f4b0a73..dca4a9f 100644 --- a/lib/src/code_editor.dart +++ b/lib/src/code_editor.dart @@ -460,11 +460,13 @@ class _CodeEditorState extends State { _editingController.bindEditor(_editorKey); } if (oldWidget.findController != widget.findController || oldWidget.controller != widget.controller) { + final CodeFindController oldFindController = _findController; if (oldWidget.findController == null) { - _findController.dispose(); + oldFindController.dispose(); + } else { + oldFindController.removeListener(_updateWidget); } _findController = widget.findController ?? CodeFindController(_editingController); - _findController.removeListener(_updateWidget); _findController.addListener(_updateWidget); } if (oldWidget.scrollController != widget.scrollController) { diff --git a/test/code_search_controller_test.dart b/test/code_search_controller_test.dart index 0e5eecb..ba6df0d 100644 --- a/test/code_search_controller_test.dart +++ b/test/code_search_controller_test.dart @@ -886,4 +886,101 @@ void main() { } }); }); + + group('CodeEditor ', () { + testWidgets('ignores notifications from a previous findController after swap', (tester) async { + final CodeLineEditingController editingController = CodeLineEditingController.fromText('abc'); + final CodeFindController oldFindController = CodeFindController(editingController); + final CodeFindController newFindController = CodeFindController(editingController); + int buildCount = 0; + CodeFindController? lastBuiltController; + + PreferredSizeWidget buildFindWidget( + BuildContext context, + CodeFindController controller, + bool readonly, + ) { + buildCount++; + lastBuiltController = controller; + return const PreferredSize( + preferredSize: Size.zero, + child: SizedBox.shrink(), + ); + } + + await tester.pumpWidget(MaterialApp( + home: CodeEditor( + controller: editingController, + findController: oldFindController, + findBuilder: buildFindWidget, + autofocus: false, + ), + )); + + expect(lastBuiltController, same(oldFindController)); + + await tester.pumpWidget(MaterialApp( + home: CodeEditor( + controller: editingController, + findController: newFindController, + findBuilder: buildFindWidget, + autofocus: false, + ), + )); + + expect(lastBuiltController, same(newFindController)); + final int buildCountAfterSwap = buildCount; + + oldFindController.findMode(); + await tester.pump(); + + expect(buildCount, buildCountAfterSwap); + expect(lastBuiltController, same(newFindController)); + + newFindController.findMode(); + await tester.pump(); + + expect(buildCount, greaterThan(buildCountAfterSwap)); + expect(lastBuiltController, same(newFindController)); + + await tester.pumpWidget(const MaterialApp(home: SizedBox.shrink())); + + oldFindController.dispose(); + newFindController.dispose(); + editingController.dispose(); + }); + + testWidgets('ignores stale findController notifications after the editor is removed', (tester) async { + final CodeLineEditingController editingController = CodeLineEditingController.fromText('abc'); + final CodeFindController oldFindController = CodeFindController(editingController); + final CodeFindController newFindController = CodeFindController(editingController); + + await tester.pumpWidget(MaterialApp( + home: CodeEditor( + controller: editingController, + findController: oldFindController, + autofocus: false, + ), + )); + + await tester.pumpWidget(MaterialApp( + home: CodeEditor( + controller: editingController, + findController: newFindController, + autofocus: false, + ), + )); + + await tester.pumpWidget(const MaterialApp(home: SizedBox.shrink())); + + oldFindController.findMode(); + await tester.pump(); + + expect(tester.takeException(), isNull); + + oldFindController.dispose(); + newFindController.dispose(); + editingController.dispose(); + }); + }); } \ No newline at end of file