diff --git a/assets/editor/editor.html b/assets/editor/editor.html
index 553186e..f46d699 100644
--- a/assets/editor/editor.html
+++ b/assets/editor/editor.html
@@ -10,7 +10,7 @@
-
+
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 8741d99..3e7d066 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -1,3 +1,5 @@
+import 'dart:convert';
+
import 'package:flutter/material.dart';
import 'package:rich_editor/rich_editor.dart';
@@ -28,11 +30,72 @@ class MyHomePage extends StatefulWidget {
}
class _MyHomePageState extends State {
+ GlobalKey keyEditor = GlobalKey();
+
@override
Widget build(BuildContext context) {
return Scaffold(
- appBar: AppBar(title: Text(widget.title)),
- body: RichEditor(),
+ appBar: AppBar(
+ title: Text(widget.title),
+ actions: [
+ PopupMenuButton(
+ child: IconButton(
+ icon: Icon(Icons.more_vert),
+ onPressed: null,
+ disabledColor: Colors.white,
+ ),
+ itemBuilder: (context) {
+ return [
+ PopupMenuItem(
+ child: Text('Get HTML'),
+ value: 0,
+ ),
+ PopupMenuItem(
+ child: Text('Clear content'),
+ value: 1,
+ ),
+ PopupMenuItem(
+ child: Text('Hide keyboard'),
+ value: 2,
+ ),
+ PopupMenuItem(
+ child: Text('Show Keyboard'),
+ value: 3,
+ ),
+ ];
+ },
+ onSelected: (val) async {
+ switch(val) {
+ case 0: {
+ String? html = await keyEditor.currentState?.getHtml();
+ print(html);
+ } break;
+ case 1: {
+ await keyEditor.currentState?.clear();
+ } break;
+ case 2: {
+ await keyEditor.currentState?.unFocus();
+ } break;
+ case 3: {
+ await keyEditor.currentState?.focus();
+ } break;
+ }
+ },
+ ),
+ ],
+ ),
+ body: RichEditor(
+ key: keyEditor,
+ value: ' init html val
',
+ // You can return a Link (maybe you need to upload the image to your
+ // storage before displaying in the editor or you can also use base64
+ getImageUrl: (image) {
+ String link = 'https://avatars.githubusercontent.com/u/24323581?v=4';
+ String base64 = base64Encode(image.readAsBytesSync());
+ String base64String = 'data:image/png;base64, $base64';
+ return base64String;
+ },
+ ),
);
}
}
diff --git a/lib/src/rendering/rich_editor.dart b/lib/src/rendering/rich_editor.dart
index ab85263..f238abb 100644
--- a/lib/src/rendering/rich_editor.dart
+++ b/lib/src/rendering/rich_editor.dart
@@ -1,25 +1,36 @@
+import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.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/tabs.dart';
import 'package:webview_flutter/webview_flutter.dart';
class RichEditor extends StatefulWidget {
+ final String? value;
+ final Function(File image)? getImageUrl;
+ final Function(File video)? getVideoUrl;
+
+ RichEditor({Key? key, this.value, this.getImageUrl, this.getVideoUrl})
+ : super(key: key);
+
@override
- _RichEditorState createState() => _RichEditorState();
+ RichEditorState createState() => RichEditorState();
}
-class _RichEditorState extends State {
+class RichEditorState extends State {
WebViewController? _controller;
String text = "";
final Key _mapKey = UniqueKey();
String assetPath = 'packages/rich_editor/assets/editor/editor.html';
int port = 5321;
+ String html = '';
LocalServer? localServer;
+ JavascriptExecutorBase javascriptExecutorBase = JavascriptExecutorBase();
@override
void initState() {
@@ -30,9 +41,9 @@ class _RichEditorState extends State {
}
}
- initServer() {
+ initServer() async {
localServer = LocalServer(port);
- localServer!.start(handleRequest);
+ await localServer!.start(handleRequest);
}
void handleRequest(HttpRequest request) {
@@ -45,7 +56,6 @@ class _RichEditorState extends State {
}
}
-
@override
void dispose() {
if (_controller != null) {
@@ -66,28 +76,36 @@ class _RichEditorState extends State {
Widget build(BuildContext context) {
return Column(
children: [
- GroupedTab(controller: _controller),
+ GroupedTab(
+ controller: _controller,
+ getImageUrl: widget.getImageUrl,
+ ),
Expanded(
child: WebView(
key: _mapKey,
- // initialUrl:
- // 'file:///android_asset/flutter_assets/packages/rich_editor/assets/editor/editor.html',
- onWebViewCreated: (WebViewController controller) {
+ onWebViewCreated: (WebViewController controller) async {
_controller = controller;
print('WebView created');
setState(() {});
if (!Platform.isAndroid) {
print('Loading');
- _loadHtmlFromAssets();
+ await _loadHtmlFromAssets();
} else {
- _controller!.loadUrl('file:///android_asset/flutter_assets/$assetPath');
+ await _controller!
+ .loadUrl('file:///android_asset/flutter_assets/$assetPath');
+ }
+ javascriptExecutorBase.init(_controller!);
+ if (widget.value != null) {
+ // wait 1 second before setting the html
+ Timer(Duration(seconds: 1), () async {
+ await javascriptExecutorBase.setHtml(widget.value!);
+ });
}
},
javascriptMode: JavascriptMode.unrestricted,
gestureNavigationEnabled: true,
gestureRecognizers: [
- Factory(
- () => VerticalDragGestureRecognizer()..onUpdate = (_) {}),
+ Factory(() => VerticalDragGestureRecognizer()..onUpdate = (_) {}),
].toSet(),
onWebResourceError: (e) {
print("error ${e.description}");
@@ -100,4 +118,32 @@ class _RichEditorState extends State {
],
);
}
+
+ Future getHtml() async {
+ try {
+ html = await javascriptExecutorBase.getCurrentHtml();
+ } catch (e) {}
+ return html;
+ }
+
+ setHtml(String html) async {
+ return await javascriptExecutorBase.setHtml(html);
+ }
+
+ // Hide the keyboard using JavaScript since it's being opened in a WebView
+ // https://stackoverflow.com/a/8263376/10835183
+ unFocus() {
+ _controller!.evaluateJavascript('document.activeElement.blur();');
+ }
+
+ // Clear editor content using Javascript
+ clear() {
+ _controller!.evaluateJavascript('document.getElementById(\'editor\').innerHTML = "";');
+ }
+
+ // Focus and Show the keyboard using JavaScript
+ // https://stackoverflow.com/a/6809236/10835183
+ focus() {
+ _controller!.evaluateJavascript('document.getElementById(\'editor\').focus();');
+ }
}
diff --git a/lib/src/widgets/insert_image_dialog.dart b/lib/src/widgets/insert_image_dialog.dart
index 5e47716..bfc01bc 100644
--- a/lib/src/widgets/insert_image_dialog.dart
+++ b/lib/src/widgets/insert_image_dialog.dart
@@ -12,6 +12,7 @@ class _InsertImageDialogState extends State {
TextEditingController link = TextEditingController();
TextEditingController alt = TextEditingController();
+ bool picked = false;
@override
Widget build(BuildContext context) {
@@ -42,7 +43,7 @@ class _InsertImageDialogState extends State {
),
),
],
- onDone: () => Navigator.pop(context, [link.text, alt.text]),
+ onDone: () => Navigator.pop(context, [link.text, alt.text, picked]),
onCancel: () => Navigator.pop(context),
);
}
@@ -57,6 +58,7 @@ class _InsertImageDialogState extends State {
if (image != null) {
link.text = image.path;
+ picked = true;
}
}
}
diff --git a/lib/src/widgets/tabs.dart b/lib/src/widgets/tabs.dart
index f0ab7f1..8e67ccd 100644
--- a/lib/src/widgets/tabs.dart
+++ b/lib/src/widgets/tabs.dart
@@ -1,3 +1,5 @@
+import 'dart:io';
+
import 'package:flutter/material.dart';
import 'package:rich_editor/src/utils/javascript_executor_base.dart';
import 'package:rich_editor/src/widgets/check_dialog.dart';
@@ -13,8 +15,10 @@ import 'heading_dialog.dart';
class GroupedTab extends StatelessWidget {
final WebViewController? controller;
+ final Function(File image)? getImageUrl;
+ final Function(File video)? getVideoUrl;
- GroupedTab({this.controller});
+ GroupedTab({this.controller, this.getImageUrl, this.getVideoUrl});
JavascriptExecutorBase javascriptExecutorBase = JavascriptExecutorBase();
@@ -77,6 +81,9 @@ class GroupedTab extends StatelessWidget {
},
);
if (link != null) {
+ if (getImageUrl != null && link[2]) {
+ link[0] = await getImageUrl!(File(link[0]));
+ }
await javascriptExecutorBase.insertImage(
link[0],
alt: link[1],
@@ -195,7 +202,8 @@ class GroupedTab extends StatelessWidget {
},
);
if (command != null)
- await javascriptExecutorBase.setFontSize(int.tryParse(command)!);
+ await javascriptExecutorBase
+ .setFontSize(int.tryParse(command)!);
},
),
TabButton(
@@ -301,13 +309,14 @@ class GroupedTab extends StatelessWidget {
await javascriptExecutorBase.insertCheckbox(text);
},
),
- TabButton(
- tooltip: 'Search',
- icon: Icons.search,
- onTap: () async {
- // await javascriptExecutorBase.insertNumberedList();
- },
- ),
+ /// TODO: Implement Search feature
+ // TabButton(
+ // tooltip: 'Search',
+ // icon: Icons.search,
+ // onTap: () async {
+ // // await javascriptExecutorBase.insertNumberedList();
+ // },
+ // ),
],
),
),