first commit
This commit is contained in:
32
lib/constants.dart
Normal file
32
lib/constants.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rich_editor/src/models/button.dart';
|
||||
|
||||
List<Button> buttons = [
|
||||
Button(icon: Icons.format_bold),
|
||||
Button(icon: Icons.format_italic),
|
||||
Button(icon: Icons.format_underline),
|
||||
Button(icon: Icons.format_strikethrough),
|
||||
Button(icon: Icons.superscript),
|
||||
Button(icon: Icons.subscript),
|
||||
Button(icon: Icons.format_clear),
|
||||
Button(icon: Icons.undo),
|
||||
Button(icon: Icons.redo),
|
||||
Button(icon: Icons.format_quote),
|
||||
Button(icon: Icons.text_format),
|
||||
Button(icon: Icons.font_download),
|
||||
Button(icon: Icons.format_size),
|
||||
Button(icon: Icons.format_color_text),
|
||||
Button(icon: Icons.format_color_fill),
|
||||
Button(icon: Icons.format_indent_decrease),
|
||||
Button(icon: Icons.format_indent_increase),
|
||||
Button(icon: Icons.format_align_left_outlined),
|
||||
Button(icon: Icons.format_align_center),
|
||||
Button(icon: Icons.format_align_right),
|
||||
Button(icon: Icons.format_align_justify),
|
||||
Button(icon: Icons.format_list_bulleted),
|
||||
Button(icon: Icons.format_list_numbered),
|
||||
Button(icon: Icons.link),
|
||||
Button(icon: Icons.image),
|
||||
Button(icon: Icons.check_box_outlined),
|
||||
Button(icon: Icons.search),
|
||||
];
|
||||
3
lib/rich_editor.dart
Normal file
3
lib/rich_editor.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
library rich_editor;
|
||||
|
||||
export 'package:rich_editor/src/rendering/rich_editor.dart';
|
||||
8
lib/src/models/button.dart
Normal file
8
lib/src/models/button.dart
Normal file
@@ -0,0 +1,8 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class Button {
|
||||
IconData icon;
|
||||
|
||||
Button({this.icon = Icons.format_bold});
|
||||
}
|
||||
46
lib/src/rendering/rich_editor.dart
Normal file
46
lib/src/rendering/rich_editor.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rich_editor/src/services/local_server.dart';
|
||||
import 'package:rich_editor/src/widgets/tabs.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class RichEditor extends StatefulWidget {
|
||||
@override
|
||||
_RichEditorState createState() => _RichEditorState();
|
||||
}
|
||||
|
||||
class _RichEditorState extends State<RichEditor> {
|
||||
WebViewController? _controller;
|
||||
String text = "";
|
||||
final Key _mapKey = UniqueKey();
|
||||
|
||||
int port = 5321;
|
||||
LocalServer? localServer;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
GroupedTab(),
|
||||
Flexible(
|
||||
child: WebView(
|
||||
initialUrl: 'file:///android_asset/flutter_assets/packages/rich_editor/assets/editor/editor.html',
|
||||
onWebViewCreated: (WebViewController controller) {
|
||||
_controller = _controller;
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
// child: InAppWebView(
|
||||
// initialFile: 'packages/rich_editor/assets/editor/index.html',
|
||||
// ),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
124
lib/src/services/local_server.dart
Normal file
124
lib/src/services/local_server.dart
Normal file
@@ -0,0 +1,124 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
|
||||
/*
|
||||
* credit thanks to https://github.com/shah-xad/flutter_tex/blob/master/lib/src/utils/tex_view_server.dart
|
||||
*/
|
||||
|
||||
class LocalServer {
|
||||
HttpServer? server;
|
||||
final int port;
|
||||
|
||||
LocalServer(this.port);
|
||||
|
||||
///Closes the server.
|
||||
Future<void> close() async {
|
||||
if (this.server != null) {
|
||||
await this.server?.close(force: true);
|
||||
this.server = null;
|
||||
}
|
||||
}
|
||||
|
||||
///Starts the server
|
||||
Future<void> start(Function(HttpRequest request) request) async {
|
||||
if (this.server != null) {
|
||||
throw Exception('Server already started on http://localhost:$port');
|
||||
}
|
||||
|
||||
var completer = new Completer();
|
||||
runZoned(() {
|
||||
HttpServer.bind('localhost', port, shared: true).then((server) {
|
||||
this.server = server;
|
||||
|
||||
server.listen((HttpRequest httpRequest) async {
|
||||
request(httpRequest);
|
||||
var body = <int>[];
|
||||
var path = httpRequest.requestedUri.path;
|
||||
path = (path.startsWith('/')) ? path.substring(1) : path;
|
||||
path += (path.endsWith('/')) ? 'index.html' : '';
|
||||
|
||||
try {
|
||||
body = (await rootBundle.load(path)).buffer.asUint8List();
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
httpRequest.response.close();
|
||||
return;
|
||||
}
|
||||
|
||||
var contentType = ['text', 'html'];
|
||||
if (!httpRequest.requestedUri.path.endsWith('/') &&
|
||||
httpRequest.requestedUri.pathSegments.isNotEmpty) {
|
||||
var mimeType = lookupMimeType(httpRequest.requestedUri.path,
|
||||
headerBytes: body);
|
||||
if (mimeType != null) {
|
||||
contentType = mimeType.split('/');
|
||||
}
|
||||
}
|
||||
|
||||
httpRequest.response.headers.contentType =
|
||||
new ContentType(contentType[0], contentType[1], charset: 'utf-8');
|
||||
httpRequest.response.add(body);
|
||||
httpRequest.response.close();
|
||||
});
|
||||
completer.complete();
|
||||
});
|
||||
}, onError: (e, stackTrace) => print('Error: $e $stackTrace'));
|
||||
return completer.future;
|
||||
}
|
||||
}
|
||||
|
||||
class WebSocketServer {
|
||||
HttpServer? server;
|
||||
|
||||
final int port;
|
||||
|
||||
WebSocketServer(this.port);
|
||||
|
||||
///Closes the server.
|
||||
Future<void> close() async {
|
||||
if (this.server != null) {
|
||||
await this.server?.close(force: true);
|
||||
this.server = null;
|
||||
}
|
||||
}
|
||||
|
||||
///Starts the server
|
||||
Future<void> start() async {
|
||||
if (this.server != null) {
|
||||
throw Exception('Server already started on http://localhost:$port');
|
||||
}
|
||||
var completer = new Completer();
|
||||
runZoned(() {
|
||||
HttpServer.bind('localhost', port, shared: true).then(
|
||||
(HttpServer server) {
|
||||
print('[+]WebSocket listening at -- ws://localhost:$port/');
|
||||
this.server = server;
|
||||
server.listen((HttpRequest request) {
|
||||
WebSocketTransformer.upgrade(request).then((WebSocket ws) {
|
||||
ws.listen(
|
||||
(data) {
|
||||
print(
|
||||
'\t\t${request.connectionInfo?.remoteAddress} -- ${data.toString()}');
|
||||
Timer(Duration(seconds: 1), () {
|
||||
if (ws.readyState == WebSocket.open)
|
||||
// checking connection state helps to avoid unprecedented errors
|
||||
ws.add("dfdfdfdfdfd");
|
||||
});
|
||||
},
|
||||
onDone: () => print('[+]Done :)'),
|
||||
onError: (err) => print('[!]Error -- ${err.toString()}'),
|
||||
cancelOnError: true,
|
||||
);
|
||||
// request.response.close();
|
||||
}, onError: (err) => print('[!]Error -- ${err.toString()}'));
|
||||
}, onError: (err) => print('[!]Error -- ${err.toString()}'));
|
||||
completer.complete();
|
||||
}, onError: (err) => print('[!]Error -- ${err.toString()}'));
|
||||
}, onError: (err) => print('[!]Error -- ${err.toString()}'));
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
}
|
||||
40
lib/src/widgets/tab_button.dart
Normal file
40
lib/src/widgets/tab_button.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TabButton extends StatelessWidget {
|
||||
final IconData icon;
|
||||
|
||||
TabButton({this.icon = Icons.format_bold});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 5.0),
|
||||
child: Container(
|
||||
height: 45.0,
|
||||
width: 45.0,
|
||||
decoration: BoxDecoration(
|
||||
// color: Color(0xff212121),
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(5.0),
|
||||
),
|
||||
),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.all(Radius.circular(5.0)),
|
||||
onTap: () {},
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Icon(
|
||||
icon,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
41
lib/src/widgets/tabs.dart
Normal file
41
lib/src/widgets/tabs.dart
Normal file
@@ -0,0 +1,41 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rich_editor/constants.dart';
|
||||
import 'package:rich_editor/src/models/button.dart';
|
||||
import 'package:rich_editor/src/widgets/tab_button.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class GroupedTab extends StatelessWidget {
|
||||
final WebViewController? controller;
|
||||
|
||||
GroupedTab({this.controller});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Color(0xff424242),
|
||||
height: 59.0,
|
||||
child: Column(
|
||||
children: [
|
||||
Flexible(
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: [
|
||||
for(Button button in buttons)
|
||||
InkWell(
|
||||
onTap: () {
|
||||
print('BOLDDD');
|
||||
controller?.evaluateJavascript('setBold()');
|
||||
},
|
||||
child: TabButton(
|
||||
icon: button.icon,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
0
lib/util.dart
Normal file
0
lib/util.dart
Normal file
Reference in New Issue
Block a user