diff --git a/super_editor/lib/src/default_editor/super_editor.dart b/super_editor/lib/src/default_editor/super_editor.dart index 0ee7952f4..3e6adb985 100644 --- a/super_editor/lib/src/default_editor/super_editor.dart +++ b/super_editor/lib/src/default_editor/super_editor.dart @@ -534,6 +534,10 @@ class SuperEditorState extends State { } } + for (final plugin in widget.plugins) { + plugin.detach(widget.editor); + } + _iosControlsController.dispose(); _androidControlsController.dispose(); diff --git a/super_editor/test/super_editor/supereditor_plugin_test.dart b/super_editor/test/super_editor/supereditor_plugin_test.dart new file mode 100644 index 000000000..23fff53cc --- /dev/null +++ b/super_editor/test/super_editor/supereditor_plugin_test.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_test_runners/flutter_test_runners.dart'; +import 'package:super_editor/super_editor.dart'; + +import 'supereditor_test_tools.dart'; + +void main() { + group('SuperEditor > plugins >', () { + testWidgetsOnAllPlatforms('are detached when the editor is disposed', (tester) async { + final plugin = _FakePlugin(); + + await tester // + .createDocument() + .withSingleParagraph() + .withPlugin(plugin) + .pump(); + + // Ensure the plugin was not attached initially. + expect(plugin.wasDetached, isFalse); + + // Pump another widget tree to dispose SuperEditor. + await tester.pumpWidget(Container()); + + // Ensure the plugin was detached. + expect(plugin.wasDetached, isTrue); + }); + }); +} + +/// A plugin that tracks whether it was detached. +class _FakePlugin extends SuperEditorPlugin { + bool get wasDetached => _wasDetached; + bool _wasDetached = false; + + @override + void detach(Editor editor) { + _wasDetached = true; + } +}