This guide explains how to add basic Codox integration with QuillJS into Flutter application with
InAppWebView plugin
GitHub
Full integration example can be found here
Main Steps of the Integration
Flutter loads local html, which contains prepared javascript code with quill editor and codox init
flutter app invokes js function to setup initial editor state.
flutter app adds listeners (callbacks) for codox hooks and error events.
flutter app triggers js function which starts codox with config.
When codox hook is triggered in javascript, the flutter corresponding callback is invoked and data is passed to flutter.
Basic Integration
Begin by preparing local files in the project.
Create seprate directory assets/ in project root to store html and javascript files
Prepare local html
Navigate to created directory assets/ and create local html file quill.html there
Download Codox lib
Create separate directory codoxLib in the project root for downloading Codox lib
For downloading Codox Lib need NodeJS to be installed in local machine. Before proceed to next step, check if nodejs is installed
Navigate to codoxLib/ and create package.json file for javascript dependencies
Insert Codox lib @codoxhq/quill-provider dependency into package.json file
"@codoxhq/quill-provider" : " ^1.0.1 "
Install Codox lib locally
# will create node_modules/ dir and download codox provider inside
Copy downloaded Codox lib file into assets/ to place the lib with local html file
# copy from source dir to local target dir with renaming to codox.js
cp codoxLib/node_modules/@codoxhq/quill-provider/dist/index.js assets/codox.js
At this step, should have assets/ directory with following structure:
QuillJS Editor init
In local html quill.html add the following init Quill editor code.
< title > Codox+QuillJS Integration </ title >
<!-- Include quill css stylesheet -->
href = " https://cdn.quilljs.com/1.3.7/quill.snow.css "
<!-- Include the Quill library -->
< script src = " https://cdn.quilljs.com/1.3.7/quill.js " ></ script >
<!-- Import codox lib core from local file -->
< script src = " file:///android_asset/flutter_assets/assets/codox.js " ></ script >
<!-- Create the editor container -->
// create editor toolbar options
[{ header: 1 }, { header: 2 }],
[{ list: ' ordered ' }, { list: ' bullet ' }],
[{ script: ' sub ' }, { script: ' super ' }],
[{ intent: ' -1 ' }, { intent: ' +1 ' }],
[{ size: [ ' small ' , false , ' large ' , ' huge ' ] }],
[{ header: [ 1 , 2 , 3 , 4 , 5 , 6 , false ] }],
[{ color: [] }, { background: [] }],
// create Quill Editor instance
var quill = new Quill ( ' #editor ' , {
toolbar: toolbarOptions , // tollbar options
theme: ' snow ' , // css theme
placeholder: ' Enter some text... ' ,
InAppWebView init
In flutter app in lib/main.dart init InAppWebView with local html with Quill editor load.
import 'package:flutter/material.dart' ;
import 'package:flutter/services.dart' show rootBundle; // Import rootBundle
import 'package:flutter_inappwebview/flutter_inappwebview.dart' ;
WidgetsFlutterBinding . ensureInitialized ();
theme : ThemeData (useMaterial3 : true ),
home : const MyWebViewPage (),
class MyWebViewPage extends StatefulWidget {
const MyWebViewPage ({ super .key});
_MyWebViewPageState createState () => _MyWebViewPageState ();
class _MyWebViewPageState extends State < MyWebViewPage > {
late InAppWebViewController _webViewController;
Widget build ( BuildContext context) {
body : FutureBuilder < String >(
future : _loadLocalHtml (),
builder : (context, snapshot) {
if (snapshot.connectionState == ConnectionState .done) {
initialData : InAppWebViewInitialData (
* Setting baseUrl is essential for codox sync to work:
* base url is considered by codox as "domain" which is allowed by codox subscription.
* Example: codox subscription has whitelisted domain "flutter_demo.app", configured in codox dashboard,
* then here need to specify baseUrl with http prefix, like "http://flutter_demo.app"
baseUrl : Uri . parse ( "http://[domain from Codox subscription]" ),
initialOptions : InAppWebViewGroupOptions (
crossPlatform : InAppWebViewOptions (
true , // Important: this must be enabled for js to work in local html
allowUniversalAccessFromFileURLs : true ,
allowFileAccessFromFileURLs : true ,
Future < String > _loadLocalHtml () async {
// Load the local HTML file content
final String htmlString =
await rootBundle. loadString ( 'assets/quill.html' );
if (htmlString.isEmpty) {
throw Exception ( 'HTML content is empty' );
print ( 'Error loading HTML file: $ e ' );
Initial Editor Content
Before initiating or joining a Codox session, the latest version of the document content must be available and must be setup in editor. The following code shows how to set up initial content from flutter app into quill editor.
In quill.html need to add javascript function to set init content to quill editor:
var quill = new Quill ( ' #editor ' , {
// callback to set initial state for editor
function setQuillInitState ( initQuillState ) {
// Set initial content to Quill
quill . setContents ( initQuillState , ' silent ' );
In flutter in lib/main.dart need to add “onLoadStop” callback to InAppWebView and invoke javascript inside it. OnLoadStop is invoked when page is fully loaded.
class _MyWebViewPageState extends State < MyWebViewPage > {
Widget build ( BuildContext context) {
body : FutureBuilder < String >(
builder : (context, snapshot) {
if (snapshot.connectionState == ConnectionState .done) {
onLoadStop : (controller, url) async {
// set initial quill editor state
String initQuillStateJSON = "" ; // json is expected here
// invoke javascript function and pass state as arg
await _webViewController. evaluateJavascript (
source : "setQuillInitState( $ initQuillStateJSON )" );
Start Codox Session
Setup start session code in quill.html.
var codox = null ; // codox instance will be assigned
// create Quill Editor instance
var quill = new Quill ( ' #editor ' , {
// function to start codox session
function startCodox ( /* can pass params here, e.g. docId, apiKey, username */ ) {
docId: ' demo_docId ' , //this is the unique id used to distinguish different documents
username: ' demo_username ' , //unique user name
apiKey: ' [your apiKey here] ' , // codox apiKey
codox . start ( codoxConfig );
Setup flutter code to invoke javascript to start codox session inside OnLoadStop callback:
Widget build ( BuildContext context) {
body : FutureBuilder < String >(
future : _loadLocalHtml (),
builder : (context, snapshot) {
if (snapshot.connectionState == ConnectionState .done) {
initialData : InAppWebViewInitialData (
initialOptions : InAppWebViewGroupOptions (
onWebViewCreated : (controller) {
_webViewController = controller;
onLoadStop : (controller, url) async {
* When page is fully loaded:
* - set initial state to editor
// set initial quill editor state
String initQuillStateJSON = "" ;
await _webViewController. evaluateJavascript (
source : "setQuillInitState( $ initQuillStateJSON )" );
await _webViewController. evaluateJavascript (
Codox hooks and error events
To subscribe to codox hooks and error events, need to add related code both on javascript and flutter sides.
In quill.html need to add hooks and error events callbacks to codox config. The callbacks contain code which sends data to flutter
var codox = null ; // codox instance will be assigned
// create Quill Editor instance
var quill = new Quill ( ' #editor ' , {
// callback for fetchDocOnNetworkReconnect hook
async function fetchDocOnNetworkReconnectHook () {
// invoke flutter handler and wait for response
const data = await window . flutter_inappwebview . callHandler (
' fetchDocOnNetworkReconnectHookHandler '
// callback for usersUpdate hook
function usersUpdateHook ( data ) {
const json = JSON . stringify ( data );
window . flutter_inappwebview . callHandler (
' usersUpdateHookHandler ' ,
// callback for contentChanged hook
function contentChangedHook ( data ) {
const json = JSON . stringify ( data );
window . flutter_inappwebview . callHandler (
' contentUpdatedHookHandler ' ,
// callback for codox error events
function onCodoxError ( data ) {
// pass data to flutter when errors from codox
window . flutter_inappwebview . callHandler (
' codoxErrorEventListener ' ,
// subscribe to codox error events and pass callback
codox . on ( ' error ' , onCodoxError );
// demo username - random number generation
let username = ` user_ ${ Math . round ( Math . random () * 1000 ) } ` ;
docId: ' demo_docId ' , //this is the unique id used to distinguish different documents
username: ' demo_username ' , //unique user name
apiKey: ' [your apiKey here] ' , // codox apiKey
// provide hooks callbacks in config
fetchDocOnNetworkReconnect: fetchDocOnNetworkReconnectHook ,
usersUpdate: usersUpdateHook ,
contentChanged: contentChangedHook ,
codox . start ( codoxConfig );
On flutter side need to implement the handling of javascript messages
Widget build ( BuildContext context) {
body : FutureBuilder < String >(
future : _loadLocalHtml (),
builder : (context, snapshot) {
if (snapshot.connectionState == ConnectionState .done) {
initialData : InAppWebViewInitialData (
initialOptions : InAppWebViewGroupOptions (
onWebViewCreated : (controller) {
_webViewController = controller;
* Add listeners to codox hooks:
* - fetchDocOnNetworkReconnectHook
* + add listener to codox errors
_webViewController. addJavaScriptHandler (
handlerName : "usersUpdateHookHandler" ,
print ( "[usersUpdateHookHandler]: $ users " );
_webViewController. addJavaScriptHandler (
handlerName : "contentUpdatedHookHandler" ,
print ( "[contentUpdatedHookHandler]: $ data " );
_webViewController. evaluateJavascript (
source : "getQuillEditorState()" );
print ( "quill editor full state json: $ fullState " );
_webViewController. addJavaScriptHandler (
handlerName : "fetchDocOnNetworkReconnectHookHandler" ,
"[fetchDocOnNetworkReconnectHookHandler] invoked" );
// fetch state from backend
// response must match schema: {content: {ops: [...]}, timestamp}
_webViewController. addJavaScriptHandler (
handlerName : "codoxErrorEventListener" ,
print ( "[codoxErrorEventListener]: $ data " );
Github
Working integration example can be found here