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