Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix composer memory leak #33

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 102 additions & 82 deletions lib/src/widgets/html_editor_widget_web.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export 'dart:html';

import 'dart:async';
import 'dart:convert';
import 'dart:html';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -63,6 +65,10 @@ class _HtmlEditorWidgetWebState extends State<HtmlEditorWidget> {

final _jsonEncoder = const JsonEncoder();

late StreamSubscription<MessageEvent> _editorJSListener;
late StreamSubscription<MessageEvent> _summernoteOnLoadListener;
static const String _summernoteLoadedMessage = '_HtmlEditorWidgetWebState::summernoteLoaded';

@override
void initState() {
actualHeight = widget.otherOptions.height;
Expand Down Expand Up @@ -553,6 +559,14 @@ class _HtmlEditorWidgetWebState extends State<HtmlEditorWidget> {
}

$jsCallbacks

function iframeLoaded(event) {
window.parent.postMessage(JSON.stringify({"view": "$createdViewId", "message": "$_summernoteLoadedMessage"}), "*");
}
window.addEventListener('load', iframeLoaded, false);
window.addEventListener('beforeunload', (event) => {
window.parent.removeEventListener('message', handleMessage, false);
});
</script>
""";
var filePath =
Expand Down Expand Up @@ -592,78 +606,7 @@ class _HtmlEditorWidgetWebState extends State<HtmlEditorWidget> {
..style.border = 'none'
..style.overflow = 'hidden'
..style.width = '100%'
..style.height = '100%'
..onLoad.listen((event) async {
if (widget.htmlEditorOptions.disabled && !alreadyDisabled) {
widget.controller.disable();
alreadyDisabled = true;
}
if (widget.callbacks != null && widget.callbacks!.onInit != null) {
widget.callbacks!.onInit!.call();
}
if (widget.htmlEditorOptions.initialText != null) {
widget.controller.setText(widget.htmlEditorOptions.initialText!);
}
var data = <String, Object>{'type': 'toIframe: getHeight'};
data['view'] = createdViewId;
var data2 = <String, Object>{'type': 'toIframe: setInputType'};
data2['view'] = createdViewId;
var jsonStr = _jsonEncoder.convert(data);
var jsonStr2 = _jsonEncoder.convert(data2);
html.window.onMessage.listen((event) {
var data = json.decode(event.data);
if (data['type'] != null &&
data['type'].contains('toDart: htmlHeight') &&
data['view'] == createdViewId &&
widget.htmlEditorOptions.autoAdjustHeight) {
final docHeight = data['height'] ?? actualHeight;
if ((docHeight != null && docHeight != actualHeight) &&
mounted &&
docHeight > 0) {
setState(mounted, this.setState, () {
actualHeight =
docHeight + (toolbarKey.currentContext?.size?.height ?? 0);
});
}
}
if (data['type'] != null &&
data['type'].contains('toDart: onChangeContent') &&
data['view'] == createdViewId) {
if (widget.callbacks != null &&
widget.callbacks!.onChangeContent != null) {
widget.callbacks!.onChangeContent!.call(data['contents']);
}

if (mounted) {
final scrollableState = Scrollable.maybeOf(context);
if (widget.htmlEditorOptions.shouldEnsureVisible &&
scrollableState != null) {
scrollableState.position.ensureVisible(
context.findRenderObject()!,
duration: const Duration(milliseconds: 100),
curve: Curves.easeIn);
}
}
}
if (data['type'] != null &&
data['type'].contains('toDart: updateToolbar') &&
data['view'] == createdViewId) {
if (widget.controller.toolbar != null) {
widget.controller.toolbar!.updateToolbar(data);
}
}
});
html.window.postMessage(jsonStr, '*');
html.window.postMessage(jsonStr2, '*');

if (widget.otherOptions.dropZoneHeight != null ||
widget.otherOptions.dropZoneWidth != null) {
_setDimensionDropZoneView(
height: widget.otherOptions.dropZoneHeight,
width: widget.otherOptions.dropZoneWidth
);
}
});
..style.height = '100%';
ui.platformViewRegistry
.registerViewFactory(createdViewId, (int viewId) => iframe);
setState(mounted, this.setState, () {
Expand All @@ -679,8 +622,8 @@ class _HtmlEditorWidgetWebState extends State<HtmlEditorWidget> {
: widget.otherOptions.height,
child: Column(
children: <Widget>[
widget.htmlToolbarOptions.toolbarPosition ==
ToolbarPosition.aboveEditor
widget.htmlToolbarOptions.toolbarPosition == ToolbarPosition.aboveEditor
&& widget.htmlToolbarOptions.toolbarType != ToolbarType.hide
? ToolbarWidget(
key: toolbarKey,
controller: widget.controller,
Expand All @@ -704,8 +647,8 @@ class _HtmlEditorWidgetWebState extends State<HtmlEditorWidget> {
: widget.otherOptions.height);
}
}))),
widget.htmlToolbarOptions.toolbarPosition ==
ToolbarPosition.belowEditor
widget.htmlToolbarOptions.toolbarPosition == ToolbarPosition.belowEditor
&& widget.htmlToolbarOptions.toolbarType != ToolbarType.hide
? ToolbarWidget(
key: toolbarKey,
controller: widget.controller,
Expand Down Expand Up @@ -861,11 +804,12 @@ class _HtmlEditorWidgetWebState extends State<HtmlEditorWidget> {

/// Adds an event listener to check when a callback is fired
void addJSListener(Callbacks c) {
html.window.onMessage.listen((event) {
_editorJSListener = html.window.onMessage.listen((event) {
var data = json.decode(event.data);
if (data['type'] != null &&
data['type'].contains('toDart:') &&
data['view'] == createdViewId) {

if (data['view'] != createdViewId) return;

if (data['type'] != null && data['type'].contains('toDart:')) {
if (data['type'].contains('onBeforeCommand')) {
c.onBeforeCommand!.call(data['contents']);
}
Expand Down Expand Up @@ -951,8 +895,77 @@ class _HtmlEditorWidgetWebState extends State<HtmlEditorWidget> {
if (data['type'].contains('onTextFontSizeChanged')) {
c.onTextFontSizeChanged!.call(data['size']);
}
if (data['type'].contains('onEditLink')) {
c.onEditLink!.call(data['urlDisplayText'], data['url'], data['isOpenInNewTab'], data['linkTagId']);
}

if (data['message'] == _summernoteLoadedMessage) {
if (widget.htmlEditorOptions.disabled && !alreadyDisabled) {
widget.controller.disable();
alreadyDisabled = true;
}
if (widget.callbacks != null && widget.callbacks!.onInit != null) {
widget.callbacks!.onInit!.call();
}
if (widget.htmlEditorOptions.initialText != null) {
widget.controller.setText(widget.htmlEditorOptions.initialText!);
}
var data = <String, Object>{'type': 'toIframe: getHeight'};
data['view'] = createdViewId;
var data2 = <String, Object>{'type': 'toIframe: setInputType'};
data2['view'] = createdViewId;
var jsonStr = _jsonEncoder.convert(data);
var jsonStr2 = _jsonEncoder.convert(data2);
_summernoteOnLoadListener = html.window.onMessage.listen((event) {
var data = json.decode(event.data);
if (data['type'] != null &&
data['type'].contains('toDart: htmlHeight') &&
data['view'] == createdViewId &&
widget.htmlEditorOptions.autoAdjustHeight) {
final docHeight = data['height'] ?? actualHeight;
if ((docHeight != null && docHeight != actualHeight) &&
mounted &&
docHeight > 0) {
setState(mounted, this.setState, () {
actualHeight =
docHeight + (toolbarKey.currentContext?.size?.height ?? 0);
});
}
}
if (data['type'] != null &&
data['type'].contains('toDart: onChangeContent') &&
data['view'] == createdViewId) {
if (widget.callbacks != null &&
widget.callbacks!.onChangeContent != null) {
widget.callbacks!.onChangeContent!.call(data['contents']);
}

if (mounted) {
final scrollableState = Scrollable.maybeOf(context);
if (widget.htmlEditorOptions.shouldEnsureVisible &&
scrollableState != null) {
scrollableState.position.ensureVisible(
context.findRenderObject()!,
duration: const Duration(milliseconds: 100),
curve: Curves.easeIn);
}
}
}
if (data['type'] != null &&
data['type'].contains('toDart: updateToolbar') &&
data['view'] == createdViewId) {
if (widget.controller.toolbar != null) {
widget.controller.toolbar!.updateToolbar(data);
}
}
});
html.window.postMessage(jsonStr, '*');
html.window.postMessage(jsonStr2, '*');

if (widget.otherOptions.dropZoneHeight != null ||
widget.otherOptions.dropZoneWidth != null) {
_setDimensionDropZoneView(
height: widget.otherOptions.dropZoneHeight,
width: widget.otherOptions.dropZoneWidth
);
}
}
});
Expand All @@ -972,4 +985,11 @@ class _HtmlEditorWidgetWebState extends State<HtmlEditorWidget> {
final jsonDimension = _jsonEncoder.convert(dataDimension);
html.window.postMessage(jsonDimension, '*');
}

@override
void dispose() {
_editorJSListener.cancel();
_summernoteOnLoadListener.cancel();
super.dispose();
}
}
Loading