diff --git a/CHANGELOG.md b/CHANGELOG.md index e86eb26..2fee4d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,12 @@ * @Author: ekibun * @Date: 2020-08-08 08:16:50 * @LastEditors: ekibun - * @LastEditTime: 2020-08-20 14:05:32 + * @LastEditTime: 2020-08-20 14:55:13 --> +## 0.0.2 + +* update example. + ## 0.0.1 * initial publish. diff --git a/example/lib/code/highlight.dart b/example/lib/code/highlight.dart index c66950b..d8da99e 100644 --- a/example/lib/code/highlight.dart +++ b/example/lib/code/highlight.dart @@ -72,7 +72,8 @@ class CodeInputController extends TextEditingController { InlineSpan leftSpan; oldSpan.children?.indexWhere((element) { String elementText = element.toPlainText(); - if (start + elementText.length > splitAt || !newText.startsWith(elementText, start)) { + if (start + elementText.length > splitAt || + !newText.startsWith(elementText, start)) { leftSpan = element; return true; } @@ -86,7 +87,9 @@ class CodeInputController extends TextEditingController { oldSpan.children?.sublist(beforeSpans.length)?.lastIndexWhere((element) { String elementText = element.toPlainText(); if (splitAt + end + elementText.length >= newText.length || - !newText.substring(start, newText.length - end).endsWith(elementText)) { + !newText + .substring(start, newText.length - end) + .endsWith(elementText)) { rightSpan = element; return true; } @@ -98,7 +101,9 @@ class CodeInputController extends TextEditingController { return TextSpan(style: style, children: [ ...beforeSpans, TextSpan( - style: leftSpan != null && leftSpan == rightSpan ? leftSpan.style : style, + style: leftSpan != null && leftSpan == rightSpan + ? leftSpan.style + : style, text: newText.substring(start, max(start, newText.length - end))), ...endSpans.reversed ]); diff --git a/example/lib/main.dart b/example/lib/main.dart index 9f3700a..ed8c715 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,21 +1,24 @@ /* - * @Description: + * @Description: example * @Author: ekibun * @Date: 2020-08-08 08:16:51 * @LastEditors: ekibun - * @LastEditTime: 2020-08-17 21:46:10 + * @LastEditTime: 2020-08-20 14:42:10 */ import 'package:flutter/material.dart'; +import 'dart:typed_data'; -import 'package:flutter_qjs_example/test.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter_qjs/flutter_qjs.dart'; + +import 'code/editor.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { - - const MyApp({ Key key }) : super(key: key); + const MyApp({Key key}) : super(key: key); @override Widget build(BuildContext context) { @@ -24,8 +27,6 @@ class MyApp extends StatelessWidget { debugShowCheckedModeBanner: false, theme: ThemeData( appBarTheme: AppBarTheme(brightness: Brightness.dark, elevation: 0), - primaryColor: Color(0xfff09199), - accentColor: Color(0xffec818a), backgroundColor: Colors.grey[300], primaryColorBrightness: Brightness.dark, ), @@ -35,5 +36,109 @@ class MyApp extends StatelessWidget { initialRoute: 'home', ); } +} -} \ No newline at end of file +class TestPage extends StatefulWidget { + @override + State createState() => _TestPageState(); +} + +class _TestPageState extends State { + String code, resp; + FlutterJs engine; + + _createEngine() async { + if (engine != null) return; + engine = FlutterJs(); + await engine.setMethodHandler((String method, List arg) async { + switch (method) { + case "http": + Response response = await Dio().get(arg[0]); + return response.data; + case "test": + return await arg[0]([ + true, + 1, + 0.5, + "str", + {"key": "val", 0: 1}, + Uint8List(2), + Int32List(2), + Int64List(2), + Float64List(2), + Float32List(2) + ]); + default: + return JsMethodHandlerNotImplement(); + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("JS engine test"), + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + FlatButton( + child: Text("create engine"), onPressed: _createEngine), + FlatButton( + child: Text("evaluate"), + onPressed: () async { + if (engine == null) { + print("please create engine first"); + return; + } + try { + resp = (await engine.evaluate(code ?? '', "")) + .toString(); + } catch (e) { + resp = e.toString(); + } + setState(() {}); + }), + FlatButton( + child: Text("close engine"), + onPressed: () async { + if (engine != null) return; + await engine.destroy(); + engine = null; + }), + ], + ), + ), + Container( + padding: const EdgeInsets.all(12), + color: Colors.grey.withOpacity(0.1), + constraints: BoxConstraints(minHeight: 200), + child: CodeEditor( + onChanged: (v) { + code = v; + }, + ), + ), + SizedBox(height: 16), + Text("result:"), + SizedBox(height: 16), + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + color: Colors.green.withOpacity(0.05), + constraints: BoxConstraints(minHeight: 100), + child: Text(resp ?? ''), + ), + ], + ), + ), + ); + } +} diff --git a/example/lib/test.dart b/example/lib/test.dart deleted file mode 100644 index 2af2c55..0000000 --- a/example/lib/test.dart +++ /dev/null @@ -1,116 +0,0 @@ -/* - * @Description: - * @Author: ekibun - * @Date: 2020-07-18 23:28:55 - * @LastEditors: ekibun - * @LastEditTime: 2020-08-20 13:11:33 - */ -import 'dart:typed_data'; - -import 'package:dio/dio.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_qjs/flutter_qjs.dart'; - -import 'code/editor.dart'; - -class TestPage extends StatefulWidget { - @override - State createState() => _TestPageState(); -} - -class _TestPageState extends State { - String code, resp; - FlutterJs engine; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text("JS engine test"), - ), - body: SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: [ - FlatButton( - child: Text("create engine"), - onPressed: () async { - if (engine != null) return; - engine = FlutterJs(); - engine.setMethodHandler((String method, List arg) async { - switch (method) { - case "http": - Response response = await Dio().get(arg[0]); - return response.data; - case "test": - return await arg[0]([ - true, - 1, - 0.5, - "str", - { "key": "val", 0: 1 }, - Uint8List(2), - Int32List(2), - Int64List(2), - Float64List(2), - Float32List(2)]); - default: - return JsMethodHandlerNotImplement(); - } - }); - }), - FlatButton( - child: Text("evaluate"), - onPressed: () async { - if (engine == null) { - print("please create engine first"); - return; - } - try { - resp = "${await engine.evaluate(code ?? '', "")}"; - } catch (e) { - resp = e.toString(); - } - setState(() {}); - }), - FlatButton( - child: Text("close engine"), - onPressed: () async { - if (engine != null) return; - await engine.destroy(); - engine = null; - }), - ], - ), - ), - Container( - padding: const EdgeInsets.all(12), - color: Colors.grey.withOpacity(0.1), - constraints: BoxConstraints(minHeight: 200), - child: CodeEditor( - onChanged: (v) { - code = v; - }, - ), - ), - SizedBox(height: 16), - Text("result:"), - SizedBox(height: 16), - Container( - width: double.infinity, - padding: const EdgeInsets.all(12), - color: Colors.green.withOpacity(0.05), - constraints: BoxConstraints(minHeight: 100), - child: Text(resp ?? ''), - ), - ], - ), - ), - ); - } -} diff --git a/example/pubspec.lock b/example/pubspec.lock index e583da5..20671eb 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -75,7 +75,7 @@ packages: path: ".." relative: true source: path - version: "0.0.1" + version: "0.0.2" flutter_test: dependency: "direct dev" description: flutter diff --git a/lib/flutter_qjs.dart b/lib/flutter_qjs.dart index 0d43030..ed7f03a 100644 --- a/lib/flutter_qjs.dart +++ b/lib/flutter_qjs.dart @@ -1,31 +1,73 @@ /* - * @Description: + * @Description: quickjs engine * @Author: ekibun * @Date: 2020-08-08 08:29:09 * @LastEditors: ekibun - * @LastEditTime: 2020-08-20 12:49:46 + * @LastEditTime: 2020-08-20 14:43:40 */ import 'dart:async'; import 'dart:io'; import 'package:flutter/services.dart'; +/// Handle function to manage js call with `dart(method, ...args)` function. typedef JsMethodHandler = Future Function(String method, List args); +/// return this in [JsMethodHandler] to mark method not implemented. class JsMethodHandlerNotImplement {} +/// FlutterJs instance. +/// Each [FlutterJs] object creates a new thread that runs a simple js loop. +/// Make sure call `destroy` to terminate thread and release memory when you don't need it. +class FlutterJs { + dynamic _engine; + + _ensureEngine() async { + if (_engine == null) { + _engine = await _FlutterJs.instance._channel.invokeMethod("createEngine"); + print(_engine); + } + } + + /// Set a handler to manage js call with `dart(method, ...args)` function. + setMethodHandler(JsMethodHandler handler) async { + await _ensureEngine(); + _FlutterJs.instance.methodHandlers[_engine] = handler; + } + + /// Terminate thread and release memory. + destroy() async { + if (_engine != null) { + await _FlutterJs.instance._channel + .invokeMethod("close", {"engine": _engine}); + _engine = null; + } + } + + /// Evaluate js script. + Future evaluate(String command, String name) async { + _ensureEngine(); + var arguments = {"engine": _engine, "script": command, "name": name}; + return _FlutterJs.instance._wrapFunctionArguments( + await _FlutterJs.instance._channel.invokeMethod("evaluate", arguments), + _engine); + } +} + class _FlutterJs { factory _FlutterJs() => _getInstance(); static _FlutterJs get instance => _getInstance(); static _FlutterJs _instance; MethodChannel _channel = const MethodChannel('soko.ekibun.flutter_qjs'); - Map methodHandlers = Map(); + Map methodHandlers = + Map(); _FlutterJs._internal() { _channel.setMethodCallHandler((call) async { print(call.arguments); var engine = call.arguments["engine"]; var args = call.arguments["args"]; if (methodHandlers[engine] == null) return call.noSuchMethod(null); - var ret = await methodHandlers[engine](call.method, _wrapFunctionArguments(args, engine)); + var ret = await methodHandlers[engine]( + call.method, _wrapFunctionArguments(args, engine)); if (ret is JsMethodHandlerNotImplement) return call.noSuchMethod(null); return ret; }); @@ -43,8 +85,13 @@ class _FlutterJs { if (val["__js_function__"] != null) { var functionId = val["__js_function__"]; return (List args) async { - var arguments = {"engine": engine, "function": functionId, "arguments": args}; - return _wrapFunctionArguments(await _channel.invokeMethod("call", arguments), engine); + var arguments = { + "engine": engine, + "function": functionId, + "arguments": args + }; + return _wrapFunctionArguments( + await _channel.invokeMethod("call", arguments), engine); }; } else for (var key in val.keys) { @@ -61,33 +108,3 @@ class _FlutterJs { return _instance; } } - -class FlutterJs { - dynamic _engine; - - _ensureEngine() async { - if (_engine == null) { - _engine = await _FlutterJs.instance._channel.invokeMethod("createEngine"); - print(_engine); - } - } - - setMethodHandler(JsMethodHandler handler) async { - await _ensureEngine(); - _FlutterJs.instance.methodHandlers[_engine] = handler; - } - - destroy() async { - if (_engine != null) { - await _FlutterJs.instance._channel.invokeMethod("close", {"engine": _engine}); - _engine = null; - } - } - - Future evaluate(String command, String name) async { - _ensureEngine(); - var arguments = {"engine": _engine, "script": command, "name": ""}; - return _FlutterJs.instance._wrapFunctionArguments( - await _FlutterJs.instance._channel.invokeMethod("evaluate", arguments), _engine); - } -} diff --git a/pubspec.yaml b/pubspec.yaml index 40de111..efa57d5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_qjs -description: A new flutter plugin project. -version: 0.0.1 +description: This plugin is a simple js engine for flutter using the `quickjs` project. Plugin currently supports Windows, Linux, and Android. +version: 0.0.2 homepage: https://github.com/ekibun/flutter_qjs environment: