feat: working on editor state
This commit is contained in:
parent
3e8ade5b18
commit
066b2019f2
@ -10,20 +10,11 @@ PODS:
|
||||
- image_picker (0.0.1):
|
||||
- Flutter
|
||||
- OrderedSet (5.0.0)
|
||||
- video_player (0.0.1):
|
||||
- Flutter
|
||||
- wakelock (0.0.1):
|
||||
- Flutter
|
||||
- webview_flutter (0.0.1):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
|
||||
- image_picker (from `.symlinks/plugins/image_picker/ios`)
|
||||
- video_player (from `.symlinks/plugins/video_player/ios`)
|
||||
- wakelock (from `.symlinks/plugins/wakelock/ios`)
|
||||
- webview_flutter (from `.symlinks/plugins/webview_flutter/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
@ -36,21 +27,12 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flutter_inappwebview/ios"
|
||||
image_picker:
|
||||
:path: ".symlinks/plugins/image_picker/ios"
|
||||
video_player:
|
||||
:path: ".symlinks/plugins/video_player/ios"
|
||||
wakelock:
|
||||
:path: ".symlinks/plugins/wakelock/ios"
|
||||
webview_flutter:
|
||||
:path: ".symlinks/plugins/webview_flutter/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
|
||||
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
|
||||
image_picker: 50e7c7ff960e5f58faa4d1f4af84a771c671bc4a
|
||||
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
||||
video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e
|
||||
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
|
||||
webview_flutter: 9f491a9b5a66f2573946a389b2677987b0ff8c0b
|
||||
|
||||
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
|
||||
|
||||
|
3
lib/src/models/callbacks/did_html_change_listener.dart
Normal file
3
lib/src/models/callbacks/did_html_change_listener.dart
Normal file
@ -0,0 +1,3 @@
|
||||
class DidHtmlChangeListener {
|
||||
didHtmlChange(bool didHtmlChange) {}
|
||||
}
|
3
lib/src/models/callbacks/get_current_html_callback.dart
Normal file
3
lib/src/models/callbacks/get_current_html_callback.dart
Normal file
@ -0,0 +1,3 @@
|
||||
class GetCurrentHtmlCallback {
|
||||
htmlRetrieved(String html) {}
|
||||
}
|
3
lib/src/models/callbacks/html_changed_listener.dart
Normal file
3
lib/src/models/callbacks/html_changed_listener.dart
Normal file
@ -0,0 +1,3 @@
|
||||
class HtmlChangedListener {
|
||||
htmlChangedAsync(String html) {}
|
||||
}
|
3
lib/src/models/callbacks/loaded_listener.dart
Normal file
3
lib/src/models/callbacks/loaded_listener.dart
Normal file
@ -0,0 +1,3 @@
|
||||
class LoadedListener {
|
||||
editorLoaded() {}
|
||||
}
|
@ -9,6 +9,7 @@ class RichEditorOptions {
|
||||
String? placeholder;
|
||||
String? baseFontFamily;
|
||||
BarPosition? barPosition;
|
||||
bool? enableVideo;
|
||||
|
||||
RichEditorOptions({
|
||||
Color? backgroundColor,
|
||||
@ -17,6 +18,7 @@ class RichEditorOptions {
|
||||
String? placeholder,
|
||||
String? baseFontFamily,
|
||||
BarPosition? barPosition,
|
||||
bool? enableVideo = true,
|
||||
}) {
|
||||
this.backgroundColor = backgroundColor;
|
||||
this.baseTextColor = baseTextColor;
|
||||
@ -24,5 +26,6 @@ class RichEditorOptions {
|
||||
this.placeholder = placeholder;
|
||||
this.baseFontFamily = baseFontFamily;
|
||||
this.barPosition = barPosition;
|
||||
this.enableVideo = enableVideo;
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import 'package:rich_editor/src/models/rich_editor_options.dart';
|
||||
import 'package:rich_editor/src/services/local_server.dart';
|
||||
import 'package:rich_editor/src/utils/javascript_executor_base.dart';
|
||||
import 'package:rich_editor/src/widgets/editor_tool_bar.dart';
|
||||
// import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class RichEditor extends StatefulWidget {
|
||||
final String? value;
|
||||
@ -35,8 +34,6 @@ class RichEditorState extends State<RichEditor> {
|
||||
final Key _mapKey = UniqueKey();
|
||||
String assetPath = 'packages/rich_editor/assets/editor/editor.html';
|
||||
|
||||
// InAppWebViewController? webViewController;
|
||||
|
||||
int port = 5321;
|
||||
String html = '';
|
||||
LocalServer? localServer;
|
||||
@ -94,6 +91,7 @@ class RichEditorState extends State<RichEditor> {
|
||||
child: EditorToolBar(
|
||||
getImageUrl: widget.getImageUrl,
|
||||
javascriptExecutor: javascriptExecutor,
|
||||
enableVideo: widget.editorOptions!.enableVideo,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
@ -114,9 +112,11 @@ class RichEditorState extends State<RichEditor> {
|
||||
}
|
||||
},
|
||||
onLoadStop: (controller, link) async {
|
||||
if (link!.path != 'blank') {
|
||||
javascriptExecutor.init(_controller!);
|
||||
await _setInitialValues();
|
||||
_addJSListener();
|
||||
}
|
||||
},
|
||||
// javascriptMode: JavascriptMode.unrestricted,
|
||||
// gestureNavigationEnabled: false,
|
||||
@ -138,6 +138,7 @@ class RichEditorState extends State<RichEditor> {
|
||||
child: EditorToolBar(
|
||||
getImageUrl: widget.getImageUrl,
|
||||
javascriptExecutor: javascriptExecutor,
|
||||
enableVideo: widget.editorOptions!.enableVideo,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -3,6 +3,9 @@ import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:rich_editor/src/extensions/extensions.dart';
|
||||
import 'package:rich_editor/src/models/callbacks/did_html_change_listener.dart';
|
||||
import 'package:rich_editor/src/models/callbacks/html_changed_listener.dart';
|
||||
import 'package:rich_editor/src/models/callbacks/loaded_listener.dart';
|
||||
import 'package:rich_editor/src/models/editor_state.dart';
|
||||
import 'package:rich_editor/src/models/enum/command_name.dart';
|
||||
|
||||
@ -19,9 +22,27 @@ class JavascriptExecutorBase {
|
||||
String defaultEncoding = "UTF-8";
|
||||
|
||||
String? htmlField = "";
|
||||
|
||||
var didHtmlChange = false;
|
||||
|
||||
Map<CommandName, CommandState> commandStates = {};
|
||||
|
||||
List<Map<CommandName, CommandState>> commandStatesChangedListeners =
|
||||
<Map<CommandName, CommandState>>[];
|
||||
|
||||
List<DidHtmlChangeListener> didHtmlChangeListeners =
|
||||
<DidHtmlChangeListener>[];
|
||||
|
||||
List<HtmlChangedListener> htmlChangedListeners = <HtmlChangedListener>[];
|
||||
|
||||
// protected val fireHtmlChangedListenersQueue = AsyncProducerConsumerQueue<String>(1) { html ->
|
||||
// fireHtmlChangedListeners(html)
|
||||
// }
|
||||
|
||||
bool isLoaded = false;
|
||||
|
||||
List<LoadedListener> loadedListeners = <LoadedListener>[];
|
||||
|
||||
init(InAppWebViewController? controller) {
|
||||
_controller = controller;
|
||||
}
|
||||
@ -42,8 +63,8 @@ class JavascriptExecutorBase {
|
||||
|
||||
getCurrentHtml() async {
|
||||
String? html = await executeJavascript('getEncodedHtml()');
|
||||
String? decodedHtml = Uri.decodeFull(html!);
|
||||
if (decodedHtml.startsWith('"') && decodedHtml.endsWith('"')) {
|
||||
String? decodedHtml = decodeHtml(html!);
|
||||
if (decodedHtml!.startsWith('"') && decodedHtml.endsWith('"')) {
|
||||
decodedHtml = decodedHtml.substring(1, decodedHtml.length - 1);
|
||||
}
|
||||
return decodedHtml;
|
||||
@ -247,11 +268,11 @@ class JavascriptExecutorBase {
|
||||
await executeJavascript("setInputEnabled($inputEnabled);");
|
||||
}
|
||||
|
||||
static decodeHtml(String html) {
|
||||
decodeHtml(String html) {
|
||||
return Uri.decodeFull(html);
|
||||
}
|
||||
|
||||
static encodeHtml(String html) {
|
||||
encodeHtml(String html) {
|
||||
return Uri.encodeFull(html);
|
||||
}
|
||||
|
||||
@ -295,9 +316,9 @@ class JavascriptExecutorBase {
|
||||
bool didHtmlChange, Map<CommandName, CommandState> commandStates) {
|
||||
if (this.didHtmlChange != didHtmlChange) {
|
||||
this.didHtmlChange = didHtmlChange;
|
||||
// didHtmlChangeListeners.forEach {
|
||||
// it.didHtmlChange(didHtmlChange);
|
||||
// }
|
||||
didHtmlChangeListeners.forEach((element) {
|
||||
element.didHtmlChange(didHtmlChange);
|
||||
});
|
||||
}
|
||||
|
||||
handleRetrievedCommandStates(commandStates);
|
||||
@ -307,37 +328,91 @@ class JavascriptExecutorBase {
|
||||
determineDerivedCommandStates(commandStates);
|
||||
|
||||
this.commandStates = commandStates;
|
||||
|
||||
// commandStatesChangedListeners.forEach {
|
||||
// it.invoke(this.commandStates)
|
||||
// }
|
||||
commandStatesChangedListeners.forEach((element) {
|
||||
element = this.commandStates;
|
||||
});
|
||||
}
|
||||
|
||||
determineDerivedCommandStates(Map<CommandName, CommandState> commandStates) {
|
||||
// commandStates[CommandName.FORMATBLOCK]?.let {
|
||||
// formatCommandState
|
||||
// ->
|
||||
// commandStates.put(CommandName.H1, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "h1")))
|
||||
// commandStates.put(CommandName.H2, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "h2")))
|
||||
// commandStates.put(CommandName.H3, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "h3")))
|
||||
// commandStates.put(CommandName.H4, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "h4")))
|
||||
// commandStates.put(CommandName.H5, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "h5")))
|
||||
// commandStates.put(CommandName.H6, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "h6")))
|
||||
// commandStates.put(CommandName.P, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "p")))
|
||||
// commandStates.put(CommandName.PRE, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "pre")))
|
||||
// commandStates.put(CommandName.BR, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "")))
|
||||
// commandStates.put(CommandName.BLOCKQUOTE, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "blockquote")))
|
||||
// }
|
||||
//
|
||||
// commandStates[CommandName.INSERTHTML]?.let { insertHtmlState ->
|
||||
// commandStates.put(CommandName.INSERTLINK, insertHtmlState)
|
||||
// commandStates.put(CommandName.INSERTIMAGE, insertHtmlState)
|
||||
// commandStates.put(CommandName.INSERTCHECKBOX, insertHtmlState)
|
||||
// }
|
||||
if (commandStates[CommandName.FORMATBLOCK] != null) {
|
||||
var formatCommandState = commandStates[CommandName.FORMATBLOCK];
|
||||
commandStates.update(
|
||||
CommandName.H1,
|
||||
(val) => CommandState(formatCommandState!.executable,
|
||||
isFormatActivated(formatCommandState, "h1")),
|
||||
);
|
||||
commandStates.update(
|
||||
CommandName.H2,
|
||||
(val) => CommandState(formatCommandState!.executable,
|
||||
isFormatActivated(formatCommandState, "h2")));
|
||||
commandStates.update(
|
||||
CommandName.H3,
|
||||
(val) => CommandState(formatCommandState!.executable,
|
||||
isFormatActivated(formatCommandState, "h3")),
|
||||
);
|
||||
commandStates.update(
|
||||
CommandName.H4,
|
||||
(val) => CommandState(formatCommandState!.executable,
|
||||
isFormatActivated(formatCommandState, "h4")),
|
||||
);
|
||||
commandStates.update(
|
||||
CommandName.H5,
|
||||
(val) => CommandState(formatCommandState!.executable,
|
||||
isFormatActivated(formatCommandState, "h5")),
|
||||
);
|
||||
commandStates.update(
|
||||
CommandName.H6,
|
||||
(val) => CommandState(formatCommandState!.executable,
|
||||
isFormatActivated(formatCommandState, "h6")),
|
||||
);
|
||||
commandStates.update(
|
||||
CommandName.P,
|
||||
(val) => CommandState(formatCommandState!.executable,
|
||||
isFormatActivated(formatCommandState, "p")),
|
||||
);
|
||||
commandStates.update(
|
||||
CommandName.PRE,
|
||||
(val) => CommandState(formatCommandState!.executable,
|
||||
isFormatActivated(formatCommandState, "pre")),
|
||||
);
|
||||
commandStates.update(
|
||||
CommandName.BR,
|
||||
(val) => CommandState(formatCommandState!.executable,
|
||||
isFormatActivated(formatCommandState, "")),
|
||||
);
|
||||
commandStates.update(
|
||||
CommandName.BLOCKQUOTE,
|
||||
(val) => CommandState(formatCommandState!.executable,
|
||||
isFormatActivated(formatCommandState, "blockquote")),
|
||||
);
|
||||
}
|
||||
|
||||
if (commandStates[CommandName.INSERTHTML] != null) {
|
||||
CommandState? insertHtmlState = commandStates[CommandName.INSERTHTML];
|
||||
commandStates.update(CommandName.INSERTLINK, (val) => insertHtmlState!);
|
||||
commandStates.update(CommandName.INSERTIMAGE, (val) => insertHtmlState!);
|
||||
commandStates.update(
|
||||
CommandName.INSERTCHECKBOX, (val) => insertHtmlState!);
|
||||
}
|
||||
}
|
||||
|
||||
String isFormatActivated(CommandState formatCommandState, String format) {
|
||||
return (formatCommandState.value == format)
|
||||
.toString(); // rich_text_editor.js reports boolean values as string, so we also have to convert it to string
|
||||
}
|
||||
|
||||
addCommandStatesChangedListener(
|
||||
Map<CommandName, CommandState> commandStates) {
|
||||
commandStatesChangedListeners.add(commandStates);
|
||||
|
||||
// listener.invoke(commandStates);
|
||||
}
|
||||
|
||||
addDidHtmlChangeListener(DidHtmlChangeListener listener) {
|
||||
didHtmlChangeListeners.add(listener);
|
||||
}
|
||||
|
||||
addHtmlChangedListener(HtmlChangedListener listener) {
|
||||
htmlChangedListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,13 @@ class EditorToolBar extends StatelessWidget {
|
||||
final Function(File image)? getImageUrl;
|
||||
final Function(File video)? getVideoUrl;
|
||||
final JavascriptExecutorBase javascriptExecutor;
|
||||
final bool? enableVideo;
|
||||
|
||||
EditorToolBar({
|
||||
this.getImageUrl,
|
||||
this.getVideoUrl,
|
||||
required this.javascriptExecutor,
|
||||
this.enableVideo,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -85,6 +87,14 @@ class EditorToolBar extends StatelessWidget {
|
||||
}
|
||||
},
|
||||
),
|
||||
Visibility(
|
||||
visible: false,
|
||||
child: TabButton(
|
||||
tooltip: 'Insert video',
|
||||
icon: Icons.video_call_sharp,
|
||||
onTap: () async {},
|
||||
),
|
||||
),
|
||||
TabButton(
|
||||
tooltip: 'Underline',
|
||||
icon: Icons.format_underline,
|
||||
|
Loading…
Reference in New Issue
Block a user