├── .github └── FUNDING.yml ├── .gitignore ├── .metadata ├── .vscode ├── launch.json └── settings.json ├── CHANGELOG.md ├── FIXED_RESOURCE.txt ├── LICENSE ├── README.md ├── android ├── .gitignore ├── CMakeLists.txt ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── io │ └── abner │ └── flutter_js │ ├── FlutterJsPlugin.kt │ ├── JSEngine.kt │ └── flutter_js │ └── FlutterJsPlugin.kt ├── assets └── js │ └── fetch.js ├── doc ├── android_ajv_form.png ├── android_ajv_result.png ├── flutter_js.png ├── ios_ajv_form.png ├── ios_ajv_result.png ├── ios_capture.png ├── ios_source.html └── macos-capture.png ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── io │ │ │ │ │ └── abner │ │ │ │ │ └── flutter_js_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── assets │ ├── js.ico │ └── js │ │ └── ajv.js ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ ├── ajv_example.dart │ ├── ajv_result_screen.dart │ ├── api_call.dart │ ├── example.dart │ ├── form.dart │ ├── json_viewer.dart │ └── main.dart ├── linux │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ │ └── CMakeLists.txt │ ├── main.cc │ ├── my_application.cc │ └── my_application.h ├── macos │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ └── Flutter-Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ └── app_icon_64.png │ │ ├── Base.lproj │ │ └── MainMenu.xib │ │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ │ ├── DebugProfile.entitlements │ │ ├── Info.plist │ │ ├── MainFlutterWindow.swift │ │ └── Release.entitlements ├── pubspec.lock ├── pubspec.yaml ├── test │ └── flutter_js_test.dart └── windows │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ └── CMakeLists.txt │ └── runner │ ├── CMakeLists.txt │ ├── Runner.rc │ ├── flutter_window.cpp │ ├── flutter_window.h │ ├── main.cpp │ ├── resource.h │ ├── resources │ └── app_icon.ico │ ├── runner.exe.manifest │ ├── utils.cpp │ ├── utils.h │ ├── win32_window.cpp │ └── win32_window.h ├── ios.mp4 ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── Console.swift │ ├── FlutterJsPlugin.h │ ├── FlutterJsPlugin.m │ ├── Global.swift │ ├── JSContextFoundation.swift │ ├── JSInsert.swift │ └── SwiftFlutterJsPlugin.swift └── flutter_js.podspec ├── lib ├── extensions │ ├── fetch.dart │ ├── handle_promises.dart │ └── xhr.dart ├── flutter_js.dart ├── javascript_runtime.dart ├── javascriptcore │ ├── binding │ │ ├── js_base.dart │ │ ├── js_context_ref.dart │ │ ├── js_global_context_ref.dart │ │ ├── js_object_ref.dart │ │ ├── js_string_ref.dart │ │ ├── js_typed_array.dart │ │ ├── js_value_ref.dart │ │ └── jsc_ffi.dart │ ├── flutter_jscore.dart │ ├── jscore │ │ ├── js_class.dart │ │ ├── js_context.dart │ │ ├── js_context_group.dart │ │ ├── js_object.dart │ │ ├── js_property_name_accumulator.dart │ │ ├── js_property_name_array.dart │ │ ├── js_string.dart │ │ └── js_value.dart │ ├── jscore_bindings.dart │ └── jscore_runtime.dart ├── js_eval_result.dart ├── quickjs-sync-server │ └── quickjs_oasis_jsbridge.dart └── quickjs │ ├── ffi.dart │ ├── isolate.dart │ ├── object.dart │ ├── qjs_typedefs.dart │ ├── quickjs_runtime.dart │ ├── quickjs_runtime2.dart │ ├── utf8_null_terminated.dart │ └── wrapper.dart ├── linux ├── CMakeLists.txt ├── flutter_js_plugin.cc ├── include │ └── flutter_js │ │ └── flutter_js_plugin.h └── shared │ └── libquickjs_c_bridge_plugin.so ├── macos ├── Classes │ └── FlutterJsPlugin.swift └── flutter_js.podspec ├── pubspec.lock ├── pubspec.yaml ├── test └── flutter_js_test.dart └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter_js_plugin.cpp ├── include └── flutter_js │ └── flutter_js_plugin.h └── shared └── quickjs_c_bridge.dll /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: abner 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .classpath 21 | .project 22 | .settings/ 23 | .vscode/* 24 | !.vscode/settings.json 25 | !.vscode/tasks.json 26 | !.vscode/launch.json 27 | !.vscode/extensions.json 28 | !.vscode/*.code-snippets 29 | 30 | # packages file containing multi-root paths 31 | .packages.generated 32 | 33 | # Flutter/Dart/Pub related 34 | **/doc/api/ 35 | .dart_tool/ 36 | .flutter-plugins 37 | .flutter-plugins-dependencies 38 | **/generated_plugin_registrant.dart 39 | .packages 40 | .pub-cache/ 41 | .pub/ 42 | build/ 43 | linked_*.ds 44 | unlinked.ds 45 | unlinked_spec.ds 46 | 47 | # Android related 48 | **/android/**/gradle-wrapper.jar 49 | .gradle/ 50 | **/android/captures/ 51 | **/android/gradlew 52 | **/android/gradlew.bat 53 | **/android/local.properties 54 | **/android/**/GeneratedPluginRegistrant.java 55 | **/android/key.properties 56 | *.keystore 57 | *.jks 58 | 59 | # iOS/XCode related 60 | **/ios/**/*.mode1v3 61 | **/ios/**/*.mode2v3 62 | **/ios/**/*.moved-aside 63 | **/ios/**/*.pbxuser 64 | **/ios/**/*.perspectivev3 65 | **/ios/**/*sync/ 66 | **/ios/**/.sconsign.dblite 67 | **/ios/**/.tags* 68 | **/ios/**/.vagrant/ 69 | **/ios/**/DerivedData/ 70 | **/ios/**/Icon? 71 | **/ios/**/Pods/ 72 | **/ios/**/.symlinks/ 73 | **/ios/**/profile 74 | **/ios/**/xcuserdata 75 | **/ios/.generated/ 76 | **/ios/Flutter/.last_build_id 77 | **/ios/Flutter/App.framework 78 | **/ios/Flutter/Flutter.framework 79 | **/ios/Flutter/Flutter.podspec 80 | **/ios/Flutter/Generated.xcconfig 81 | **/ios/Flutter/ephemeral 82 | **/ios/Flutter/app.flx 83 | **/ios/Flutter/app.zip 84 | **/ios/Flutter/flutter_assets/ 85 | **/ios/Flutter/flutter_export_environment.sh 86 | **/ios/ServiceDefinitions.json 87 | **/ios/Runner/GeneratedPluginRegistrant.* 88 | 89 | # macOS 90 | **/Flutter/ephemeral/ 91 | **/Pods/ 92 | **/macos/Flutter/GeneratedPluginRegistrant.swift 93 | **/macos/Flutter/ephemeral 94 | **/xcuserdata/ 95 | 96 | # Windows 97 | **/windows/flutter/generated_plugin_registrant.cc 98 | **/windows/flutter/generated_plugin_registrant.h 99 | **/windows/flutter/generated_plugins.cmake 100 | 101 | # Linux 102 | **/linux/flutter/generated_plugin_registrant.cc 103 | **/linux/flutter/generated_plugin_registrant.h 104 | **/linux/flutter/generated_plugins.cmake 105 | 106 | # Coverage 107 | coverage/ 108 | 109 | # Symbols 110 | app.*.symbols 111 | 112 | # Obfuscation related 113 | app.*.map.json 114 | 115 | # Exceptions to above rules. 116 | !**/ios/**/default.mode1v3 117 | !**/ios/**/default.mode2v3 118 | !**/ios/**/default.pbxuser 119 | !**/ios/**/default.perspectivev3 120 | 121 | # Android Studio will place build artifacts here 122 | /android/app/debug 123 | /android/app/profile 124 | /android/app/release 125 | .cxx/ 126 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 27321ebbad34b0a3fafe99fac037102196d655ff 8 | channel: stable 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "flutter_js", 9 | "request": "launch", 10 | "type": "dart", 11 | "args": [ 12 | "--verbose" 13 | ], 14 | "program": "example/lib/main.dart" 15 | }, 16 | { 17 | "name": "example", 18 | "cwd": "example", 19 | "request": "launch", 20 | "type": "dart", 21 | "args": [ 22 | "--verbose" 23 | ] 24 | }, 25 | { 26 | "name": "packages", 27 | "cwd": "packages", 28 | "request": "launch", 29 | "type": "dart" 30 | }, 31 | { 32 | "name": "test-with-flutterjs", 33 | "type": "dart", 34 | "program": "test/flutter_js_test.dart", 35 | "windows": { 36 | "env": { 37 | "PATH": "${env:Path};${workspaceFolder}\\example\\build\\windows\\runner\\Debug" 38 | } 39 | }, 40 | "linux": { 41 | "env": { 42 | "LIBQUICKJSC_TEST_PATH": "${workspaceFolder}/example/build/linux/debug/bundle/lib/libquickjs_c_bridge_plugin.so" 43 | } 44 | }, 45 | "request": "launch" 46 | } 47 | ] 48 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "future": "cpp" 4 | } 5 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.8.1 2 | 3 | - fix ffi loading of JavascriptCore which failed to resolve relative path 'JavaScriptCore.framework/JavaScriptCore' in 4 | MacOS and iOS, since Flutter version 3.22.0 5 | 6 | # 0.8.0 7 | 8 | - upgrade http dependency to ^1.0.0 9 | 10 | # 0.7.2 11 | - upgraded quickjs code (to allow set timeout and set maxMemory) in preparation for further improvements 12 | - reduced the json output in the example app 13 | 14 | # 0.7.1 15 | - async callback for JSC runtime 16 | - upgraded jsc core in example app 17 | 18 | # 0.7.0 19 | - **Breaking**: require dart 3.0 and Flutter 3 and above 20 | - now supports Flutter 3.10 and Dart 3.0 21 | # 0.6.0 22 | 23 | - **Breaking**: require dart 2.17 24 | - **Breaking**: bump `package:ffi` to `2.0.0` 25 | 26 | # 0.5.1 27 | 28 | - Remove deprecated jCenter android repository in favor of mavenCentral 29 | 30 | # 0.5.0+6 31 | 32 | - Accepted PR [66](https://github.com/abner/flutter_js/pull/66) to return the stack 33 | trace into the error message from javascriptcore 34 | - Accepted PR [#74] which prints multiples args passed into console.log 35 | - Updated example application with new Flutter version (2.10.1) 36 | 37 | # 0.5.0+5 38 | 39 | - Accept PR [67](https://github.com/abner/flutter_js/pull/67) to adjust channelFunction to return the function result when using JavascriptCore on Android 40 | 41 | # 0.5.0+4 42 | 43 | - Accept PR [54](https://github.com/abner/flutter_js/pull/54) to adjust channelFunction to return the function result 44 | 45 | # 0.5.0+3 46 | 47 | - Fix the network call bug: https://github.com/abner/flutter_js/pull/57 48 | 49 | # 0.5.0+2 50 | 51 | - Fix the minimal stack size for 1MB for QuickJS Runtime 52 | 53 | # 0.5.0+1 54 | 55 | - Fix the handle_promises when the promise result is not a string 56 | 57 | # 0.5.0+0 58 | 59 | - Fixes issue relative to build on Dart 2.13 (issues #41 and #42) 60 | 61 | # 0.4.0+6 62 | 63 | - Fix executePendingJobs (wasn't dispatching in the most current version) 64 | 65 | # 0.4.0+5 66 | 67 | - Removed console.log from fetch.js 68 | 69 | # 0.4.0+4 70 | 71 | - Fixed issue on xhr requests - wasn't passing headers to the requests 72 | 73 | # 0.4.0+3 74 | 75 | - Fixed dynamic library load for tests 76 | - Added info about tests into the [README.md](README.md) 77 | 78 | # 0.4.0+2 79 | 80 | - Updated README.md with information about github repository containing the C bridge used on 81 | Windows and Linux 82 | 83 | # 0.4.0+1 84 | 85 | - Updated README.md 86 | 87 | # 0.4.0+0 88 | 89 | - Added support to windows, macos and linux platforms 90 | - Fixed fetch error 91 | - Improved the channels/dart callback integration 92 | 93 | # 0.3.0+0 94 | 95 | - Null-safety migration 96 | 97 | # 0.2.4+0 98 | 99 | - Updated ffi, http dependencies 100 | - Upgraded code for compatibility with ffi 1.0.0 101 | 102 | # 0.2.3+0 103 | 104 | - Updated QuickJS engine to version 2020-11-08 105 | - Fix fetch 106 | 107 | # 0.2.2+0 108 | 109 | - Updated QuickJS engine to version 2020-09-06 110 | 111 | # 0.2.1+0 112 | 113 | - Updated to use QuickJS through Dart ffi instead of Platform Channel 114 | 115 | # 0.2.0+0 116 | 117 | - Updated to use QuickJS through PlatformChannel on Android (with this change, Android apk added size will return to be minimal ) 118 | - Change QuickJS integration to call Android platform in a sync way through http 119 | - Added option to use JavascriptCore on Android 120 | 121 | # 0.1.0+2 122 | 123 | - Small fixes in the documentation on README.md 124 | 125 | # 0.1.0+1 126 | 127 | - Add example of onMessage (bridge which allow javascript code to call Dart) 128 | in the README.md 129 | 130 | # 0.1.0+0 131 | 132 | - Changed to use Dart FFI to call the Javascript Runtimes: QuickJS by Default in Android and JavascriptCore in iOS 133 | 134 | # 0.0.3+1 135 | 136 | - Updated to use a new version of oasis-jsbridge-android which brings _quickjs_ (js engine for Android) 137 | upgraded to the latest version (currently 2020-07-05) 138 | 139 | # 0.0.2+1 140 | 141 | - Upgraded to use [oasis-jsbridge-android](https://github.com/p7s1digital/oasis-jsbridge-android) library under the hood 142 | 143 | # 0.0.1+2 144 | 145 | - Fixed a typo in the FlutterJsPlugin.kt class 146 | 147 | # 0.0.1+1 148 | 149 | - Initial version only provides a very simple api which allow to init the javascript engine and evaluate javascript expressions and get the result as String. 150 | -------------------------------------------------------------------------------- /FIXED_RESOURCE.txt: -------------------------------------------------------------------------------- 1 | HTTP CALL WORKED OK! File returned from https://raw.githubusercontent.com/abner/flutter_js/master/FIXED_RESOURCE.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ábner Oliveira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .cxx 10 | -------------------------------------------------------------------------------- /android/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.6.0) 2 | 3 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -DANDROID_STL=c++_static -DANDROID_TOOLCHAIN=clang -DDEBUGGER_SERVER_PORT=${DEBUGGER_SERVER_PORT}") 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQUICKJS") 5 | include_directories( 6 | src/main/c/quickjs 7 | ) 8 | add_library( fastdev_quickjs_runtime 9 | 10 | # Sets the library as a shared library. 11 | SHARED 12 | 13 | # Provides a relative path to your source file(s). 14 | src/main/c/libfastdev_quickjs_runtime.cpp 15 | 16 | src/main/c/quickjs/cutils.c 17 | src/main/c/quickjs/libregexp.c 18 | src/main/c/quickjs/libunicode.c 19 | src/main/c/quickjs/quickjs.c 20 | ) 21 | 22 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCONFIG_VERSION=\\\"2020-11-08\\\"") 23 | 24 | find_library(log-lib log) 25 | target_link_libraries(fastdev_quickjs_runtime ${log-lib}) -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'io.abner.flutter_js' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | ext.kotlin_version = '1.7.20' 6 | repositories { 7 | google() 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:7.3.1' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | rootProject.allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | maven { url 'https://jitpack.io' } 22 | } 23 | } 24 | 25 | apply plugin: 'com.android.library' 26 | apply plugin: 'kotlin-android' 27 | 28 | android { 29 | compileSdkVersion 34 30 | 31 | namespace "io.abner.flutter_js" 32 | 33 | kotlinOptions { 34 | jvmTarget = JavaVersion.VERSION_1_8.toString() 35 | } 36 | 37 | // //quickjs C build disabled for now - crashing with SEGV_MAPERR on ARM based devices 38 | // externalNativeBuild { 39 | // // Encapsulates your CMake build configurations. 40 | // cmake { 41 | // // Provides a relative path to your CMake build script. 42 | // path "CMakeLists.txt" 43 | // } 44 | // } 45 | 46 | 47 | sourceSets { 48 | main.java.srcDirs += 'src/main/kotlin' 49 | main.jniLibs.srcDirs = ['jniLibs'] 50 | } 51 | defaultConfig { 52 | minSdkVersion 21 53 | ndk { 54 | abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" 55 | } 56 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 57 | } 58 | lintOptions { 59 | disable 'InvalidPackage' 60 | } 61 | } 62 | 63 | dependencies { 64 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 65 | 66 | // implementation using platform-channel with oasis jsbridge 67 | // implementation 'com.github.p7s1digital.oasis-jsbridge-android:oasis-jsbridge-quickjs:0.12.0' 68 | // compile group: 'org.nanohttpd', name: 'nanohttpd', version: '2.3.0' 69 | 70 | // for serve library from jitpack repository 71 | implementation "com.github.fast-development.android-js-runtimes:fastdev-jsruntimes-quickjs:0.3.5" 72 | // implementation "com.github.fast-development.android-js-runtimes:fastdev-jsruntimes-jsc:0.1.3" 73 | } 74 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jul 07 13:01:34 IST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'flutter_js' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/kotlin/io/abner/flutter_js/JSEngine.kt: -------------------------------------------------------------------------------- 1 | package io.abner.flutter_js 2 | 3 | import android.util.Log 4 | //import de.prosiebensat1digital.oasisjsbridge.* 5 | //import kotlinx.coroutines.Dispatchers 6 | import java.util.* 7 | import java.util.logging.Logger 8 | 9 | 10 | class JSEngine(context: android.content.Context) { 11 | 12 | // private var runtime: JsBridge = JsBridge(JsBridgeConfig.standardConfig()) 13 | // private var messageChannelMap = mutableMapOf String>() 14 | 15 | // fun getRuntime(): JsBridge { 16 | // return runtime 17 | // } 18 | 19 | // var runtimeInitialized = false 20 | // val host = "localhost" 21 | 22 | // val port = 0 23 | // init { 24 | 25 | // val errorListener = object : JsBridge.ErrorListener(Dispatchers.Main) { 26 | // override fun onError(error: JsBridgeError) { 27 | // Log.e("MainActivity", error.errorString()) 28 | // } 29 | // } 30 | // runtime.registerErrorListener(errorListener) 31 | 32 | // val getUUID = JsValue.fromNativeFunction0(runtime) { 33 | // UUID.randomUUID().toString() 34 | // }.assignToGlobal("FLUTTERJS_getUUID") 35 | 36 | // val sendMessage = JsValue.fromNativeFunction2(runtime) { channelName: String, message: String -> 37 | 38 | // try { 39 | // if (messageChannelMap.containsKey(channelName)) { 40 | // messageChannelMap.getValue(channelName).invoke(message); 41 | // } else { 42 | // Log.i("SendMessage Channel", "Channel ${channelName} wasn't registered!") 43 | // } 44 | 45 | // return@fromNativeFunction2 "$channelName:$message" 46 | // } catch (e: Exception) { 47 | // return@fromNativeFunction2 e.message 48 | // } 49 | 50 | // }.assignToGlobal("FLUTTERJS_sendMessage") 51 | 52 | // runtime.evaluateBlocking( 53 | // """ 54 | // var FLUTTERJS_pendingMessages = {}; 55 | // function sendMessage(channel, message) { 56 | // var idMessage = FLUTTERJS_getUUID(); 57 | // return new Promise((resolve, reject) => { 58 | // FLUTTERJS_pendingMessages[idMessage] = { 59 | // resolve: (v) => { resolve(v); return v;}, 60 | // reject: reject 61 | // }; 62 | // FLUTTERJS_sendMessage(channel, JSON.stringify({ id: idMessage, message: message }) ); 63 | // }); 64 | // } 65 | // """.trimIndent(), 66 | // JsonObjectWrapper::class.java 67 | // ) 68 | // } 69 | 70 | // fun registerChannel(channelName: String, channelFn: (message: String) -> String) { 71 | // messageChannelMap[channelName] = channelFn 72 | // } 73 | 74 | // fun eval(script: String): JsonObjectWrapper { 75 | // return runtime.evaluateBlocking(script, JsonObjectWrapper::class.java) as JsonObjectWrapper 76 | // } 77 | 78 | // fun release() { 79 | // runtime.release() 80 | // } 81 | 82 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/abner/flutter_js/flutter_js/FlutterJsPlugin.kt: -------------------------------------------------------------------------------- 1 | package io.abner.flutter_js.flutter_js 2 | 3 | import androidx.annotation.NonNull 4 | 5 | import io.flutter.embedding.engine.plugins.FlutterPlugin 6 | import io.flutter.plugin.common.MethodCall 7 | import io.flutter.plugin.common.MethodChannel 8 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler 9 | import io.flutter.plugin.common.MethodChannel.Result 10 | 11 | /** FlutterJsPlugin */ 12 | class FlutterJsPlugin: FlutterPlugin, MethodCallHandler { 13 | /// The MethodChannel that will the communication between Flutter and native Android 14 | /// 15 | /// This local reference serves to register the plugin with the Flutter Engine and unregister it 16 | /// when the Flutter Engine is detached from the Activity 17 | private lateinit var channel : MethodChannel 18 | 19 | override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { 20 | channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_js") 21 | channel.setMethodCallHandler(this) 22 | } 23 | 24 | override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { 25 | if (call.method == "getPlatformVersion") { 26 | result.success("Android ${android.os.Build.VERSION.RELEASE}") 27 | } else { 28 | result.notImplemented() 29 | } 30 | } 31 | 32 | override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { 33 | channel.setMethodCallHandler(null) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /assets/js/fetch.js: -------------------------------------------------------------------------------- 1 | function fetch(url, options) { 2 | options = options || {}; 3 | return new Promise( (resolve, reject) => { 4 | const request = new XMLHttpRequest(); 5 | const keys = []; 6 | const all = []; 7 | const headers = {}; 8 | 9 | const response = () => ({ 10 | ok: (request.status/100|0) == 2, // 200-299 11 | statusText: request.statusText, 12 | status: request.status, 13 | url: request.responseURL, 14 | text: () => Promise.resolve(request.responseText), 15 | json: () => { 16 | // TODO: review this handle because it may discard \n from json attributes 17 | try { 18 | // console.log('RESPONSE TEXT IN FETCH: ' + request.responseText); 19 | return Promise.resolve(JSON.parse(request.responseText)); 20 | } catch (e) { 21 | // console.log('ERROR on fetch parsing JSON: ' + e.message); 22 | return Promise.resolve(request.responseText); 23 | } 24 | }, 25 | blob: () => Promise.resolve(new Blob([request.response])), 26 | clone: response, 27 | headers: { 28 | keys: () => keys, 29 | entries: () => all, 30 | get: n => headers[n.toLowerCase()], 31 | has: n => n.toLowerCase() in headers 32 | } 33 | }); 34 | 35 | request.open(options.method || 'get', url, true); 36 | 37 | request.onload = () => { 38 | request.getAllResponseHeaders().replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm, (m, key, value) => { 39 | keys.push(key = key.toLowerCase()); 40 | all.push([key, value]); 41 | headers[key] = headers[key] ? `${headers[key]},${value}` : value; 42 | }); 43 | resolve(response()); 44 | }; 45 | 46 | request.onerror = reject; 47 | 48 | request.withCredentials = options.credentials=='include'; 49 | 50 | if (options.headers) { 51 | if (options.headers.constructor.name == 'Object') { 52 | for (const i in options.headers) { 53 | request.setRequestHeader(i, options.headers[i]); 54 | } 55 | } else { // if it is some Headers pollyfill, the way to iterate is through for of 56 | for (const header of options.headers) { 57 | request.setRequestHeader(header[0], header[1]); 58 | } 59 | } 60 | } 61 | 62 | request.send(options.body || null); 63 | }); 64 | } -------------------------------------------------------------------------------- /doc/android_ajv_form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/doc/android_ajv_form.png -------------------------------------------------------------------------------- /doc/android_ajv_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/doc/android_ajv_result.png -------------------------------------------------------------------------------- /doc/flutter_js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/doc/flutter_js.png -------------------------------------------------------------------------------- /doc/ios_ajv_form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/doc/ios_ajv_form.png -------------------------------------------------------------------------------- /doc/ios_ajv_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/doc/ios_ajv_result.png -------------------------------------------------------------------------------- /doc/ios_capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/doc/ios_capture.png -------------------------------------------------------------------------------- /doc/ios_source.html: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | triangle with all three sides equal 18 | 19 | 20 | -------------------------------------------------------------------------------- /doc/macos-capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/doc/macos-capture.png -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: db747aa1331bd95bc9b3874c842261ca2d302cd5 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_js_example 2 | 3 | Here is a small flutter app showing how to evaluate javascript code inside a flutter app 4 | 5 | 6 | ```dart 7 | import 'package:flutter/material.dart'; 8 | import 'dart:async'; 9 | 10 | import 'package:flutter/services.dart'; 11 | import 'package:flutter_js/flutter_js.dart'; 12 | 13 | void main() => runApp(MyApp()); 14 | 15 | class MyApp extends StatefulWidget { 16 | @override 17 | _MyAppState createState() => _MyAppState(); 18 | } 19 | 20 | class _MyAppState extends State { 21 | String _jsResult = ''; 22 | int _idJsEngine = -1; 23 | @override 24 | void initState() { 25 | super.initState(); 26 | initJsEngine(); 27 | } 28 | 29 | // Platform messages are asynchronous, so we initialize in an async method. 30 | Future initJsEngine() async { 31 | 32 | 33 | try { 34 | _idJsEngine = await FlutterJs.initEngine(); 35 | } on PlatformException catch (e) { 36 | print('Failed to init js engine: ${e.details}'); 37 | } 38 | 39 | // If the widget was removed from the tree while the asynchronous platform 40 | // message was in flight, we want to discard the reply rather than calling 41 | // setState to update our non-existent appearance. 42 | if (!mounted) return; 43 | 44 | 45 | } 46 | 47 | @override 48 | Widget build(BuildContext context) { 49 | return MaterialApp( 50 | home: Scaffold( 51 | appBar: AppBar( 52 | title: const Text('FlutterJS Example'), 53 | ), 54 | body: Center( 55 | child: Column( 56 | mainAxisAlignment: MainAxisAlignment.center, 57 | children: [ 58 | Text('JS Evaluate Result: $_jsResult\n'), 59 | SizedBox(height: 20,), 60 | Padding(padding: EdgeInsets.all(10), child: Text('Click on the big JS Yellow Button to evaluate the expression bellow using the flutter_js plugin'),), 61 | Padding( 62 | padding: const EdgeInsets.all(8.0), 63 | child: Text("Math.trunc(Math.random() * 100).toString();", style: TextStyle(fontSize: 12, fontStyle: FontStyle.italic, fontWeight: FontWeight.bold),), 64 | ) 65 | ], 66 | ), 67 | ), 68 | floatingActionButton: FloatingActionButton( 69 | backgroundColor: Colors.transparent, 70 | child: Image.asset('assets/js.ico'), 71 | onPressed: () async { 72 | try { 73 | String result = await FlutterJs.evaluate( 74 | "Math.trunc(Math.random() * 100).toString();", _idJsEngine); 75 | setState(() { 76 | _jsResult = result; 77 | }); 78 | } on PlatformException catch (e) { 79 | print('ERRO: ${e.details}'); 80 | } 81 | }, 82 | ), 83 | ), 84 | ); 85 | } 86 | } 87 | 88 | ``` 89 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | android { 26 | namespace "io.abner.flutter_js_example" 27 | compileSdkVersion flutter.compileSdkVersion 28 | 29 | ndkVersion "27.0.12077973" 30 | 31 | 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | 37 | kotlinOptions { 38 | jvmTarget = JavaVersion.VERSION_1_8.toString() 39 | } 40 | 41 | sourceSets { 42 | main { 43 | java.srcDirs += 'src/main/kotlin' 44 | jniLibs.srcDirs = ['jniLibs'] 45 | } 46 | } 47 | 48 | defaultConfig { 49 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 50 | applicationId "io.abner.flutter_js_example" 51 | 52 | //minSdkVersion flutter.minSdkVersion 53 | minSdkVersion 21 54 | targetSdkVersion flutter.targetSdkVersion 55 | versionCode flutterVersionCode.toInteger() 56 | versionName flutterVersionName 57 | 58 | // externalNativeBuild { 59 | // cmake { 60 | // cppFlags "-std=c++14 -fexceptions -frtti" 61 | // arguments "-DANDROID_ARM_NEON=TRUE",'-DANDROID_STL=c++_shared' 62 | // } 63 | // } 64 | 65 | externalNativeBuild { 66 | 67 | cmake { 68 | 69 | cppFlags "" 70 | 71 | arguments "-DANDROID_STL=c++_shared", "-DANDROID_ARM_NEON=TRUE" 72 | 73 | } 74 | 75 | } 76 | } 77 | 78 | buildTypes { 79 | release { 80 | // TODO: Add your own signing config for the release build. 81 | // Signing with the debug keys for now, so `flutter run --release` works. 82 | signingConfig signingConfigs.debug 83 | 84 | minifyEnabled true 85 | shrinkResources true 86 | 87 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 88 | } 89 | } 90 | } 91 | 92 | flutter { 93 | source '../..' 94 | } 95 | 96 | dependencies { 97 | // implementation "com.github.fast-development.android-js-runtimes:fastdev-jsruntimes-jsc-intl:0.3.5" 98 | implementation "com.github.fast-development.android-js-runtimes:fastdev-jsruntimes-jsc:0.3.5" 99 | } 100 | -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | #Flutter Wrapper 2 | -keep class io.flutter.app.** { *; } 3 | -keep class io.flutter.plugin.** { *; } 4 | -keep class io.flutter.util.** { *; } 5 | -keep class io.flutter.view.** { *; } 6 | -keep class io.flutter.** { *; } 7 | -keep class io.flutter.plugins.** { *; } 8 | -keep class de.prosiebensat1digital.** { *; } -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/io/abner/flutter_js_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package io.abner.flutter_js_example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = '../build' 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(':app') 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M \ 2 | --add-exports=java.base/sun.nio.ch=ALL-UNNAMED \ 3 | --add-opens=java.base/java.lang=ALL-UNNAMED \ 4 | --add-opens=java.base/java.lang.reflect=ALL-UNNAMED \ 5 | --add-opens=java.base/java.io=ALL-UNNAMED \ 6 | --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED 7 | 8 | android.useAndroidX=true 9 | android.enableJetifier=true 10 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Feb 22 21:40:47 GMT 2025 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.7.3" apply false 22 | id "org.jetbrains.kotlin.android" version "2.1.10" apply false 23 | } 24 | 25 | include ":app" -------------------------------------------------------------------------------- /example/assets/js.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/assets/js.ico -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '11.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - flutter_js (0.1.0): 4 | - Flutter 5 | 6 | DEPENDENCIES: 7 | - Flutter (from `Flutter`) 8 | - flutter_js (from `.symlinks/plugins/flutter_js/ios`) 9 | 10 | EXTERNAL SOURCES: 11 | Flutter: 12 | :path: Flutter 13 | flutter_js: 14 | :path: ".symlinks/plugins/flutter_js/ios" 15 | 16 | SPEC CHECKSUMS: 17 | Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 18 | flutter_js: 95929d4e146e8ceb1c8e1889d8c2065c5d840076 19 | 20 | PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 21 | 22 | COCOAPODS: 1.12.1 23 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Flutter Js 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_js_example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | UIApplicationSupportsIndirectInputEvents 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/lib/ajv_result_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_js_example/json_viewer.dart'; 5 | 6 | class AjvResultScreen extends StatelessWidget { 7 | const AjvResultScreen(this.jsonString, {super.key, this.notRoot = false}); 8 | 9 | final String jsonString; 10 | final bool notRoot; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | appBar: AppBar(title: const Text('Ajv Result')), 16 | body: SafeArea( 17 | child: SingleChildScrollView( 18 | //child: JsonViewerWidget(json.decode(jsonString), notRoot: notRoot ) 19 | child: JsonViewerRoot( 20 | jsonObj: json.decode(jsonString), 21 | expandDeep: 4, 22 | )), 23 | )); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /example/lib/api_call.dart: -------------------------------------------------------------------------------- 1 | // import 'dart:async'; 2 | 3 | // import 'package:flutter_js/flutter_js.dart'; 4 | 5 | // // example to show how to an api call can be made and used a Future in dart 6 | // Future getUser() { 7 | // final completer = Completer(); 8 | // const code = """ 9 | // try { 10 | // const response = await fetch('https://reqres.in/api/users?page=2'); 11 | // const body = await response.json(); 12 | // if (response.status === 200) { 13 | // sendMessage('onRequestSuccess', JSON.stringify(body)); 14 | // } else { 15 | // sendMessage('onRequestFailure', JSON.stringify(body)); 16 | // } 17 | // } catch(e) { 18 | // console.log(e.message); 19 | // sendMessage('onError', e.message); 20 | // } 21 | // """; 22 | // final jsRuntime = getJavascriptRuntime(); 23 | // jsRuntime.onMessage('onRequestSuccess', completer.complete); 24 | // jsRuntime.onMessage('onRequestFailure', (args) { 25 | // completer.completeError(args); 26 | // }); 27 | // jsRuntime.onMessage('onError', (args) { 28 | // completer.completeError(args); 29 | // }); 30 | // return completer.future; 31 | // } 32 | -------------------------------------------------------------------------------- /example/lib/example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/services.dart'; 4 | 5 | class Example { 6 | static const MethodChannel _channel = MethodChannel('example'); 7 | 8 | static Future get platformVersion async { 9 | final String version = await _channel.invokeMethod('getPlatformVersion'); 10 | return version; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_js/flutter_js.dart'; 5 | import 'package:flutter_js_example/ajv_example.dart'; 6 | 7 | void main() { 8 | WidgetsFlutterBinding.ensureInitialized(); 9 | runApp(MyApp()); 10 | } 11 | 12 | class MyApp extends StatefulWidget { 13 | final GlobalKey scaffoldState = GlobalKey(); 14 | 15 | MyApp({super.key}); 16 | 17 | @override 18 | _MyAppState createState() => _MyAppState(); 19 | } 20 | 21 | class _MyAppState extends State { 22 | @override 23 | Widget build(BuildContext context) { 24 | return const MaterialApp( 25 | home: FlutterJsHomeScreen(), 26 | ); 27 | } 28 | } 29 | 30 | class FlutterJsHomeScreen extends StatefulWidget { 31 | const FlutterJsHomeScreen({super.key}); 32 | 33 | @override 34 | _FlutterJsHomeScreenState createState() => _FlutterJsHomeScreenState(); 35 | } 36 | 37 | class _FlutterJsHomeScreenState extends State { 38 | String _jsResult = ''; 39 | 40 | final JavascriptRuntime javascriptRuntime = 41 | getJavascriptRuntime(forceJavascriptCoreOnAndroid: false); 42 | 43 | String? _quickjsVersion; 44 | 45 | Future evalJS() async { 46 | JsEvalResult jsResult = await javascriptRuntime.evaluateAsync( 47 | """ 48 | if (typeof MyClass == 'undefined') { 49 | var MyClass = class { 50 | constructor(id) { 51 | this.id = id; 52 | } 53 | 54 | getId() { 55 | return this.id; 56 | } 57 | } 58 | } 59 | async function test() { 60 | var obj = new MyClass(1); 61 | var jsonStringified = JSON.stringify(obj); 62 | var value = Math.trunc(Math.random() * 100).toString(); 63 | var asyncResult = await sendMessage("getDataAsync", JSON.stringify({"count": Math.trunc(Math.random() * 10)})); 64 | var err; 65 | try { 66 | await sendMessage("asyncWithError", "{}"); 67 | } catch(e) { 68 | err = e.message || e; 69 | } 70 | return {"object": jsonStringified, "expression": value, "asyncResult": asyncResult, "expectedError": err}; 71 | } 72 | test(); 73 | """, 74 | sourceUrl: 'script.js', 75 | ); 76 | javascriptRuntime.executePendingJob(); 77 | JsEvalResult asyncResult = await javascriptRuntime.handlePromise(jsResult); 78 | return asyncResult.stringResult; 79 | } 80 | 81 | @override 82 | void initState() { 83 | super.initState(); 84 | javascriptRuntime.setInspectable(true); 85 | javascriptRuntime.onMessage('getDataAsync', (args) async { 86 | await Future.delayed(const Duration(seconds: 1)); 87 | final int count = args['count']; 88 | Random rnd = Random(); 89 | final result = >[]; 90 | for (int i = 0; i < count; i++) { 91 | result.add({'key$i': rnd.nextInt(100)}); 92 | } 93 | return result; 94 | }); 95 | javascriptRuntime.onMessage('asyncWithError', (_) async { 96 | await Future.delayed(const Duration(milliseconds: 100)); 97 | return Future.error('Some error'); 98 | }); 99 | } 100 | 101 | @override 102 | void dispose() { 103 | javascriptRuntime.dispose(); 104 | super.dispose(); 105 | } 106 | 107 | @override 108 | Widget build(BuildContext context) { 109 | return Scaffold( 110 | appBar: AppBar( 111 | title: const Text('FlutterJS Example'), 112 | ), 113 | body: Center( 114 | child: Column( 115 | mainAxisAlignment: MainAxisAlignment.center, 116 | children: [ 117 | Text( 118 | 'JS Evaluate Result:\n\n$_jsResult\n', 119 | textAlign: TextAlign.center, 120 | ), 121 | const SizedBox( 122 | height: 20, 123 | ), 124 | const Padding( 125 | padding: EdgeInsets.all(10), 126 | child: Text( 127 | 'Click on the big JS Yellow Button to evaluate the expression bellow using the flutter_js plugin'), 128 | ), 129 | const Padding( 130 | padding: EdgeInsets.all(8.0), 131 | child: Text( 132 | "Math.trunc(Math.random() * 100).toString();", 133 | style: TextStyle( 134 | fontSize: 12, 135 | fontStyle: FontStyle.italic, 136 | fontWeight: FontWeight.bold), 137 | ), 138 | ), 139 | ElevatedButton( 140 | onPressed: () => Navigator.of(context).push( 141 | MaterialPageRoute( 142 | builder: (ctx) => AjvExample( 143 | //widget.javascriptRuntime, 144 | javascriptRuntime), 145 | ), 146 | ), 147 | child: const Text('See Ajv Example'), 148 | ), 149 | SizedBox.fromSize(size: const Size(double.maxFinite, 20)), 150 | ElevatedButton( 151 | child: const Text('Fetch Remote Data'), 152 | onPressed: () async { 153 | var asyncResult = await javascriptRuntime.evaluateAsync(""" 154 | fetch('https://raw.githubusercontent.com/abner/flutter_js/master/FIXED_RESOURCE.txt').then(response => response.text()); 155 | """); 156 | javascriptRuntime.executePendingJob(); 157 | final promiseResolved = 158 | await javascriptRuntime.handlePromise(asyncResult); 159 | var result = promiseResolved.stringResult; 160 | setState(() => _quickjsVersion = result); 161 | }, 162 | ), 163 | Text( 164 | 'QuickJS Version\n${_quickjsVersion ?? ''}', 165 | textAlign: TextAlign.center, 166 | ) 167 | ], 168 | ), 169 | ), 170 | floatingActionButton: FloatingActionButton( 171 | //backgroundColor: Colors.transparent, 172 | child: Image.asset('assets/js.ico'), 173 | onPressed: () async { 174 | final result = await evalJS(); 175 | if (!mounted) return; 176 | setState(() { 177 | _jsResult = result; 178 | }); 179 | }, 180 | ), 181 | ); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /example/linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /example/linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(runner LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "flutter_js_example") 5 | set(APPLICATION_ID "com.example.flutter_js2") 6 | 7 | cmake_policy(SET CMP0063 NEW) 8 | 9 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 10 | 11 | # Root filesystem for cross-building. 12 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 13 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 14 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 17 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 18 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 19 | endif() 20 | 21 | # Configure build options. 22 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 23 | set(CMAKE_BUILD_TYPE "Debug" CACHE 24 | STRING "Flutter build mode" FORCE) 25 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 26 | "Debug" "Profile" "Release") 27 | endif() 28 | 29 | # Compilation settings that should be applied to most targets. 30 | function(APPLY_STANDARD_SETTINGS TARGET) 31 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 32 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 33 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 34 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 35 | endfunction() 36 | 37 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 38 | 39 | # Flutter library and tool build rules. 40 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 41 | 42 | # System-level dependencies. 43 | find_package(PkgConfig REQUIRED) 44 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 45 | 46 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 47 | 48 | # Application build 49 | add_executable(${BINARY_NAME} 50 | "main.cc" 51 | "my_application.cc" 52 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 53 | ) 54 | apply_standard_settings(${BINARY_NAME}) 55 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 56 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 57 | add_dependencies(${BINARY_NAME} flutter_assemble) 58 | # Only the install-generated bundle's copy of the executable will launch 59 | # correctly, since the resources must in the right relative locations. To avoid 60 | # people trying to run the unbundled copy, put it in a subdirectory instead of 61 | # the default top-level location. 62 | set_target_properties(${BINARY_NAME} 63 | PROPERTIES 64 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 65 | ) 66 | 67 | # Generated plugin build rules, which manage building the plugins and adding 68 | # them to the application. 69 | include(flutter/generated_plugins.cmake) 70 | 71 | 72 | # === Installation === 73 | # By default, "installing" just makes a relocatable bundle in the build 74 | # directory. 75 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 76 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 77 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 78 | endif() 79 | 80 | # Start with a clean build bundle directory every time. 81 | install(CODE " 82 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 83 | " COMPONENT Runtime) 84 | 85 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 86 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 87 | 88 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 89 | COMPONENT Runtime) 90 | 91 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 92 | COMPONENT Runtime) 93 | 94 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 95 | COMPONENT Runtime) 96 | 97 | if(PLUGIN_BUNDLED_LIBRARIES) 98 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 99 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 100 | COMPONENT Runtime) 101 | endif() 102 | 103 | # Fully re-copy the assets directory on each build to avoid having stale files 104 | # from a previous install. 105 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 106 | install(CODE " 107 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 108 | " COMPONENT Runtime) 109 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 110 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 111 | 112 | # Install the AOT library on non-Debug builds only. 113 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 114 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 115 | COMPONENT Runtime) 116 | endif() 117 | -------------------------------------------------------------------------------- /example/linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | 11 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 12 | # which isn't available in 3.10. 13 | function(list_prepend LIST_NAME PREFIX) 14 | set(NEW_LIST "") 15 | foreach(element ${${LIST_NAME}}) 16 | list(APPEND NEW_LIST "${PREFIX}${element}") 17 | endforeach(element) 18 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 19 | endfunction() 20 | 21 | # === Flutter Library === 22 | # System-level dependencies. 23 | find_package(PkgConfig REQUIRED) 24 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 25 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 26 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 27 | 28 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 29 | 30 | # Published to parent scope for install step. 31 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 32 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 33 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 34 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 35 | 36 | list(APPEND FLUTTER_LIBRARY_HEADERS 37 | "fl_basic_message_channel.h" 38 | "fl_binary_codec.h" 39 | "fl_binary_messenger.h" 40 | "fl_dart_project.h" 41 | "fl_engine.h" 42 | "fl_json_message_codec.h" 43 | "fl_json_method_codec.h" 44 | "fl_message_codec.h" 45 | "fl_method_call.h" 46 | "fl_method_channel.h" 47 | "fl_method_codec.h" 48 | "fl_method_response.h" 49 | "fl_plugin_registrar.h" 50 | "fl_plugin_registry.h" 51 | "fl_standard_message_codec.h" 52 | "fl_standard_method_codec.h" 53 | "fl_string_codec.h" 54 | "fl_value.h" 55 | "fl_view.h" 56 | "flutter_linux.h" 57 | ) 58 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 59 | add_library(flutter INTERFACE) 60 | target_include_directories(flutter INTERFACE 61 | "${EPHEMERAL_DIR}" 62 | ) 63 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 64 | target_link_libraries(flutter INTERFACE 65 | PkgConfig::GTK 66 | PkgConfig::GLIB 67 | PkgConfig::GIO 68 | ) 69 | add_dependencies(flutter flutter_assemble) 70 | 71 | # === Flutter tool backend === 72 | # _phony_ is a non-existent file to force this command to run every time, 73 | # since currently there's no way to get a full input/output list from the 74 | # flutter tool. 75 | add_custom_command( 76 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 77 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 78 | COMMAND ${CMAKE_COMMAND} -E env 79 | ${FLUTTER_TOOL_ENVIRONMENT} 80 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 81 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 82 | VERBATIM 83 | ) 84 | add_custom_target(flutter_assemble DEPENDS 85 | "${FLUTTER_LIBRARY}" 86 | ${FLUTTER_LIBRARY_HEADERS} 87 | ) 88 | -------------------------------------------------------------------------------- /example/linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /example/linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen* screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "flutter_js_example"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } else { 47 | gtk_window_set_title(window, "flutter_js_example"); 48 | } 49 | 50 | gtk_window_set_default_size(window, 1280, 720); 51 | gtk_widget_show(GTK_WIDGET(window)); 52 | 53 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 54 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 55 | 56 | FlView* view = fl_view_new(project); 57 | gtk_widget_show(GTK_WIDGET(view)); 58 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 59 | 60 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 61 | 62 | gtk_widget_grab_focus(GTK_WIDGET(view)); 63 | } 64 | 65 | // Implements GApplication::local_command_line. 66 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 67 | MyApplication* self = MY_APPLICATION(application); 68 | // Strip out the first argument as it is the binary name. 69 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 70 | 71 | g_autoptr(GError) error = nullptr; 72 | if (!g_application_register(application, nullptr, &error)) { 73 | g_warning("Failed to register: %s", error->message); 74 | *exit_status = 1; 75 | return TRUE; 76 | } 77 | 78 | g_application_activate(application); 79 | *exit_status = 0; 80 | 81 | return TRUE; 82 | } 83 | 84 | // Implements GObject::dispose. 85 | static void my_application_dispose(GObject* object) { 86 | MyApplication* self = MY_APPLICATION(object); 87 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 88 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 89 | } 90 | 91 | static void my_application_class_init(MyApplicationClass* klass) { 92 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 93 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 94 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 95 | } 96 | 97 | static void my_application_init(MyApplication* self) {} 98 | 99 | MyApplication* my_application_new() { 100 | return MY_APPLICATION(g_object_new(my_application_get_type(), 101 | "application-id", APPLICATION_ID, 102 | "flags", G_APPLICATION_NON_UNIQUE, 103 | nullptr)); 104 | } 105 | -------------------------------------------------------------------------------- /example/linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/xcuserdata/ 7 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | end 35 | 36 | post_install do |installer| 37 | installer.pods_project.targets.each do |target| 38 | flutter_additional_macos_build_settings(target) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /example/macos/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - flutter_js (0.0.1): 3 | - FlutterMacOS 4 | - FlutterMacOS (1.0.0) 5 | 6 | DEPENDENCIES: 7 | - flutter_js (from `Flutter/ephemeral/.symlinks/plugins/flutter_js/macos`) 8 | - FlutterMacOS (from `Flutter/ephemeral`) 9 | 10 | EXTERNAL SOURCES: 11 | flutter_js: 12 | :path: Flutter/ephemeral/.symlinks/plugins/flutter_js/macos 13 | FlutterMacOS: 14 | :path: Flutter/ephemeral 15 | 16 | SPEC CHECKSUMS: 17 | flutter_js: 79c3c70d33ba464c67d8eb88639a61aa718cd8b8 18 | FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 19 | 20 | PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 21 | 22 | COCOAPODS: 1.16.2 23 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 53 | 55 | 61 | 62 | 63 | 64 | 65 | 66 | 72 | 74 | 80 | 81 | 82 | 83 | 85 | 86 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @main 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | 10 | override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { 11 | return true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /example/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = flutter_js_example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = io.abner.flutterJsExample 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2021 io.abner. All rights reserved. 15 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /example/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.client 10 | 11 | com.apple.security.network.server 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /example/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_js_example 2 | description: Demonstrates how to use the flutter_js plugin. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 7 | 8 | environment: 9 | sdk: ">=3.0.0 <4.0.0" 10 | flutter: ">=3.3.0 <4.0.0" 11 | 12 | # Dependencies specify other packages that your package needs in order to work. 13 | # To automatically upgrade your package dependencies to the latest versions 14 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 15 | # dependencies can be manually updated by changing the version numbers below to 16 | # the latest version available on pub.dev. To see which dependencies have newer 17 | # versions available, run `flutter pub outdated`. 18 | dependencies: 19 | flutter: 20 | sdk: flutter 21 | 22 | flutter_js: 23 | # When depending on this package from a real application you should use: 24 | # flutter_js2: ^x.y.z 25 | # See https://dart.dev/tools/pub/dependencies#version-constraints 26 | # The example app is bundled with the plugin so we use a path dependency on 27 | # the parent directory to use the current plugin's version. 28 | path: ../ 29 | 30 | # The following adds the Cupertino Icons font to your application. 31 | # Use with the CupertinoIcons class for iOS style icons. 32 | cupertino_icons: ^1.0.2 33 | 34 | dev_dependencies: 35 | flutter_test: 36 | sdk: flutter 37 | 38 | # The "flutter_lints" package below contains a set of recommended lints to 39 | # encourage good coding practices. The lint set provided by the package is 40 | # activated in the `analysis_options.yaml` file located at the root of your 41 | # package. See that file for information about deactivating specific lint 42 | # rules and activating additional ones. 43 | flutter_lints: ^1.0.0 44 | 45 | # For information on the generic Dart part of this file, see the 46 | # following page: https://dart.dev/tools/pub/pubspec 47 | 48 | # The following section is specific to Flutter. 49 | flutter: 50 | # The following line ensures that the Material Icons font is 51 | # included with your application, so that you can use the icons in 52 | # the material Icons class. 53 | uses-material-design: true 54 | 55 | # To add assets to your application, add an assets section, like this: 56 | assets: 57 | - assets/ 58 | - assets/js/ 59 | 60 | # An image asset can refer to one or more resolution-specific "variants", see 61 | # https://flutter.dev/assets-and-images/#resolution-aware. 62 | 63 | # For details regarding adding assets from package dependencies, see 64 | # https://flutter.dev/assets-and-images/#from-packages 65 | 66 | # To add custom fonts to your application, add a fonts section here, 67 | # in this "flutter" section. Each entry in this list should have a 68 | # "family" key with the font family name, and a "fonts" key with a 69 | # list giving the asset and other descriptors for the font. For 70 | # example: 71 | # fonts: 72 | # - family: Schyler 73 | # fonts: 74 | # - asset: fonts/Schyler-Regular.ttf 75 | # - asset: fonts/Schyler-Italic.ttf 76 | # style: italic 77 | # - family: Trajan Pro 78 | # fonts: 79 | # - asset: fonts/TrajanPro.ttf 80 | # - asset: fonts/TrajanPro_Bold.ttf 81 | # weight: 700 82 | # 83 | # For details regarding fonts from package dependencies, 84 | # see https://flutter.dev/custom-fonts/#from-packages 85 | -------------------------------------------------------------------------------- /example/test/flutter_js_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter_js/flutter_js.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | 5 | void main() { 6 | TestWidgetsFlutterBinding.ensureInitialized(); 7 | 8 | late JavascriptRuntime jsRuntime; 9 | 10 | setUp(() { 11 | jsRuntime = getJavascriptRuntime(); 12 | }); 13 | 14 | tearDown(() { 15 | try { 16 | jsRuntime.dispose(); 17 | } on Error catch (_) {} 18 | }); 19 | 20 | test('evaluate javascript', () { 21 | final result = jsRuntime.evaluate('Math.pow(5,3)'); 22 | if (kDebugMode) { 23 | print('${result.rawResult}, ${result.stringResult}'); 24 | print( 25 | '${result.rawResult.runtimeType}, ${result.stringResult.runtimeType}'); 26 | } 27 | expect(result.rawResult, equals(125)); 28 | expect(result.stringResult, equals('125')); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /example/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /example/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(example LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "example") 5 | 6 | cmake_policy(SET CMP0063 NEW) 7 | 8 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 9 | 10 | # Configure build options. 11 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 12 | if(IS_MULTICONFIG) 13 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 14 | CACHE STRING "" FORCE) 15 | else() 16 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 17 | set(CMAKE_BUILD_TYPE "Debug" CACHE 18 | STRING "Flutter build mode" FORCE) 19 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 20 | "Debug" "Profile" "Release") 21 | endif() 22 | endif() 23 | 24 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 25 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 26 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 27 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 28 | 29 | # Use Unicode for all projects. 30 | add_definitions(-DUNICODE -D_UNICODE) 31 | 32 | # Compilation settings that should be applied to most targets. 33 | function(APPLY_STANDARD_SETTINGS TARGET) 34 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 35 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 36 | target_compile_options(${TARGET} PRIVATE /EHsc) 37 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 38 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 39 | endfunction() 40 | 41 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 42 | 43 | # Flutter library and tool build rules. 44 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 45 | 46 | # Application build 47 | add_subdirectory("runner") 48 | 49 | # Generated plugin build rules, which manage building the plugins and adding 50 | # them to the application. 51 | include(flutter/generated_plugins.cmake) 52 | 53 | 54 | # === Installation === 55 | # Support files are copied into place next to the executable, so that it can 56 | # run in place. This is done instead of making a separate bundle (as on Linux) 57 | # so that building and running from within Visual Studio will work. 58 | set(BUILD_BUNDLE_DIR "$") 59 | # Make the "install" step default, as it's required to run. 60 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 61 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 62 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 63 | endif() 64 | 65 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 66 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 67 | 68 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 69 | COMPONENT Runtime) 70 | 71 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 72 | COMPONENT Runtime) 73 | 74 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 75 | COMPONENT Runtime) 76 | 77 | if(PLUGIN_BUNDLED_LIBRARIES) 78 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 79 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 80 | COMPONENT Runtime) 81 | endif() 82 | 83 | # Fully re-copy the assets directory on each build to avoid having stale files 84 | # from a previous install. 85 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 86 | install(CODE " 87 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 88 | " COMPONENT Runtime) 89 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 90 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 91 | 92 | # Install the AOT library on non-Debug builds only. 93 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 94 | CONFIGURATIONS Profile;Release 95 | COMPONENT Runtime) 96 | -------------------------------------------------------------------------------- /example/windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 11 | 12 | # === Flutter Library === 13 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 14 | 15 | # Published to parent scope for install step. 16 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 17 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 18 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 19 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 20 | 21 | list(APPEND FLUTTER_LIBRARY_HEADERS 22 | "flutter_export.h" 23 | "flutter_windows.h" 24 | "flutter_messenger.h" 25 | "flutter_plugin_registrar.h" 26 | "flutter_texture_registrar.h" 27 | ) 28 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 29 | add_library(flutter INTERFACE) 30 | target_include_directories(flutter INTERFACE 31 | "${EPHEMERAL_DIR}" 32 | ) 33 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 34 | add_dependencies(flutter flutter_assemble) 35 | 36 | # === Wrapper === 37 | list(APPEND CPP_WRAPPER_SOURCES_CORE 38 | "core_implementations.cc" 39 | "standard_codec.cc" 40 | ) 41 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 42 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 43 | "plugin_registrar.cc" 44 | ) 45 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 46 | list(APPEND CPP_WRAPPER_SOURCES_APP 47 | "flutter_engine.cc" 48 | "flutter_view_controller.cc" 49 | ) 50 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 51 | 52 | # Wrapper sources needed for a plugin. 53 | add_library(flutter_wrapper_plugin STATIC 54 | ${CPP_WRAPPER_SOURCES_CORE} 55 | ${CPP_WRAPPER_SOURCES_PLUGIN} 56 | ) 57 | apply_standard_settings(flutter_wrapper_plugin) 58 | set_target_properties(flutter_wrapper_plugin PROPERTIES 59 | POSITION_INDEPENDENT_CODE ON) 60 | set_target_properties(flutter_wrapper_plugin PROPERTIES 61 | CXX_VISIBILITY_PRESET hidden) 62 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 63 | target_include_directories(flutter_wrapper_plugin PUBLIC 64 | "${WRAPPER_ROOT}/include" 65 | ) 66 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 67 | 68 | # Wrapper sources needed for the runner. 69 | add_library(flutter_wrapper_app STATIC 70 | ${CPP_WRAPPER_SOURCES_CORE} 71 | ${CPP_WRAPPER_SOURCES_APP} 72 | ) 73 | apply_standard_settings(flutter_wrapper_app) 74 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 75 | target_include_directories(flutter_wrapper_app PUBLIC 76 | "${WRAPPER_ROOT}/include" 77 | ) 78 | add_dependencies(flutter_wrapper_app flutter_assemble) 79 | 80 | # === Flutter tool backend === 81 | # _phony_ is a non-existent file to force this command to run every time, 82 | # since currently there's no way to get a full input/output list from the 83 | # flutter tool. 84 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 85 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 86 | add_custom_command( 87 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 88 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 89 | ${CPP_WRAPPER_SOURCES_APP} 90 | ${PHONY_OUTPUT} 91 | COMMAND ${CMAKE_COMMAND} -E env 92 | ${FLUTTER_TOOL_ENVIRONMENT} 93 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 94 | windows-x64 $ 95 | VERBATIM 96 | ) 97 | add_custom_target(flutter_assemble DEPENDS 98 | "${FLUTTER_LIBRARY}" 99 | ${FLUTTER_LIBRARY_HEADERS} 100 | ${CPP_WRAPPER_SOURCES_CORE} 101 | ${CPP_WRAPPER_SOURCES_PLUGIN} 102 | ${CPP_WRAPPER_SOURCES_APP} 103 | ) 104 | -------------------------------------------------------------------------------- /example/windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | add_executable(${BINARY_NAME} WIN32 5 | "flutter_window.cpp" 6 | "main.cpp" 7 | "utils.cpp" 8 | "win32_window.cpp" 9 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 10 | "Runner.rc" 11 | "runner.exe.manifest" 12 | ) 13 | apply_standard_settings(${BINARY_NAME}) 14 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 15 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 16 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 17 | add_dependencies(${BINARY_NAME} flutter_assemble) 18 | -------------------------------------------------------------------------------- /example/windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #ifdef FLUTTER_BUILD_NUMBER 64 | #define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0 67 | #endif 68 | 69 | #ifdef FLUTTER_BUILD_NAME 70 | #define VERSION_AS_STRING #FLUTTER_BUILD_NAME 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "io.abner" "\0" 93 | VALUE "FileDescription", "example" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "example" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2022 io.abner. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "example.exe" "\0" 98 | VALUE "ProductName", "example" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | return true; 30 | } 31 | 32 | void FlutterWindow::OnDestroy() { 33 | if (flutter_controller_) { 34 | flutter_controller_ = nullptr; 35 | } 36 | 37 | Win32Window::OnDestroy(); 38 | } 39 | 40 | LRESULT 41 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 42 | WPARAM const wparam, 43 | LPARAM const lparam) noexcept { 44 | // Give Flutter, including plugins, an opportunity to handle window messages. 45 | if (flutter_controller_) { 46 | std::optional result = 47 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 48 | lparam); 49 | if (result) { 50 | return *result; 51 | } 52 | } 53 | 54 | switch (message) { 55 | case WM_FONTCHANGE: 56 | flutter_controller_->engine()->ReloadSystemFonts(); 57 | break; 58 | } 59 | 60 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 61 | } 62 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /example/windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.CreateAndShow(L"example", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /example/windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /example/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/example/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /example/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr); 51 | if (target_length == 0) { 52 | return std::string(); 53 | } 54 | std::string utf8_string; 55 | utf8_string.resize(target_length); 56 | int converted_length = ::WideCharToMultiByte( 57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 58 | -1, utf8_string.data(), 59 | target_length, nullptr, nullptr); 60 | if (converted_length == 0) { 61 | return std::string(); 62 | } 63 | return utf8_string; 64 | } 65 | -------------------------------------------------------------------------------- /example/windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /example/windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates and shows a win32 window with |title| and position and size using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size to will treat the width height passed in to this function 35 | // as logical pixels and scale to appropriate for the default monitor. Returns 36 | // true if the window was created successfully. 37 | bool CreateAndShow(const std::wstring& title, 38 | const Point& origin, 39 | const Size& size); 40 | 41 | // Release OS resources associated with window. 42 | void Destroy(); 43 | 44 | // Inserts |content| into the window tree. 45 | void SetChildContent(HWND content); 46 | 47 | // Returns the backing Window handle to enable clients to set icon and other 48 | // window properties. Returns nullptr if the window has been destroyed. 49 | HWND GetHandle(); 50 | 51 | // If true, closing this window will quit the application. 52 | void SetQuitOnClose(bool quit_on_close); 53 | 54 | // Return a RECT representing the bounds of the current client area. 55 | RECT GetClientArea(); 56 | 57 | protected: 58 | // Processes and route salient window messages for mouse handling, 59 | // size change and DPI. Delegates handling of these to member overloads that 60 | // inheriting classes can handle. 61 | virtual LRESULT MessageHandler(HWND window, 62 | UINT const message, 63 | WPARAM const wparam, 64 | LPARAM const lparam) noexcept; 65 | 66 | // Called when CreateAndShow is called, allowing subclass window-related 67 | // setup. Subclasses should return false if setup fails. 68 | virtual bool OnCreate(); 69 | 70 | // Called when Destroy is called. 71 | virtual void OnDestroy(); 72 | 73 | private: 74 | friend class WindowClassRegistrar; 75 | 76 | // OS callback called by message pump. Handles the WM_NCCREATE message which 77 | // is passed when the non-client area is being created and enables automatic 78 | // non-client DPI scaling so that the non-client area automatically 79 | // responsponds to changes in DPI. All other messages are handled by 80 | // MessageHandler. 81 | static LRESULT CALLBACK WndProc(HWND const window, 82 | UINT const message, 83 | WPARAM const wparam, 84 | LPARAM const lparam) noexcept; 85 | 86 | // Retrieves a class instance pointer for |window| 87 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 88 | 89 | bool quit_on_close_ = false; 90 | 91 | // window handle for top level window. 92 | HWND window_handle_ = nullptr; 93 | 94 | // window handle for hosted content. 95 | HWND child_content_ = nullptr; 96 | }; 97 | 98 | #endif // RUNNER_WIN32_WINDOW_H_ 99 | -------------------------------------------------------------------------------- /ios.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/ios.mp4 -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/Console.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JavaScriptCore 3 | 4 | @objc enum ConsoleLevel : Int { 5 | case log, info, warn, error 6 | } 7 | @objc protocol ConsoleJSExport : JSExport { 8 | func output(_ level: ConsoleLevel, arguments: [AnyObject]) 9 | } 10 | 11 | @objc class Console : NSObject, ConsoleJSExport, JSInsert { 12 | let levelDictionary : [ConsoleLevel : String] = [ 13 | .log: "", 14 | .info: "[info]", 15 | .warn: "[WARN]", 16 | .error: "[ERROR]" 17 | ] 18 | 19 | func output(_ level: ConsoleLevel, arguments: [AnyObject]) { 20 | var levelOutput = levelDictionary[level] 21 | if levelOutput == nil { 22 | levelOutput = "" 23 | } 24 | 25 | print("[JSCotextFoundation]" + levelOutput!, terminator: " ") 26 | for argument in arguments { 27 | print(argument, terminator: " ") 28 | } 29 | 30 | 31 | // new line 32 | print("") 33 | } 34 | 35 | func insert(_ jsContext: JSContext) { 36 | jsContext.setObject(self, forKeyedSubscript:"$console" as (NSCopying & NSObjectProtocol)) 37 | jsContext.evaluateScript( 38 | "var console = {" + 39 | "log: function() { $console.outputArguments(0, arguments); }," + 40 | "info: function() { $console.outputArguments(1, arguments); }," + 41 | "warn: function() { $console.outputArguments(2, arguments); }," + 42 | "error: function() { $console.outputArguments(3, arguments); }" + 43 | "};" + 44 | "if (global) { global.console = console; }" 45 | ) 46 | } 47 | } -------------------------------------------------------------------------------- /ios/Classes/FlutterJsPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface FlutterJsPlugin : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /ios/Classes/FlutterJsPlugin.m: -------------------------------------------------------------------------------- 1 | #import "FlutterJsPlugin.h" 2 | #if __has_include() 3 | #import 4 | #else 5 | // Support project import fallback if the generated compatibility header 6 | // is not copied when this plugin is created as a library. 7 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 8 | #import "flutter_js-Swift.h" 9 | #endif 10 | 11 | @implementation FlutterJsPlugin 12 | + (void)registerWithRegistrar:(NSObject*)registrar { 13 | [SwiftFlutterJsPlugin registerWithRegistrar:registrar]; 14 | } 15 | @end 16 | -------------------------------------------------------------------------------- /ios/Classes/Global.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JavaScriptCore 3 | 4 | @objc protocol GlobalJSExport : JSExport { 5 | } 6 | 7 | @objc class Global : NSObject, GlobalJSExport, JSInsert { 8 | func insert(_ jsContext: JSContext) { 9 | jsContext.setObject(self, forKeyedSubscript:"global" as (NSCopying & NSObjectProtocol)) 10 | jsContext.evaluateScript("var window = global;") 11 | } 12 | } -------------------------------------------------------------------------------- /ios/Classes/JSContextFoundation.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JavaScriptCore 3 | 4 | public enum JSContextFoundationError: Error { 5 | case fileNotFound 6 | case fileNotLoaded 7 | case fileNotDownloaded 8 | } 9 | 10 | open class JSContextFoundation : JSContext { 11 | public override init!(virtualMachine: JSVirtualMachine!) { 12 | super.init(virtualMachine: virtualMachine) 13 | 14 | exceptionHandler = { context, exception in 15 | let exceptionDictionary = exception?.toDictionary() 16 | print("[JSCotextFoundation][Exception] \(String(describing: exception)) at line \(String(describing: exceptionDictionary?["line"])):\(String(describing: exceptionDictionary?["column"]))") 17 | } 18 | 19 | insert() 20 | } 21 | 22 | public convenience override init!() { 23 | self.init(virtualMachine: JSVirtualMachine()) 24 | } 25 | 26 | open func requireWithPath(_ path: String) throws { 27 | guard FileManager.default.fileExists(atPath: path) else { 28 | throw JSContextFoundationError.fileNotFound 29 | } 30 | guard let script = try? String(contentsOfFile: path, encoding: String.Encoding.utf8) else { 31 | throw JSContextFoundationError.fileNotLoaded 32 | } 33 | 34 | evaluateScript(script) 35 | } 36 | 37 | open func requireWithUrl(_ url: URL, completionHandler: @escaping (Error?) -> Void) { 38 | let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) -> Void in 39 | if let error = error { 40 | completionHandler(error) 41 | } 42 | else { 43 | guard let httpResponse = response as? HTTPURLResponse else { 44 | completionHandler(JSContextFoundationError.fileNotDownloaded) 45 | return 46 | } 47 | 48 | switch httpResponse.statusCode { 49 | case 404: 50 | completionHandler(JSContextFoundationError.fileNotFound) 51 | default: 52 | guard let data = data, let script = String(data: data, encoding: String.Encoding.utf8) as String? else { 53 | completionHandler(JSContextFoundationError.fileNotDownloaded) 54 | return 55 | } 56 | 57 | self.evaluateScript(script) 58 | completionHandler(nil) 59 | } 60 | } 61 | }) 62 | 63 | task.resume() 64 | } 65 | 66 | fileprivate func insert() { 67 | let jsInsertArray: [JSInsert] = [Global(), Console()] 68 | for jsInsert in jsInsertArray { 69 | jsInsert.insert(self) 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /ios/Classes/JSInsert.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JavaScriptCore 3 | 4 | @objc protocol JSInsert { 5 | func insert(_ jsContext: JSContext) 6 | } -------------------------------------------------------------------------------- /ios/Classes/SwiftFlutterJsPlugin.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import JavaScriptCore 3 | import UIKit 4 | 5 | public class SwiftFlutterJsPlugin: NSObject, FlutterPlugin { 6 | private var jsEngineMap = [Int: JSContextFoundation]() 7 | public static func register(with registrar: FlutterPluginRegistrar) { 8 | let channel = FlutterMethodChannel(name: "io.abner.flutter_js", binaryMessenger: registrar.messenger()) 9 | let instance = SwiftFlutterJsPlugin() 10 | registrar.addMethodCallDelegate(instance, channel: channel) 11 | } 12 | 13 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 14 | switch call.method { 15 | case "initEngine": 16 | let engineId = call.arguments as! Int 17 | jsEngineMap[engineId] = JSContextFoundation() 18 | result(engineId) 19 | case "evaluate": 20 | let argsMap = call.arguments as! NSDictionary 21 | let command: String = argsMap.value(forKey: "command") as! String 22 | let engineId: Int = argsMap.value(forKey: "engineId") as! Int 23 | let convertTo: String = argsMap.value(forKey: "convertTo") as! String 24 | 25 | if let jsEngine = jsEngineMap[engineId] { 26 | jsEngine.exceptionHandler = { _, exception in 27 | let exceptionDictionary = exception?.toDictionary() 28 | print("[JSCotextFoundation][Exception] \(String(describing: exception)) at line \(String(describing: exceptionDictionary?["line"])):\(String(describing: exceptionDictionary?["column"]))") 29 | result(FlutterError(code: "EvaluateError", 30 | message: String(describing: exception), 31 | details: nil)) 32 | } 33 | 34 | let resultJsValue: JSValue = jsEngine.evaluateScript(command) 35 | if convertTo != nil { 36 | if convertTo == "array" { 37 | result(resultJsValue.toArray()) 38 | } else if convertTo == "object" { 39 | result(resultJsValue.toDictionary()) 40 | } else { 41 | result(resultJsValue.toString()) 42 | } 43 | } else { 44 | result(resultJsValue.toString()) 45 | } 46 | 47 | } else { 48 | result(FlutterError(code: "EvaluateError", 49 | message: "jsEngine was not found", 50 | details: nil)) 51 | } 52 | case "close": 53 | result(FlutterMethodNotImplemented) 54 | break 55 | default: 56 | result(FlutterMethodNotImplemented) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ios/flutter_js.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint flutter_js.podspec' to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'flutter_js' 7 | s.version = '0.1.0' 8 | s.summary = 'A Javascript engine to use with flutter. It uses quickjs on Android and JavascriptCore on IOS' 9 | s.description = <<-DESC 10 | A Javascript engine to use with flutter. It uses quickjs on Android and JavascriptCore on IOS 11 | DESC 12 | s.homepage = 'http://example.com' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'Your Company' => 'email@example.com' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.dependency 'Flutter' 18 | s.platform = :ios, '8.0' 19 | s.frameworks = 'JavaScriptCore' 20 | # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. 21 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } 22 | s.swift_version = '5.0' 23 | end 24 | -------------------------------------------------------------------------------- /lib/extensions/fetch.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart' show rootBundle; 2 | import 'package:flutter_js/javascript_runtime.dart'; 3 | import './xhr.dart'; 4 | 5 | var _fetchDebug = false; 6 | 7 | setFetchDebug(bool value) => _fetchDebug = value; 8 | 9 | extension JavascriptRuntimeFetchExtension on JavascriptRuntime { 10 | Future enableFetch() async { 11 | debug('Before enable xhr'); 12 | enableXhr(); 13 | debug('After enable xhr'); 14 | final fetchPolyfill = 15 | await rootBundle.loadString('packages/flutter_js/assets/js/fetch.js'); 16 | debug('Loaded fetchPolyfill'); 17 | final evalFetchResult = evaluate(fetchPolyfill); 18 | debug('Eval Fetch Result: $evalFetchResult'); 19 | return this; 20 | } 21 | } 22 | 23 | void debug(String message) { 24 | if (_fetchDebug) { 25 | print('JavascriptRuntimeFetchExtension: $message'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/flutter_js.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_js/javascript_runtime.dart'; 6 | import 'package:flutter_js/javascriptcore/jscore_runtime.dart'; 7 | 8 | import './extensions/fetch.dart'; 9 | import './extensions/handle_promises.dart'; 10 | import './quickjs/quickjs_runtime2.dart'; 11 | 12 | export './extensions/handle_promises.dart'; 13 | //import 'package:flutter_js/quickjs-sync-server/quickjs_oasis_jsbridge.dart'; 14 | //import 'package:flutter_js/quickjs/quickjs_runtime.dart'; 15 | 16 | export './quickjs/quickjs_runtime.dart'; 17 | export './quickjs/quickjs_runtime2.dart'; 18 | export 'javascript_runtime.dart'; 19 | export 'js_eval_result.dart'; 20 | export 'quickjs-sync-server/quickjs_oasis_jsbridge.dart'; 21 | 22 | // import condicional to not import ffi libraries when using web as target 23 | // import "something.dart" if (dart.library.io) "other.dart"; 24 | // REF: 25 | // - https://medium.com/flutter-community/conditional-imports-across-flutter-and-web-4b88885a886e 26 | // - https://github.com/creativecreatorormaybenot/wakelock/blob/master/wakelock/lib/wakelock.dart 27 | JavascriptRuntime getJavascriptRuntime({ 28 | bool forceJavascriptCoreOnAndroid = false, 29 | bool xhr = true, 30 | Map? extraArgs = const {}, 31 | }) { 32 | JavascriptRuntime runtime; 33 | if ((Platform.isAndroid && !forceJavascriptCoreOnAndroid)) { 34 | int stackSize = extraArgs?['stackSize'] ?? 1024 * 1024; 35 | runtime = QuickJsRuntime2(stackSize: stackSize); 36 | // FlutterJs engine = FlutterJs(); 37 | // runtime = QuickJsService(engine); 38 | } else if (Platform.isWindows) { 39 | runtime = QuickJsRuntime2(); 40 | } else if (Platform.isLinux) { 41 | // runtime = FlutterJsLinuxWin()..init(); 42 | //runtime = JavascriptCoreRuntime(); //('f1.js'); 43 | runtime = QuickJsRuntime2(); 44 | } else { 45 | runtime = JavascriptCoreRuntime(); 46 | } 47 | if (xhr) runtime.enableFetch(); 48 | runtime.enableHandlePromises(); 49 | return runtime; 50 | } 51 | 52 | // JavascriptRuntime getJavascriptRuntime({bool xhr = true}) { 53 | // JavascriptRuntime runtime = JavascriptCoreRuntime(); 54 | // // setFetchDebug(true); 55 | // if (xhr) runtime.enableFetch(); 56 | // runtime.enableHandlePromises(); 57 | // return runtime; 58 | // } 59 | 60 | final Map _engineMap = {}; 61 | 62 | MethodChannel _methodChannel = const MethodChannel('io.abner.flutter_js') 63 | ..setMethodCallHandler((MethodCall call) { 64 | if (call.method == "sendMessage") { 65 | final engineId = call.arguments[0] as int?; 66 | final channel = call.arguments[1] as String?; 67 | final message = call.arguments[2] as String?; 68 | 69 | if (_engineMap[engineId] != null) { 70 | return _engineMap[engineId]!.onMessageReceived( 71 | channel, 72 | message, 73 | ); 74 | } else { 75 | return Future.value('Error: no engine found with id: $engineId'); 76 | } 77 | } 78 | return Future.error('No method "${call.method}" was found!'); 79 | }); 80 | 81 | bool messageHandlerRegistered = false; 82 | 83 | typedef FlutterJsChannelCallbak = Future Function( 84 | String? args, 85 | ); 86 | 87 | class FlutterJs { 88 | int? _engineId; 89 | static int? _httpPort; 90 | 91 | static int? get httpPort => _httpPort; 92 | static String? get httpPassword => _httpPassword; 93 | 94 | static var _engineCount = -1; 95 | static String? _httpPassword; 96 | 97 | bool _ready = false; 98 | 99 | int? get id => _engineId; 100 | 101 | Map _channels = {}; 102 | 103 | FlutterJs() { 104 | _engineCount += 1; 105 | _engineId = _engineCount; 106 | FlutterJs.initEngine(_engineId).then((_) => _ready = true); 107 | _engineMap[_engineId] = this; 108 | } 109 | 110 | dispose() { 111 | FlutterJs.close(_engineId); 112 | } 113 | 114 | addChannel(String name, FlutterJsChannelCallbak fn, 115 | {String? dartChannelAddress}) { 116 | _channels[name] = fn; 117 | _methodChannel.invokeMethod( 118 | "registerChannel", 119 | { 120 | "engineId": id, 121 | "channelName": name, 122 | "dartChannelAddress": dartChannelAddress 123 | }, 124 | ); 125 | } 126 | 127 | Future onMessageReceived(String? channel, String? message) { 128 | if (_channels[channel!] != null) { 129 | return _channels[channel]!(message); 130 | } else { 131 | return Future.error('No channel "$channel" was registered!'); 132 | } 133 | } 134 | 135 | bool isReady() => _ready; 136 | 137 | // ignore: non_constant_identifier_names 138 | static bool DEBUG = false; 139 | 140 | Future eval(String code) { 141 | return evaluate(code, _engineId); 142 | } 143 | 144 | static Future get platformVersion async { 145 | final String? version = 146 | await _methodChannel.invokeMethod('getPlatformVersion'); 147 | return version; 148 | } 149 | 150 | static Future initEngine(int? engineId) async { 151 | Map mapResult = await (_methodChannel.invokeMethod( 152 | "initEngine", engineId) as Future>); 153 | _httpPort = mapResult['httpPort'] as int?; 154 | _httpPassword = mapResult['httpPassword'] as String?; 155 | return engineId; 156 | } 157 | 158 | static Future close(int? engineId) async { 159 | await _methodChannel.invokeMethod("close", engineId); 160 | return engineId; 161 | } 162 | 163 | static Future evaluate(String command, int? id, 164 | {String convertTo = ""}) async { 165 | var arguments = { 166 | "engineId": id, 167 | "command": command, 168 | "convertTo": convertTo 169 | }; 170 | final rs = await _methodChannel.invokeMethod("evaluate", arguments); 171 | final String? jsResult = rs is Map || rs is List ? json.encode(rs) : rs; 172 | if (DEBUG) { 173 | print("${DateTime.now().toIso8601String()} - JS RESULT : $jsResult"); 174 | } 175 | return jsResult ?? "null"; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /lib/javascript_runtime.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:ffi'; 4 | 5 | import 'package:flutter/foundation.dart'; 6 | 7 | import 'js_eval_result.dart'; 8 | 9 | class FlutterJsPlatformEmpty extends JavascriptRuntime { 10 | @override 11 | JsEvalResult callFunction(Pointer fn, Pointer obj) { 12 | throw UnimplementedError(); 13 | } 14 | 15 | @override 16 | T? convertValue(JsEvalResult jsValue) { 17 | throw UnimplementedError(); 18 | } 19 | 20 | @override 21 | void dispose() {} 22 | 23 | @override 24 | JsEvalResult evaluate(String code, {String? sourceUrl}) { 25 | throw UnimplementedError(); 26 | } 27 | 28 | @override 29 | Future evaluateAsync(String code, {String? sourceUrl}) { 30 | throw UnimplementedError(); 31 | } 32 | 33 | @override 34 | int executePendingJob() { 35 | throw UnimplementedError(); 36 | } 37 | 38 | @override 39 | String getEngineInstanceId() { 40 | throw UnimplementedError(); 41 | } 42 | 43 | @override 44 | void initChannelFunctions() { 45 | throw UnimplementedError(); 46 | } 47 | 48 | @override 49 | String jsonStringify(JsEvalResult jsValue) { 50 | throw UnimplementedError(); 51 | } 52 | 53 | @override 54 | bool setupBridge(String channelName, void Function(dynamic args) fn) { 55 | throw UnimplementedError(); 56 | } 57 | 58 | @override 59 | void setInspectable(bool inspectable) { 60 | throw UnimplementedError(); 61 | } 62 | } 63 | 64 | abstract class JavascriptRuntime { 65 | static bool debugEnabled = false; 66 | 67 | @protected 68 | JavascriptRuntime init() { 69 | initChannelFunctions(); 70 | _setupConsoleLog(); 71 | _setupSetTimeout(); 72 | return this; 73 | } 74 | 75 | Map localContext = {}; 76 | 77 | Map dartContext = {}; 78 | 79 | void dispose(); 80 | 81 | static Map> 82 | _channelFunctionsRegistered = {}; 83 | 84 | static Map> 85 | get channelFunctionsRegistered => _channelFunctionsRegistered; 86 | 87 | JsEvalResult evaluate(String code, {String? sourceUrl}); 88 | 89 | Future evaluateAsync(String code, {String? sourceUrl}); 90 | 91 | JsEvalResult callFunction(Pointer fn, Pointer obj); 92 | 93 | T? convertValue(JsEvalResult jsValue); 94 | 95 | String jsonStringify(JsEvalResult jsValue); 96 | 97 | @protected 98 | void initChannelFunctions(); 99 | 100 | int executePendingJob(); 101 | 102 | void _setupConsoleLog() { 103 | evaluate(""" 104 | var console = { 105 | log: function() { 106 | sendMessage('ConsoleLog', JSON.stringify(['log', ...arguments])); 107 | }, 108 | warn: function() { 109 | sendMessage('ConsoleLog', JSON.stringify(['info', ...arguments])); 110 | }, 111 | error: function() { 112 | sendMessage('ConsoleLog', JSON.stringify(['error', ...arguments])); 113 | } 114 | }"""); 115 | onMessage('ConsoleLog', (dynamic args) { 116 | args..removeAt(0); 117 | String output = args.join(' '); 118 | print(output); 119 | }); 120 | } 121 | 122 | void _setupSetTimeout() { 123 | evaluate(""" 124 | var __NATIVE_FLUTTER_JS__setTimeoutCount = -1; 125 | var __NATIVE_FLUTTER_JS__setTimeoutCallbacks = {}; 126 | function setTimeout(fnTimeout, timeout) { 127 | // console.log('Set Timeout Called'); 128 | try { 129 | __NATIVE_FLUTTER_JS__setTimeoutCount += 1; 130 | var timeoutIndex = '' + __NATIVE_FLUTTER_JS__setTimeoutCount; 131 | __NATIVE_FLUTTER_JS__setTimeoutCallbacks[timeoutIndex] = fnTimeout; 132 | ; 133 | // console.log(typeof(sendMessage)); 134 | // console.log('BLA'); 135 | sendMessage('SetTimeout', JSON.stringify({ timeoutIndex, timeout})); 136 | 137 | } catch (e) { 138 | console.error('ERROR HERE',e.message); 139 | } 140 | }; 141 | 1 142 | """); 143 | //print('SET TIMEOUT EVAL RESULT: $setTImeoutResult'); 144 | onMessage('SetTimeout', (dynamic args) { 145 | try { 146 | int duration = args['timeout'] ?? 0; 147 | String idx = args['timeoutIndex']; 148 | 149 | Timer(Duration(milliseconds: duration), () { 150 | evaluate(""" 151 | __NATIVE_FLUTTER_JS__setTimeoutCallbacks[$idx].call(); 152 | delete __NATIVE_FLUTTER_JS__setTimeoutCallbacks[$idx]; 153 | """); 154 | }); 155 | } on Exception catch (e) { 156 | print('Exception no setTimeout: $e'); 157 | } on Error catch (e) { 158 | print('Erro no setTimeout: $e'); 159 | } 160 | }); 161 | } 162 | 163 | sendMessage({ 164 | required String channelName, 165 | required List args, 166 | String? uuid, 167 | }) { 168 | if (uuid != null) { 169 | evaluate( 170 | "DART_TO_QUICKJS_CHANNEL_sendMessage('$channelName', '${jsonEncode(args)}', '$uuid');"); 171 | } else { 172 | evaluate( 173 | "DART_TO_QUICKJS_CHANNEL_sendMessage('$channelName', '${jsonEncode(args)}');"); 174 | } 175 | } 176 | 177 | onMessage(String channelName, dynamic Function(dynamic args) fn) { 178 | setupBridge(channelName, fn); 179 | } 180 | 181 | bool setupBridge(String channelName, void Function(dynamic args) fn); 182 | 183 | String getEngineInstanceId(); 184 | 185 | void setInspectable(bool inspectable); 186 | } 187 | -------------------------------------------------------------------------------- /lib/javascriptcore/binding/js_base.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | 3 | import 'jsc_ffi.dart'; 4 | 5 | /// typedef JSTypedArrayBytesDeallocator A function used to deallocate bytes passed to a Typed Array constructor. The function should take two arguments. The first is a pointer to the bytes that were originally passed to the Typed Array constructor. The second is a pointer to additional information desired at the time the bytes are to be freed. 6 | /// typedef void (*JSTypedArrayBytesDeallocator)(void* bytes, void* deallocatorContext); 7 | typedef JSTypedArrayBytesDeallocator = Void Function( 8 | Pointer bytes, Pointer deallocatorContext); 9 | typedef JSTypedArrayBytesDeallocatorDart = void Function( 10 | Pointer bytes, Pointer deallocatorContext); 11 | 12 | /// Evaluates a string of JavaScript. 13 | /// [ctx] (JSContextRef) The execution context to use. 14 | /// [script] (JSStringRef) A JSString containing the script to evaluate. 15 | /// [thisObject] (JSObjectRef) The object to use as "this," or NULL to use the global object as "this." 16 | /// [sourceURL] (JSStringRef) A JSString containing a URL for the script's source file. This is used by debuggers and when reporting exceptions. Pass NULL if you do not care to include source file information. 17 | /// [startingLineNumber] (int) An integer value specifying the script's starting line number in the file located at sourceURL. This is only used when reporting exceptions. The value is one-based, so the first line is line 1 and invalid values are clamped to 1. 18 | /// [exception] (JSValueRef*) A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. 19 | /// [@result] (JSValueRef) The JSValue that results from evaluating script, or NULL if an exception is thrown. 20 | final Pointer Function(Pointer ctx, Pointer script, Pointer thisObject, 21 | Pointer sourceURL, int startingLineNumber, Pointer exception) 22 | jSEvaluateScript = JscFfi.lib 23 | .lookup< 24 | NativeFunction< 25 | Pointer Function(Pointer, Pointer, Pointer, Pointer, Int32, 26 | Pointer)>>('JSEvaluateScript') 27 | .asFunction(); 28 | 29 | /// Checks for syntax errors in a string of JavaScript. 30 | /// [ctx] (JSContextRef) The execution context to use. 31 | /// [script] (JSStringRef) A JSString containing the script to check for syntax errors. 32 | /// [sourceURL] (JSStringRef) A JSString containing a URL for the script's source file. This is only used when reporting exceptions. Pass NULL if you do not care to include source file information in exceptions. 33 | /// [startingLineNumber] (int) An integer value specifying the script's starting line number in the file located at sourceURL. This is only used when reporting exceptions. The value is one-based, so the first line is line 1 and invalid values are clamped to 1. 34 | /// [exception] (JSValueRef*) A pointer to a JSValueRef in which to store a syntax error exception, if any. Pass NULL if you do not care to store a syntax error exception. 35 | /// [@result] (bool) true if the script is syntactically correct, otherwise false. 36 | final Pointer Function(Pointer ctx, Pointer script, Pointer sourceURL, 37 | int startingLineNumber, Pointer exception) 38 | jSCheckScriptSyntax = JscFfi.lib 39 | .lookup< 40 | NativeFunction< 41 | Pointer Function(Pointer, Pointer, Pointer, Int32, 42 | Pointer)>>('JSCheckScriptSyntax') 43 | .asFunction(); 44 | 45 | /// Performs a JavaScript garbage collection. 46 | /// JavaScript values that are on the machine stack, in a register, 47 | /// protected by JSValueProtect, set as the global object of an execution context, 48 | /// or reachable from any such value will not be collected. 49 | /// 50 | /// During JavaScript execution, you are not required to call this function; the 51 | /// JavaScript engine will garbage collect as needed. JavaScript values created 52 | /// within a context group are automatically destroyed when the last reference 53 | /// to the context group is released. 54 | /// [ctx] (JSContextRef) The execution context to use. 55 | final void Function(Pointer ctx) jSGarbageCollect = JscFfi.lib 56 | .lookup>('JSGarbageCollect') 57 | .asFunction(); 58 | -------------------------------------------------------------------------------- /lib/javascriptcore/binding/js_context_ref.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | 3 | import 'jsc_ffi.dart'; 4 | 5 | /// Creates a JavaScript context group. 6 | /// A JSContextGroup associates JavaScript contexts with one another. 7 | /// Contexts in the same group may share and exchange JavaScript objects. Sharing and/or exchanging 8 | /// JavaScript objects between contexts in different groups will produce undefined behavior. 9 | /// When objects from the same context group are used in multiple threads, explicit 10 | /// synchronization is required. 11 | /// 12 | /// A JSContextGroup may need to run deferred tasks on a run loop, such as garbage collection 13 | /// or resolving WebAssembly compilations. By default, calling JSContextGroupCreate will use 14 | /// the run loop of the thread it was called on. Currently, there is no API to change a 15 | /// JSContextGroup's run loop once it has been created. 16 | /// [@result] (JSContextGroupRef) The created JSContextGroup. 17 | final Pointer Function() jSContextGroupCreate = JscFfi.lib 18 | .lookup>('JSContextGroupCreate') 19 | .asFunction(); 20 | 21 | /// Retains a JavaScript context group. 22 | /// [group] (JSContextGroupRef) The JSContextGroup to retain. 23 | /// [@result] (JSContextGroupRef) A JSContextGroup that is the same as group. 24 | final Pointer Function(Pointer group) jSContextGroupRetain = JscFfi.lib 25 | .lookup>('JSContextGroupRetain') 26 | .asFunction(); 27 | 28 | /// Releases a JavaScript context group. 29 | /// [group] (JSContextGroupRef) The JSContextGroup to release. 30 | final void Function(Pointer group) jSContextGroupRelease = JscFfi.lib 31 | .lookup>('JSContextGroupRelease') 32 | .asFunction(); 33 | 34 | /// Creates a global JavaScript execution context. 35 | /// JSGlobalContextCreate allocates a global object and populates it with all the 36 | /// built-in JavaScript objects, such as Object, Function, String, and Array. 37 | /// 38 | /// In WebKit version 4.0 and later, the context is created in a unique context group. 39 | /// Therefore, scripts may execute in it concurrently with scripts executing in other contexts. 40 | /// However, you may not use values created in the context in other contexts. 41 | /// [globalObjectClass] (JSClassRef) The class to use when creating the global object. Pass NULL to use the default object class. 42 | /// [@result] (JSGlobalContextRef) A JSGlobalContext with a global object of class globalObjectClass. 43 | final Pointer Function(Pointer globalObjectClass) jSGlobalContextCreate = JscFfi 44 | .lib 45 | .lookup>('JSGlobalContextCreate') 46 | .asFunction(); 47 | 48 | /// Creates a global JavaScript execution context in the context group provided. 49 | /// JSGlobalContextCreateInGroup allocates a global object and populates it with 50 | /// all the built-in JavaScript objects, such as Object, Function, String, and Array. 51 | /// [group] (JSContextGroupRef) The context group to use. The created global context retains the group. Pass NULL to create a unique group for the context. 52 | /// [globalObjectClass] (JSClassRef) The class to use when creating the global object. Pass NULL to use the default object class. 53 | /// [@result] (JSGlobalContextRef) A JSGlobalContext with a global object of class globalObjectClass and a context group equal to group. 54 | final Pointer Function(Pointer group, Pointer globalObjectClass) 55 | jSGlobalContextCreateInGroup = JscFfi.lib 56 | .lookup>( 57 | 'JSGlobalContextCreateInGroup') 58 | .asFunction(); 59 | 60 | /// Retains a global JavaScript execution context. 61 | /// [ctx] (JSGlobalContextRef) The JSGlobalContext to retain. 62 | /// [@result] (JSGlobalContextRef) A JSGlobalContext that is the same as ctx. 63 | final Pointer Function(Pointer ctx) jSGlobalContextRetain = JscFfi.lib 64 | .lookup>('JSGlobalContextRetain') 65 | .asFunction(); 66 | 67 | /// Releases a global JavaScript execution context. 68 | /// [ctx] (JSGlobalContextRef) The JSGlobalContext to release. 69 | final void Function(Pointer ctx) jSGlobalContextRelease = JscFfi.lib 70 | .lookup>('JSGlobalContextRelease') 71 | .asFunction(); 72 | 73 | /// Gets the global object of a JavaScript execution context. 74 | /// [ctx] (JSContextRef) The JSContext whose global object you want to get. 75 | /// [@result] (JSObjectRef) ctx's global object. 76 | final Pointer Function(Pointer ctx) jSContextGetGlobalObject = JscFfi.lib 77 | .lookup>( 78 | 'JSContextGetGlobalObject') 79 | .asFunction(); 80 | 81 | /// Gets the context group to which a JavaScript execution context belongs. 82 | /// [ctx] (JSContextRef) The JSContext whose group you want to get. 83 | /// [@result] (JSContextGroupRef) ctx's group. 84 | final Pointer Function(Pointer ctx) jSContextGetGroup = JscFfi.lib 85 | .lookup>('JSContextGetGroup') 86 | .asFunction(); 87 | 88 | /// Gets the global context of a JavaScript execution context. 89 | /// [ctx] (JSContextRef) The JSContext whose global context you want to get. 90 | /// [@result] (JSGlobalContextRef) ctx's global context. 91 | final Pointer Function(Pointer ctx) jSContextGetGlobalContext = JscFfi.lib 92 | .lookup>( 93 | 'JSContextGetGlobalContext') 94 | .asFunction(); 95 | 96 | /// Gets a copy of the name of a context. 97 | /// A JSGlobalContext's name is exposed for remote debugging to make it 98 | /// easier to identify the context you would like to attach to. 99 | /// [ctx] (JSGlobalContextRef) The JSGlobalContext whose name you want to get. 100 | /// [@result] (JSStringRef) The name for ctx. 101 | final Pointer Function(Pointer ctx) jSGlobalContextCopyName = JscFfi.lib 102 | .lookup>( 103 | 'JSGlobalContextCopyName') 104 | .asFunction(); 105 | 106 | /// Sets the remote debugging name for a context. 107 | /// [ctx] (JSGlobalContextRef) The JSGlobalContext that you want to name. 108 | /// [name] (JSStringRef) The remote debugging name to set on ctx. 109 | final void Function(Pointer ctx, Pointer name) jSGlobalContextSetName = JscFfi 110 | .lib 111 | .lookup>( 112 | 'JSGlobalContextSetName') 113 | .asFunction(); 114 | -------------------------------------------------------------------------------- /lib/javascriptcore/binding/js_global_context_ref.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | 3 | import 'jsc_ffi.dart'; 4 | 5 | final void Function(Pointer ctx, bool inspectable) 6 | jSGlobalContextSetInspectable = JscFfi.lib 7 | .lookup>( 8 | 'JSGlobalContextSetInspectable') 9 | .asFunction(); 10 | -------------------------------------------------------------------------------- /lib/javascriptcore/binding/js_string_ref.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | 3 | import 'package:ffi/ffi.dart'; 4 | 5 | import 'jsc_ffi.dart'; 6 | 7 | /// Creates a JavaScript string from a buffer of Unicode characters. 8 | /// [chars] (JSChar*) The buffer of Unicode characters to copy into the new JSString. 9 | /// [numChars] (size_t) The number of characters to copy from the buffer pointed to by chars. 10 | /// [@result] (JSStringRef) A JSString containing chars. Ownership follows the Create Rule. 11 | final Pointer Function(Pointer chars, Pointer numChars) 12 | jSStringCreateWithCharacters = JscFfi.lib 13 | .lookup, Pointer)>>( 14 | 'JSStringCreateWithCharacters') 15 | .asFunction(); 16 | 17 | /// Creates a JavaScript string from a null-terminated UTF8 string. 18 | /// [string] (char*) The null-terminated UTF8 string to copy into the new JSString. 19 | /// [@result] (JSStringRef) A JSString containing string. Ownership follows the Create Rule. 20 | final Pointer Function(Pointer string) jSStringCreateWithUTF8CString = 21 | JscFfi.lib 22 | .lookup)>>( 23 | 'JSStringCreateWithUTF8CString') 24 | .asFunction(); 25 | 26 | /// Retains a JavaScript string. 27 | /// [string] (JSStringRef) The JSString to retain. 28 | /// [@result] (JSStringRef) A JSString that is the same as string. 29 | final Pointer Function(Pointer string) jSStringRetain = JscFfi.lib 30 | .lookup>('JSStringRetain') 31 | .asFunction(); 32 | 33 | /// Releases a JavaScript string. 34 | /// [string] (JSStringRef) The JSString to release. 35 | final void Function(Pointer string) jSStringRelease = JscFfi.lib 36 | .lookup>('JSStringRelease') 37 | .asFunction(); 38 | 39 | /// Returns the number of Unicode characters in a JavaScript string. 40 | /// [string] (JSStringRef) The JSString whose length (in Unicode characters) you want to know. 41 | /// [@result] (size_t) The number of Unicode characters stored in string. 42 | final int Function(Pointer string) jSStringGetLength = JscFfi.lib 43 | .lookup>('JSStringGetLength') 44 | .asFunction(); 45 | 46 | /// Returns a pointer to the Unicode character buffer that 47 | /// serves as the backing store for a JavaScript string. 48 | /// [string] (JSStringRef) The JSString whose backing store you want to access. 49 | /// [@result] (const JSChar*) A pointer to the Unicode character buffer that serves as string's backing store, which will be deallocated when string is deallocated. 50 | final Pointer Function(Pointer string) jSStringGetCharactersPtr = JscFfi 51 | .lib 52 | .lookup Function(Pointer)>>( 53 | 'JSStringGetCharactersPtr') 54 | .asFunction(); 55 | 56 | /// Returns the maximum number of bytes a JavaScript string will 57 | /// take up if converted into a null-terminated UTF8 string. 58 | /// [string] (JSStringRef) The JSString whose maximum converted size (in bytes) you want to know. 59 | /// [@result] (size_t) The maximum number of bytes that could be required to convert string into a null-terminated UTF8 string. The number of bytes that the conversion actually ends up requiring could be less than this, but never more. 60 | final int Function(Pointer string) jSStringGetMaximumUTF8CStringSize = JscFfi 61 | .lib 62 | .lookup>( 63 | 'JSStringGetMaximumUTF8CStringSize') 64 | .asFunction(); 65 | 66 | /// Converts a JavaScript string into a null-terminated UTF8 string, 67 | /// and copies the result into an external byte buffer. 68 | /// [string] (JSStringRef) The source JSString. 69 | /// [buffer] (char*) The destination byte buffer into which to copy a null-terminated UTF8 representation of string. On return, buffer contains a UTF8 string representation of string. If bufferSize is too small, buffer will contain only partial results. If buffer is not at least bufferSize bytes in size, behavior is undefined. 70 | /// [bufferSize] (size_t) The size of the external buffer in bytes. 71 | /// [@result] (size_t) The number of bytes written into buffer (including the null-terminator byte). 72 | final int Function(Pointer string, Pointer buffer, int bufferSize) 73 | jSStringGetUTF8CString = JscFfi.lib 74 | .lookup>( 75 | 'JSStringGetUTF8CString') 76 | .asFunction(); 77 | 78 | /// Tests whether two JavaScript strings match. 79 | /// [a] (JSStringRef) The first JSString to test. 80 | /// [b] (JSStringRef) The second JSString to test. 81 | /// [@result] (bool) true if the two strings match, otherwise false. 82 | final int Function(Pointer a, Pointer b) jSStringIsEqual = JscFfi.lib 83 | .lookup>('JSStringIsEqual') 84 | .asFunction(); 85 | 86 | /// Tests whether a JavaScript string matches a null-terminated UTF8 string. 87 | /// [a] (JSStringRef) The JSString to test. 88 | /// [b] (char*) The null-terminated UTF8 string to test. 89 | /// [@result] (bool) true if the two strings match, otherwise false. 90 | final Pointer Function(Pointer a, Pointer b) jSStringIsEqualToUTF8CString = 91 | JscFfi.lib 92 | .lookup>( 93 | 'JSStringIsEqualToUTF8CString') 94 | .asFunction(); 95 | -------------------------------------------------------------------------------- /lib/javascriptcore/binding/jsc_ffi.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | import 'dart:io'; 3 | 4 | class JscFfi { 5 | /// You can replace it with the version you want 6 | static DynamicLibrary lib = Platform.isIOS || Platform.isMacOS 7 | ? getMacOsIOSJavascriptCoreLibPath() 8 | : Platform.isWindows 9 | ? DynamicLibrary.open('JavaScriptCore.dll') 10 | : Platform.isLinux 11 | ? DynamicLibrary.open('libjavascriptcoregtk-4.0.so.18') 12 | : DynamicLibrary.open('libjsc.so'); 13 | 14 | static DynamicLibrary getMacOsIOSJavascriptCoreLibPath() { 15 | try { 16 | return DynamicLibrary.open('JavaScriptCore.framework/JavaScriptCore'); 17 | } on ArgumentError { 18 | return DynamicLibrary.open( 19 | '/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore'); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/javascriptcore/flutter_jscore.dart: -------------------------------------------------------------------------------- 1 | export 'jscore/js_class.dart'; 2 | export 'jscore/js_context_group.dart'; 3 | export 'jscore/js_context.dart'; 4 | export 'jscore/js_object.dart'; 5 | export 'jscore/js_property_name_accumulator.dart'; 6 | export 'jscore/js_property_name_array.dart'; 7 | export 'jscore/js_string.dart'; 8 | export 'jscore/js_value.dart'; 9 | 10 | //import 'dart:async'; 11 | // 12 | //import 'package:flutter/services.dart'; 13 | // 14 | //class FlutterJscore { 15 | // static const MethodChannel _channel = const MethodChannel('flutter_jscore'); 16 | // 17 | // static Future get platformVersion async { 18 | // final String version = await _channel.invokeMethod('getPlatformVersion'); 19 | // return version; 20 | // } 21 | //} 22 | -------------------------------------------------------------------------------- /lib/javascriptcore/jscore/js_class.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | 3 | import '../binding/js_object_ref.dart' as JSObjectRef; 4 | 5 | import 'js_object.dart'; 6 | 7 | /// A JavaScript class. Used with JSObjectMake to construct objects with custom behavior. 8 | class JSClass { 9 | /// C pointer 10 | Pointer pointer; 11 | 12 | JSClass(this.pointer); 13 | 14 | /// Creates a JavaScript class suitable for use with JSObjectMake. 15 | /// [definition] (JSClassDefinition*) A JSClassDefinition that defines the class. 16 | JSClass.create(JSClassDefinition definition) 17 | : this.pointer = JSObjectRef.jSClassCreate(definition.create()); 18 | 19 | /// Retains a JavaScript class. 20 | void retain() { 21 | pointer = JSObjectRef.jSClassRetain(pointer); 22 | } 23 | 24 | /// Releases a JavaScript class. 25 | void release() { 26 | JSObjectRef.jSClassRelease(pointer); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/javascriptcore/jscore/js_context.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | 3 | import '../binding/js_base.dart' as JSBase; 4 | import '../binding/js_context_ref.dart' as JSContextRef; 5 | import '../binding/js_global_context_ref.dart' as JSGlobalContextRef; 6 | import 'js_class.dart'; 7 | import 'js_context_group.dart'; 8 | import 'js_object.dart'; 9 | import 'js_string.dart'; 10 | import 'js_value.dart'; 11 | 12 | /// A JavaScript execution context. Holds the global object and other execution state. 13 | class JSContext { 14 | /// C pointer 15 | Pointer _pointer; 16 | 17 | Pointer get pointer => _pointer; 18 | 19 | /// Exception (JSValueRef*) A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. 20 | JSValuePointer exception = JSValuePointer(); 21 | 22 | JSContext(this._pointer); 23 | 24 | /// Creates a global JavaScript execution context. 25 | /// JSGlobalContextCreate allocates a global object and populates it with all the 26 | /// built-in JavaScript objects, such as Object, Function, String, and Array. 27 | /// 28 | /// In WebKit version 4.0 and later, the context is created in a unique context group. 29 | /// Therefore, scripts may execute in it concurrently with scripts executing in other contexts. 30 | /// However, you may not use values created in the context in other contexts. 31 | /// [globalObjectClass] (JSClass) The class to use when creating the global object. Pass NULL to use the default object class. 32 | /// [@result] (JSGlobalContext) A JSGlobalContext with a global object of class globalObjectClass. 33 | JSContext.create({ 34 | JSClass? globalObjectClass, 35 | }) : this._pointer = JSContextRef.jSGlobalContextCreate( 36 | globalObjectClass == null ? nullptr : globalObjectClass.pointer); 37 | 38 | /// Creates a global JavaScript execution context in the context group provided. 39 | /// JSGlobalContextCreateInGroup allocates a global object and populates it with 40 | /// all the built-in JavaScript objects, such as Object, Function, String, and Array. 41 | /// [group] (JSContextGroup) The context group to use. The created global context retains the group. Pass NULL to create a unique group for the context. 42 | /// [globalObjectClass] (JSClass) The class to use when creating the global object. Pass NULL to use the default object class. 43 | /// [@result] (JSGlobalContext) A JSGlobalContext with a global object of class globalObjectClass and a context group equal to group. 44 | JSContext.createInGroup({ 45 | JSContextGroup? group, 46 | JSClass? globalObjectClass, 47 | }) : this._pointer = JSContextRef.jSGlobalContextCreateInGroup( 48 | group == null ? JSContextRef.jSContextGroupCreate() : group.pointer, 49 | globalObjectClass == null ? nullptr : globalObjectClass.pointer); 50 | 51 | /// Retains a global JavaScript execution context. 52 | /// [@result] (JSGlobalContext) A JSGlobalContext that is the same as ctx. 53 | void retain() { 54 | _pointer = JSContextRef.jSGlobalContextRetain(pointer); 55 | } 56 | 57 | /// Releases a global JavaScript execution context. 58 | /// [ctx] (JSGlobalContext) The JSGlobalContext to release. 59 | void release() { 60 | return JSContextRef.jSGlobalContextRelease(pointer); 61 | } 62 | 63 | /// Gets the global object of a JavaScript execution context. 64 | /// [@result] (JSObject) ctx's global object. 65 | JSObject get globalObject { 66 | return JSObject(this, JSContextRef.jSContextGetGlobalObject(pointer)); 67 | } 68 | 69 | /// Gets the context group to which a JavaScript execution context belongs. 70 | /// [@result] (JSContextGroup) ctx's group. 71 | JSContextGroup get group { 72 | return JSContextGroup(JSContextRef.jSContextGetGroup(pointer)); 73 | } 74 | 75 | /// Gets the global context of a JavaScript execution context. 76 | /// [@result] (JSGlobalContext) ctx's global context. 77 | /*JSGlobalContext get globalContext { 78 | return JSGlobalContext(JSContextRef.jSContextGetGlobalContext(pointer)); 79 | }*/ 80 | 81 | /// Gets a copy of the name of a context. 82 | /// A JSGlobalContext's name is exposed for remote debugging to make it 83 | /// easier to identify the context you would like to attach to. 84 | /// [@result] (JSString) The name for ctx. 85 | JSString copyName() { 86 | return JSString(JSContextRef.jSGlobalContextCopyName(pointer)); 87 | } 88 | 89 | /// Sets the remote debugging name for a context. 90 | /// [name] (JSString) The remote debugging name to set on ctx. 91 | void setName(JSString name) { 92 | return JSContextRef.jSGlobalContextSetName(pointer, name.pointer); 93 | } 94 | 95 | /// Evaluates a string of JavaScript. 96 | /// [script] (String) A JSString containing the script to evaluate. 97 | /// [thisObject] (JSObject) The object to use as "this," or NULL to use the global object as "this." 98 | /// [sourceURL] (String) A JSString containing a URL for the script's source file. This is used by debuggers and when reporting exceptions. Pass NULL if you do not care to include source file information. 99 | /// [startingLineNumber] (int) An integer value specifying the script's starting line number in the file located at sourceURL. This is only used when reporting exceptions. The value is one-based, so the first line is line 1 and invalid values are clamped to 1. 100 | /// [@result] (JSValueRef) The JSValue that results from evaluating script, or NULL if an exception is thrown. 101 | JSValue evaluate( 102 | String script, { 103 | JSObject? thisObject, 104 | String? sourceURL, 105 | int startingLineNumber = 1, 106 | }) { 107 | return JSValue( 108 | this, 109 | JSBase.jSEvaluateScript( 110 | pointer, 111 | JSString.fromString(script).pointer, 112 | thisObject == null ? nullptr : thisObject.pointer, 113 | sourceURL == null ? nullptr : JSString.fromString(sourceURL).pointer, 114 | startingLineNumber, 115 | exception.pointer, 116 | )); 117 | } 118 | 119 | void setInspectable(bool inspectable) { 120 | JSGlobalContextRef.jSGlobalContextSetInspectable(_pointer, inspectable); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/javascriptcore/jscore/js_context_group.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | import '../binding/js_context_ref.dart' as JSContextRef; 3 | 4 | /// JSContextGroupRef A group that associates JavaScript contexts with one another. Contexts in the same group may share and exchange JavaScript objects. 5 | class JSContextGroup { 6 | /// C pointer 7 | final Pointer pointer; 8 | 9 | JSContextGroup(this.pointer); 10 | 11 | /// Creates a JavaScript context group. 12 | /// A JSContextGroup associates JavaScript contexts with one another. 13 | /// Contexts in the same group may share and exchange JavaScript objects. Sharing and/or exchanging 14 | /// JavaScript objects between contexts in different groups will produce undefined behavior. 15 | /// When objects from the same context group are used in multiple threads, explicit 16 | /// synchronization is required. 17 | /// 18 | /// A JSContextGroup may need to run deferred tasks on a run loop, such as garbage collection 19 | /// or resolving WebAssembly compilations. By default, calling JSContextGroupCreate will use 20 | /// the run loop of the thread it was called on. Currently, there is no API to change a 21 | /// JSContextGroup's run loop once it has been created. 22 | JSContextGroup.create() : this.pointer = JSContextRef.jSContextGroupCreate(); 23 | 24 | /// Retains a JavaScript context group. 25 | /// [@result] (JSContextGroupRef) A JSContextGroup that is the same as group. 26 | JSContextGroup retain() { 27 | return JSContextGroup(JSContextRef.jSContextGroupRetain(pointer)); 28 | } 29 | 30 | /// Releases a JavaScript context group. 31 | void release() { 32 | return JSContextRef.jSContextGroupRelease(pointer); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/javascriptcore/jscore/js_property_name_accumulator.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | 3 | /// An ordered set used to collect the names of a JavaScript object's properties. 4 | class JSPropertyNameAccumulator { 5 | /// C pointer 6 | final Pointer pointer; 7 | 8 | JSPropertyNameAccumulator(this.pointer); 9 | } 10 | -------------------------------------------------------------------------------- /lib/javascriptcore/jscore/js_property_name_array.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | 3 | import '../binding/js_object_ref.dart' as JSObjectRef; 4 | 5 | import 'js_string.dart'; 6 | 7 | /// An array of JavaScript property names. 8 | class JSPropertyNameArray { 9 | /// C pointer 10 | Pointer pointer; 11 | 12 | JSPropertyNameArray(this.pointer); 13 | 14 | /// Retains a JavaScript property name array. 15 | void retain() { 16 | pointer = JSObjectRef.jSPropertyNameArrayRetain(pointer); 17 | } 18 | 19 | /// Releases a JavaScript property name array. 20 | void release() { 21 | JSObjectRef.jSPropertyNameArrayRelease(pointer); 22 | } 23 | 24 | /// Gets a count of the number of items in a JavaScript property name array. 25 | int get count { 26 | return JSObjectRef.jSPropertyNameArrayGetCount(pointer); 27 | } 28 | 29 | /// Gets a property name at a given index in a JavaScript property name array. 30 | /// [index] (size_t) The index of the property name to retrieve. 31 | String propertyNameArrayGetNameAtIndex(int index) { 32 | return JSString( 33 | JSObjectRef.jSPropertyNameArrayGetNameAtIndex(pointer, index)) 34 | .string!; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/javascriptcore/jscore/js_string.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:ffi/ffi.dart'; 5 | 6 | import '../binding/js_string_ref.dart' as JSStringRef; 7 | 8 | /// A UTF16 character buffer. The fundamental string representation in JavaScript. 9 | class JSString { 10 | /// C pointer 11 | late Pointer _pointer; 12 | get pointer => _pointer; 13 | 14 | JSString(this._pointer); 15 | 16 | /// Creates a JavaScript string from dart String. 17 | /// [string] The dart String. 18 | JSString.fromString(String? string) { 19 | if (string == null) { 20 | _pointer = nullptr; 21 | } else { 22 | var cString = string.toNativeUtf8(); 23 | _pointer = JSStringRef.jSStringCreateWithUTF8CString(cString); 24 | malloc.free(cString); 25 | } 26 | } 27 | 28 | /// Retains a JavaScript string. 29 | /// [@result] (JSStringRef) A JSString that is the same as string. 30 | void retain() { 31 | _pointer = JSStringRef.jSStringRetain(_pointer); 32 | } 33 | 34 | /// Releases a JavaScript string. 35 | void release() { 36 | if (_pointer != nullptr) { 37 | JSStringRef.jSStringRelease(_pointer); 38 | } 39 | } 40 | 41 | /// Returns the number of Unicode characters in a JavaScript string. 42 | int get length { 43 | return JSStringRef.jSStringGetLength(_pointer); 44 | } 45 | 46 | /// Returns dart String 47 | String? get string { 48 | if (_pointer == nullptr) return null; 49 | var cString = JSStringRef.jSStringGetCharactersPtr(_pointer); 50 | if (cString == nullptr) { 51 | return null; 52 | } 53 | int cStringLength = JSStringRef.jSStringGetLength(_pointer); 54 | return String.fromCharCodes(Uint16List.view( 55 | cString.cast().asTypedList(cStringLength).buffer, 56 | 0, 57 | cStringLength)); 58 | } 59 | 60 | @override 61 | bool operator ==(Object other) => 62 | identical(this, other) || 63 | other is JSString && 64 | runtimeType == other.runtimeType && 65 | JSStringRef.jSStringIsEqual(_pointer, other.pointer) == 1 || 66 | other is String && string == other; 67 | 68 | @override 69 | int get hashCode => _pointer.hashCode; 70 | } 71 | 72 | /// JSStringRef pointer 73 | class JSStringPointer { 74 | /// C pointer 75 | final Pointer pointer; 76 | 77 | /// Pointer array count 78 | final int count; 79 | 80 | JSStringPointer([Pointer? value]) 81 | : this.count = 1, 82 | this.pointer = malloc.call(1) { 83 | pointer.value = value ?? nullptr; 84 | } 85 | 86 | /// JSStringRef array 87 | JSStringPointer.array(List array) 88 | : this.count = array.length, 89 | this.pointer = malloc.call(array.length) { 90 | for (int i = 0; i < array.length; i++) { 91 | this.pointer[i] = JSString.fromString(array[i]).pointer; 92 | } 93 | } 94 | 95 | /// Get JSValue 96 | /// [index] Array index 97 | JSString getValue([int index = 0]) { 98 | return JSString(pointer[index]); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib/javascriptcore/jscore_bindings.dart: -------------------------------------------------------------------------------- 1 | export 'binding/js_base.dart'; 2 | export 'binding/js_context_ref.dart'; 3 | export 'binding/js_string_ref.dart'; 4 | export 'binding/js_value_ref.dart'; 5 | export 'binding/js_object_ref.dart'; 6 | export 'binding/js_typed_array.dart'; 7 | export 'binding/jsc_ffi.dart'; 8 | -------------------------------------------------------------------------------- /lib/js_eval_result.dart: -------------------------------------------------------------------------------- 1 | class JsEvalResult { 2 | final String stringResult; 3 | final dynamic rawResult; 4 | final bool isPromise; 5 | final bool isError; 6 | 7 | JsEvalResult(this.stringResult, this.rawResult, 8 | {this.isError = false, this.isPromise = false}); 9 | 10 | toString() => stringResult; 11 | } 12 | -------------------------------------------------------------------------------- /lib/quickjs/qjs_typedefs.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | 3 | import 'package:flutter_js/quickjs/utf8_null_terminated.dart'; 4 | 5 | final class JSContext extends Struct { 6 | @Uint8() 7 | external int char; 8 | } 9 | 10 | final class JSRuntime extends Struct { 11 | @Uint8() 12 | external int char; 13 | } 14 | 15 | final class JSValueConst extends Struct { 16 | @Uint8() 17 | external int char; 18 | } 19 | 20 | const int JS_EVAL_TYPE_GLOBAL = 0; 21 | const int JS_EVAL_TYPE_MODULE = 1; 22 | const int JS_EVAL_TYPE_DIRECT = 2; 23 | const int JS_EVAL_TYPE_INDIRECT = 3; 24 | 25 | enum QuickJSTypeModule { 26 | JS_EVAL_TYPE_GLOBAL, 27 | JS_EVAL_TYPE_MODULE, 28 | JS_EVAL_TYPE_DIRECT, 29 | JS_EVAL_TYPE_INDIRECT 30 | } 31 | 32 | const JS_TAG_FIRST = -11, 33 | /* first negative tag */ 34 | JS_TAG_BIG_DECIMAL = -11, 35 | JS_TAG_BIG_INT = -10, 36 | JS_TAG_BIG_FLOAT = -9, 37 | JS_TAG_SYMBOL = -8, 38 | JS_TAG_STRING = -7, 39 | JS_TAG_OBJECT = -1, 40 | JS_TAG_INT = 0, 41 | JS_TAG_BOOL = 1, 42 | JS_TAG_NULL = 2, 43 | JS_TAG_UNDEFINED = 3, 44 | JS_TAG_UNINITIALIZED = 4, 45 | JS_TAG_CATCH_OFFSET = 5, 46 | JS_TAG_EXCEPTION = 6, 47 | JS_TAG_FLOAT64 = 7; 48 | 49 | // ignore: camel_case_types 50 | typedef JS_NewRuntimeDartBridge = Pointer Function(); 51 | 52 | typedef ChannelCallback = Pointer Function( 53 | Pointer, 54 | Pointer, 55 | Pointer, 56 | ); 57 | 58 | // ignore: camel_case_types 59 | typedef JS_NewContextFn = Pointer Function( 60 | Pointer? jrt, 61 | Pointer>? fnConsoleLog, 62 | Pointer>? fnSetTimeout, 63 | Pointer>? fnSendNative, 64 | ); 65 | 66 | typedef JSEvalWrapper = Pointer Function( 67 | Pointer ctx, 68 | Pointer input, 69 | int inputLength, 70 | Pointer filename, 71 | int evalFlags, 72 | Pointer errors, 73 | Pointer result, 74 | Pointer> stringResult); 75 | 76 | // ignore: camel_case_types 77 | typedef JS_GetNullValue = Pointer Function( 78 | Pointer ctx, 79 | Pointer v, 80 | ); 81 | 82 | typedef JSEvalWrapperNative = Pointer Function( 83 | Pointer ctx, 84 | Pointer input, 85 | Int32 inputLength, 86 | Pointer filename, 87 | Int32 evalFlags, 88 | Pointer errors, 89 | Pointer result, 90 | Pointer> stringResult); 91 | 92 | typedef JSExecutePendingJob = int Function( 93 | Pointer rt, 94 | Pointer ctx, 95 | ); 96 | 97 | typedef JSExecutePendingJobNative = Uint32 Function( 98 | Pointer rt, 99 | Pointer ctx, 100 | ); 101 | 102 | typedef JSCallFunction1ArgNative = Uint32 Function( 103 | Pointer ctx, 104 | Pointer function, 105 | Pointer object, 106 | Pointer result, 107 | Pointer> stringResult, 108 | ); 109 | 110 | typedef JSCallFunction1Arg = int Function( 111 | Pointer ctx, 112 | Pointer function, 113 | Pointer object, 114 | Pointer result, 115 | Pointer> stringResult, 116 | ); 117 | 118 | typedef JSGetTypeTagNative = Int32 Function(Pointer jsValue); 119 | typedef JSGetTypeTag = int Function(Pointer jsValue); 120 | 121 | typedef JSIsArrayNative = Int32 Function( 122 | Pointer ctx, Pointer jsValue); 123 | typedef JSIsArray = int Function( 124 | Pointer ctx, Pointer jsValue); 125 | 126 | typedef int JSJSONStringify( 127 | Pointer ctx, 128 | Pointer obj, 129 | Pointer res, 130 | Pointer> stringResult, 131 | ); 132 | typedef Int32 JSJSONStringifyNative( 133 | Pointer ctx, 134 | Pointer obj, 135 | Pointer res, 136 | Pointer> stringResult, 137 | ); 138 | -------------------------------------------------------------------------------- /lib/quickjs/utf8_null_terminated.dart: -------------------------------------------------------------------------------- 1 | // Must Fix Utf8 because QuickJS need end with terminator '\0' 2 | import 'dart:convert'; 3 | import 'dart:ffi'; 4 | 5 | import 'package:ffi/ffi.dart'; 6 | 7 | final class Utf8NullTerminated extends Struct { 8 | @Uint8() 9 | external int char; 10 | 11 | static Pointer toUtf8(String s) { 12 | final bytes = Utf8Encoder().convert(s); 13 | final ptr = calloc(bytes.length + 1); 14 | for (var i = 0; i < bytes.length; i++) { 15 | ptr.elementAt(i).ref.char = bytes[i]; 16 | } 17 | // Add the terminator '\0' 18 | ptr.elementAt(bytes.length).ref.char = 0; 19 | return ptr; 20 | } 21 | 22 | static String fromUtf8(Pointer ptr) { 23 | final List bytes = []; 24 | var len = 0; 25 | while (true) { 26 | final char = ptr.elementAt(len++).ref.char; 27 | if (char == 0) break; 28 | bytes.add(char); 29 | } 30 | return Utf8Decoder().convert(bytes); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | 4 | set(PROJECT_NAME "flutter_js") 5 | project(${PROJECT_NAME} LANGUAGES CXX C) 6 | 7 | set(PLUGIN_NAME "${PROJECT_NAME}_plugin") 8 | 9 | set(CMAKE_C_COMPILER "gcc") 10 | set(CMAKE_CXX_COMPILER "g++") 11 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -DANDROID_STL=c++_static -DDEBUGGER_SERVER_PORT=${DEBUGGER_SERVER_PORT}") 12 | 13 | add_library(${PLUGIN_NAME} SHARED 14 | "flutter_js_plugin.cc" 15 | ) 16 | 17 | apply_standard_settings(${PLUGIN_NAME}) 18 | target_compile_features(${PLUGIN_NAME} PUBLIC cxx_std_17) 19 | set_target_properties(${PLUGIN_NAME} PROPERTIES 20 | CXX_VISIBILITY_PRESET hidden) 21 | target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) 22 | target_include_directories(${PLUGIN_NAME} INTERFACE 23 | "${CMAKE_CURRENT_SOURCE_DIR}/include") 24 | 25 | target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) 26 | target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) 27 | 28 | # List of absolute paths to libraries that should be bundled with the plugin 29 | set(flutter_qjs_bundled_libraries 30 | "$" 31 | PARENT_SCOPE 32 | ) 33 | 34 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 35 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 36 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 37 | endif() 38 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 39 | 40 | set(QUICKJSC_BRIDGE "libquickjs_c_bridge_plugin.so") 41 | set(QUICKJSC_BRIDGE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/shared/${QUICKJSC_BRIDGE}") 42 | install(CODE "file(REMOVE_RECURSE \"${INSTALL_BUNDLE_LIB_DIR}/${QUICKJSC_BRIDGE}\")" COMPONENT Runtime) 43 | install(FILES "${QUICKJSC_BRIDGE_PATH}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) 44 | -------------------------------------------------------------------------------- /linux/flutter_js_plugin.cc: -------------------------------------------------------------------------------- 1 | #include "include/flutter_js/flutter_js_plugin.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #define FLUTTER_JS_PLUGIN(obj) \ 11 | (G_TYPE_CHECK_INSTANCE_CAST((obj), flutter_js_plugin_get_type(), \ 12 | FlutterJsPlugin)) 13 | 14 | struct _FlutterJsPlugin { 15 | GObject parent_instance; 16 | }; 17 | 18 | G_DEFINE_TYPE(FlutterJsPlugin, flutter_js_plugin, g_object_get_type()) 19 | 20 | // Called when a method call is received from Flutter. 21 | static void flutter_js_plugin_handle_method_call( 22 | FlutterJsPlugin* self, 23 | FlMethodCall* method_call) { 24 | g_autoptr(FlMethodResponse) response = nullptr; 25 | 26 | const gchar* method = fl_method_call_get_name(method_call); 27 | 28 | if (strcmp(method, "getPlatformVersion") == 0) { 29 | struct utsname uname_data = {}; 30 | uname(&uname_data); 31 | g_autofree gchar *version = g_strdup_printf("Linux %s", uname_data.version); 32 | g_autoptr(FlValue) result = fl_value_new_string(version); 33 | response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); 34 | } else { 35 | response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); 36 | } 37 | 38 | fl_method_call_respond(method_call, response, nullptr); 39 | } 40 | 41 | static void flutter_js_plugin_dispose(GObject* object) { 42 | G_OBJECT_CLASS(flutter_js_plugin_parent_class)->dispose(object); 43 | } 44 | 45 | static void flutter_js_plugin_class_init(FlutterJsPluginClass* klass) { 46 | G_OBJECT_CLASS(klass)->dispose = flutter_js_plugin_dispose; 47 | } 48 | 49 | static void flutter_js_plugin_init(FlutterJsPlugin* self) {} 50 | 51 | static void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call, 52 | gpointer user_data) { 53 | FlutterJsPlugin* plugin = FLUTTER_JS_PLUGIN(user_data); 54 | flutter_js_plugin_handle_method_call(plugin, method_call); 55 | } 56 | 57 | // Gets the directory the current executable is in, borrowed from: 58 | // https://github.com/flutter/engine/blob/master/shell/platform/linux/fl_dart_project.cc#L27 59 | // 60 | // Copyright 2013 The Flutter Authors. All rights reserved. 61 | // Use of this source code is governed by a BSD-style license that can be 62 | // found in https://github.com/flutter/engine/blob/master/LICENSE. 63 | static gchar* get_executable_dir() { 64 | g_autoptr(GError) error = nullptr; 65 | g_autofree gchar* exe_path = g_file_read_link("/proc/self/exe", &error); 66 | if (exe_path == nullptr) { 67 | g_critical("Failed to determine location of executable: %s", 68 | error->message); 69 | return nullptr; 70 | } 71 | 72 | return g_path_get_dirname(exe_path); 73 | } 74 | 75 | void flutter_js_plugin_register_with_registrar(FlPluginRegistrar* registrar) { 76 | FlutterJsPlugin* plugin = FLUTTER_JS_PLUGIN( 77 | g_object_new(flutter_js_plugin_get_type(), nullptr)); 78 | 79 | g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); 80 | g_autoptr(FlMethodChannel) channel = 81 | fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar), 82 | "flutter_js", 83 | FL_METHOD_CODEC(codec)); 84 | fl_method_channel_set_method_call_handler(channel, method_call_cb, 85 | g_object_ref(plugin), 86 | g_object_unref); 87 | 88 | g_object_unref(plugin); 89 | // get the current executable dir 90 | g_autofree gchar* executable_dir = get_executable_dir(); 91 | // resolve the shared library path 92 | g_autofree gchar* lib_path = g_build_filename(executable_dir, "lib", "libquickjs_c_bridge_plugin.so", nullptr); 93 | // share the libpath to Dart through an environment variable 94 | setenv("LIBQUICKJSC_PATH", lib_path, 0); 95 | } 96 | 97 | -------------------------------------------------------------------------------- /linux/include/flutter_js/flutter_js_plugin.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_PLUGIN_FLUTTER_JS_PLUGIN_H_ 2 | #define FLUTTER_PLUGIN_FLUTTER_JS_PLUGIN_H_ 3 | 4 | #include 5 | 6 | G_BEGIN_DECLS 7 | 8 | #ifdef FLUTTER_PLUGIN_IMPL 9 | #define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) 10 | #else 11 | #define FLUTTER_PLUGIN_EXPORT 12 | #endif 13 | 14 | typedef struct _FlutterJsPlugin FlutterJsPlugin; 15 | typedef struct { 16 | GObjectClass parent_class; 17 | } FlutterJsPluginClass; 18 | 19 | FLUTTER_PLUGIN_EXPORT GType flutter_js_plugin_get_type(); 20 | 21 | FLUTTER_PLUGIN_EXPORT void flutter_js_plugin_register_with_registrar( 22 | FlPluginRegistrar* registrar); 23 | 24 | G_END_DECLS 25 | 26 | #endif // FLUTTER_PLUGIN_FLUTTER_JS_PLUGIN_H_ 27 | -------------------------------------------------------------------------------- /linux/shared/libquickjs_c_bridge_plugin.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/linux/shared/libquickjs_c_bridge_plugin.so -------------------------------------------------------------------------------- /macos/Classes/FlutterJsPlugin.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | public class FlutterJsPlugin: NSObject, FlutterPlugin { 5 | public static func register(with registrar: FlutterPluginRegistrar) { 6 | let channel = FlutterMethodChannel(name: "flutter_js", binaryMessenger: registrar.messenger) 7 | let instance = FlutterJsPlugin() 8 | registrar.addMethodCallDelegate(instance, channel: channel) 9 | } 10 | 11 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 12 | switch call.method { 13 | case "getPlatformVersion": 14 | result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString) 15 | default: 16 | result(FlutterMethodNotImplemented) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /macos/flutter_js.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint flutter_js.podspec` to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'flutter_js' 7 | s.version = '0.0.1' 8 | s.summary = 'A new flutter plugin project.' 9 | s.description = <<-DESC 10 | A new flutter plugin project. 11 | DESC 12 | s.homepage = 'http://example.com' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'Your Company' => 'email@example.com' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.dependency 'FlutterMacOS' 18 | 19 | s.platform = :osx, '10.11' 20 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } 21 | s.swift_version = '5.0' 22 | end 23 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_js 2 | description: A Javascript engine to use with flutter. 3 | It uses Quickjs on Android and JavascriptCore on IOS 4 | version: 0.8.4 5 | homepage: https://github.com/abner/flutter_js 6 | repository: https://github.com/abner/flutter_js 7 | 8 | environment: 9 | sdk: ">=3.0.0 <4.0.0" 10 | flutter: ">=3.0.0" 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | ffi: ^2.0.0 15 | http: ^1.0.0 16 | sync_http: ^0.3.0 17 | dev_dependencies: 18 | flutter_test: 19 | sdk: flutter 20 | 21 | flutter: 22 | plugin: 23 | platforms: 24 | android: 25 | package: io.abner.flutter_js 26 | pluginClass: FlutterJsPlugin 27 | ios: 28 | pluginClass: FlutterJsPlugin 29 | linux: 30 | #default_package: flutter_js_linux_win 31 | pluginClass: FlutterJsPlugin 32 | macos: 33 | pluginClass: FlutterJsPlugin 34 | # windows: 35 | # default_package: flutter_js_linux_windows 36 | windows: 37 | pluginClass: FlutterJsPlugin 38 | 39 | assets: 40 | - assets/js/ 41 | -------------------------------------------------------------------------------- /test/flutter_js_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_js/flutter_js.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | void main() { 5 | TestWidgetsFlutterBinding.ensureInitialized(); 6 | 7 | late JavascriptRuntime jsRuntime; 8 | 9 | setUp(() { 10 | jsRuntime = getJavascriptRuntime(); 11 | }); 12 | 13 | tearDown(() { 14 | try { 15 | jsRuntime.dispose(); 16 | } on Error catch (_) {} 17 | }); 18 | 19 | test('evaluate javascript', () { 20 | final result = jsRuntime.evaluate('Math.pow(5,3)'); 21 | print('${result.rawResult}, ${result.stringResult}'); 22 | print( 23 | '${result.rawResult.runtimeType}, ${result.stringResult.runtimeType}'); 24 | expect(result.rawResult, equals(125)); 25 | expect(result.stringResult, equals('125')); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | set(PROJECT_NAME "flutter_js") 4 | project(${PROJECT_NAME} LANGUAGES CXX) 5 | add_compile_options("$<$:/utf-8>") 6 | 7 | # This value is used when generating builds using this plugin, so it must 8 | # not be changed 9 | set(PLUGIN_NAME "flutter_js_plugin") 10 | 11 | add_library(${PLUGIN_NAME} SHARED 12 | "flutter_js_plugin.cpp" 13 | ) 14 | apply_standard_settings(${PLUGIN_NAME}) 15 | set_target_properties(${PLUGIN_NAME} PROPERTIES 16 | CXX_VISIBILITY_PRESET hidden) 17 | target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) 18 | target_include_directories(${PLUGIN_NAME} INTERFACE 19 | "${CMAKE_CURRENT_SOURCE_DIR}/include") 20 | target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) 21 | 22 | # List of absolute paths to libraries that should be bundled with the plugin 23 | set(flutter_js_bundled_libraries 24 | "" 25 | PARENT_SCOPE 26 | ) 27 | 28 | 29 | set(BUILD_BUNDLE_DIR "$") 30 | # Make the "install" step default, as it's required to run. 31 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 32 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 33 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 34 | endif() 35 | 36 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 37 | set(QUICKJSC_BRIDGE "quickjs_c_bridge.dll") 38 | set(QUICKJSC_BRIDGE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/shared/${QUICKJSC_BRIDGE}") 39 | install(CODE "file(REMOVE_RECURSE \"${INSTALL_BUNDLE_LIB_DIR}/${QUICKJSC_BRIDGE}\")" COMPONENT Runtime) 40 | install(FILES "${QUICKJSC_BRIDGE_PATH}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) -------------------------------------------------------------------------------- /windows/flutter_js_plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "include/flutter_js/flutter_js_plugin.h" 2 | 3 | // This must be included before many other Windows headers. 4 | #include 5 | 6 | // For getPlatformVersion; remove unless needed for your plugin implementation. 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace { 18 | 19 | class FlutterJsPlugin : public flutter::Plugin { 20 | public: 21 | static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar); 22 | 23 | FlutterJsPlugin(); 24 | 25 | virtual ~FlutterJsPlugin(); 26 | 27 | private: 28 | // Called when a method is called on this plugin's channel from Dart. 29 | void HandleMethodCall( 30 | const flutter::MethodCall &method_call, 31 | std::unique_ptr> result); 32 | }; 33 | 34 | // static 35 | void FlutterJsPlugin::RegisterWithRegistrar( 36 | flutter::PluginRegistrarWindows *registrar) { 37 | auto channel = 38 | std::make_unique>( 39 | registrar->messenger(), "flutter_js", 40 | &flutter::StandardMethodCodec::GetInstance()); 41 | 42 | auto plugin = std::make_unique(); 43 | 44 | channel->SetMethodCallHandler( 45 | [plugin_pointer = plugin.get()](const auto &call, auto result) { 46 | plugin_pointer->HandleMethodCall(call, std::move(result)); 47 | }); 48 | 49 | registrar->AddPlugin(std::move(plugin)); 50 | } 51 | 52 | FlutterJsPlugin::FlutterJsPlugin() {} 53 | 54 | FlutterJsPlugin::~FlutterJsPlugin() {} 55 | 56 | void FlutterJsPlugin::HandleMethodCall( 57 | const flutter::MethodCall &method_call, 58 | std::unique_ptr> result) { 59 | if (method_call.method_name().compare("getPlatformVersion") == 0) { 60 | std::ostringstream version_stream; 61 | version_stream << "Windows "; 62 | if (IsWindows10OrGreater()) { 63 | version_stream << "10+"; 64 | } else if (IsWindows8OrGreater()) { 65 | version_stream << "8"; 66 | } else if (IsWindows7OrGreater()) { 67 | version_stream << "7"; 68 | } 69 | result->Success(flutter::EncodableValue(version_stream.str())); 70 | } else { 71 | result->NotImplemented(); 72 | } 73 | } 74 | 75 | } // namespace 76 | 77 | void FlutterJsPluginRegisterWithRegistrar( 78 | FlutterDesktopPluginRegistrarRef registrar) { 79 | FlutterJsPlugin::RegisterWithRegistrar( 80 | flutter::PluginRegistrarManager::GetInstance() 81 | ->GetRegistrar(registrar)); 82 | } 83 | -------------------------------------------------------------------------------- /windows/include/flutter_js/flutter_js_plugin.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_PLUGIN_FLUTTER_JS_PLUGIN_H_ 2 | #define FLUTTER_PLUGIN_FLUTTER_JS_PLUGIN_H_ 3 | 4 | #include 5 | 6 | #ifdef FLUTTER_PLUGIN_IMPL 7 | #define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) 8 | #else 9 | #define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) 10 | #endif 11 | 12 | #if defined(__cplusplus) 13 | extern "C" { 14 | #endif 15 | 16 | FLUTTER_PLUGIN_EXPORT void FlutterJsPluginRegisterWithRegistrar( 17 | FlutterDesktopPluginRegistrarRef registrar); 18 | 19 | #if defined(__cplusplus) 20 | } // extern "C" 21 | #endif 22 | 23 | #endif // FLUTTER_PLUGIN_FLUTTER_JS_PLUGIN_H_ 24 | -------------------------------------------------------------------------------- /windows/shared/quickjs_c_bridge.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abner/flutter_js/efdae8c40b46cd31a500d4e57be365e029a9ef06/windows/shared/quickjs_c_bridge.dll --------------------------------------------------------------------------------