Flutter Widget Integration

A complete guide to embedding and configuring a Flutter web widget inside Zoho CRM using the Zoho Embedded App SDK.


1. Overview

This document describes how to add the Zoho Embedded App SDK script to an existing Flutter Web project so that the Flutter widget runs inside a Zoho CRM panel and communicates correctly with the Zoho JS SDK.

What the Integration Does

  • Loads the Zoho Embedded App SDK from the Zoho CDN.

  • Initialises the SDK on page load so the widget can call Zoho CRM APIs.

  • Automatically resizes the widget panel to fill the available space in Zoho CRM.

  • Bootstraps the compiled Flutter web app through the standard flutter_bootstrap.js entry point.

Prerequisites

Requirement

Details

Flutter SDK

3.x or later with web support enabled

Zoho CRM account

Developer or above plan with Widget Builder access

Zoho Embedded App SDK

Loaded via CDN (no local install needed)

Web server / hosting

Any static host or Zoho Widget deployment target


2. Project File Structure

The integration targets the index.html file that Flutter generates inside the web/ directory of your project.

my_flutter_app/
  web/
    index.html          <-- Primary file to modify
    manifest.json
    flutter_bootstrap.js
    icons/
  lib/
  pubspec.yaml


3. Complete index.html

Replace the entire content of web/index.html with the code below. Every section is explained in detail in the following chapter.

<!DOCTYPE html>
<html>

<head>
  <base href="$FLUTTER_BASE_HREF">

  <meta charset="UTF-8">
  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
  <meta name="description" content="Flutter Zoho Widget">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>flutter_map_web</title>
  <link rel="manifest" href="manifest.json">

  <style>
    html,
    body {
      height: 100%;
      width: 100%;
      margin: 0;
      padding: 0;
      overflow: hidden;
    }

    #flutter_container {
      height: 100%;
      width: 100%;
    }
  </style>

  <script src="https://live.zwidgets.com/js-sdk/1.2/ZohoEmbededAppSDK.min.js"></script>
</head>

<body>
  <div id="flutter_container"></div>

  <script>

    function resizeWidget() {
      if (window.ZOHO && ZOHO.CRM && ZOHO.CRM.UI) {
        ZOHO.CRM.UI.Resize({
          height: "100%",
          width: "100%"
        });
      }
    }

    window.addEventListener("load", function () {
      if (window.ZOHO) {
        ZOHO.embeddedApp.on("PageLoad", function () {
          resizeWidget();
        });

        ZOHO.embeddedApp
          .init()
          .then(function () {
            console.log("Zoho SDK Initialized");
          })
          .catch(function (error) {
            console.error("Zoho SDK Init Failed:", error);
          });
      }
    });
  </script>

  <script src="flutter_bootstrap.js" async></script>
</body>

</html>


4. Code Explanation

4.1 <head> Section

4.1.1 base href

<base href="$FLUTTER_BASE_HREF">

Flutter replaces the $FLUTTER_BASE_HREF placeholder at build time with the correct sub-path for the deployment. This tag is mandatory for Flutter web asset resolution and must remain exactly as shown.


4.1.2 Viewport Meta Tag

<meta name="viewport" content="width=device-width, initial-scale=1.0">

Ensures the widget renders correctly on both desktop Zoho CRM panels and mobile browsers. Without this, the Flutter canvas may render at the wrong scale inside the CRM panel.


4.1.3 CSS Reset

html, body {
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
}
#flutter_container { height: 100%; width: 100%; }

Removes all default browser spacing so the Flutter canvas fills the Zoho CRM panel edge-to-edge. overflow: hidden prevents unwanted scrollbars inside the panel.


4.1.4 Zoho SDK Script

<script src="https://live.zwidgets.com/js-sdk/1.2/ZohoEmbededAppSDK.min.js"></script>

Loads the Zoho Embedded App SDK from the official Zoho CDN. This script must be placed in <head> and loaded before the Zoho initialisation code in <body> runs. It exposes the global ZOHO object used throughout the integration.

NOTE: Do not host this file locally. Zoho requires the CDN version for authentication tokens and API routing to work correctly.


4.2 <body> Section

4.2.1 Flutter Container Div

<div id="flutter_container"></div>

Flutter web renders its canvas into this div. The id must match what flutter_bootstrap.js expects. The CSS defined earlier stretches it to fill the entire viewport.


4.2.2 resizeWidget Helper Function

function resizeWidget() {
  if (window.ZOHO && ZOHO.CRM && ZOHO.CRM.UI) {
    ZOHO.CRM.UI.Resize({
      height: "100%",
      width: "100%"
    });
  }
}

Calls the Zoho CRM UI API to resize the widget panel to 100% of its container. The guard checks that ZOHO.CRM.UI is available before calling it, which prevents console errors when the page is opened outside of Zoho (e.g. during local development).


4.2.3 SDK Initialisation Block

window.addEventListener("load", function () {
  if (window.ZOHO) {
    ZOHO.embeddedApp.on("PageLoad", function () {
      resizeWidget();
    });

    ZOHO.embeddedApp
      .init()
      .then(function () {
        console.log("Zoho SDK Initialized");
      })
      .catch(function (error) {
        console.error("Zoho SDK Init Failed:", error);
      });
  }
});

This is the core SDK bootstrap. Each part explained:

Code

Purpose

window.addEventListener("load", ...)

Waits until all resources (including the Zoho SDK script) are fully loaded before running.

if (window.ZOHO)

Safety check. If the page is opened outside Zoho (e.g. localhost), the SDK script will not inject the ZOHO global, and this block is safely skipped.

ZOHO.embeddedApp.on("PageLoad", ...)

Registers a callback that fires each time Zoho CRM loads or reloads the widget panel. The resize call is placed here so panel dimensions are set on every load.

ZOHO.embeddedApp.init()

Authenticates the widget with Zoho CRM, fetches the CRM context (logged-in user, module, record), and resolves the promise. All subsequent ZOHO.CRM API calls must be made after this resolves.

.then(...)

Called when init() succeeds. Replace the console.log with any startup logic your Flutter widget needs.

.catch(...)

Called when init() fails. Add error handling or a user-facing message here.


4.2.4 Flutter Bootstrap Script

<script src="flutter_bootstrap.js" async></script>

Loads the Flutter web entry point. The async attribute allows the browser to continue parsing the HTML and running the Zoho initialisation block in parallel, which reduces widget startup time. Flutter will render into #flutter_container once its own initialisation is complete.


5. Build & Deployment

5.1 Build the Flutter Web App

# Standard release build
flutter build web

# If deployed to a sub-path, set the base href manually
flutter build web --base-href /your-sub-path/

The build output is placed in build/web/. The web/index.html you edited is copied into this output directory with $FLUTTER_BASE_HREF replaced automatically.

5.2 Upload to Zoho CRM Widget Builder

  1. Open Zoho CRM and go to Settings > Developer Space > Widgets.

  2. Click Create Widget and choose the widget type (Detail View, List View, etc.).

  3. Upload the entire contents of build/web/ as a ZIP file.

  4. Set the Widget URL to the index.html at the root of the uploaded bundle.

  5. Save and preview the widget inside Zoho CRM.

NOTE: If you update the Flutter app, rebuild with flutter build web and re-upload the new build/web/ ZIP. The Zoho SDK script URL does not need to change.


6. Local Development & Testing

6.1 Running Outside Zoho

When you run flutter run -d chrome, the ZOHO global will not exist. The if (window.ZOHO) guard means the SDK block is silently skipped, so the Flutter app still loads and renders normally.

To simulate the Zoho environment locally, you can stub the ZOHO global in a separate dev-only script:

// dev_stub.js — do NOT include in production build
window.ZOHO = {
  embeddedApp: {
    on: function(event, cb) { if (event === "PageLoad") cb(); },
    init: function() { return Promise.resolve(); }
  },
  CRM: {
    UI: { Resize: function(o) { console.log("Resize called", o); } }
  }
};


7. Calling Zoho CRM APIs from Flutter

Once the SDK is initialised you can call Zoho CRM APIs from JavaScript and pass data to Flutter using postMessage or dart:js interop.

7.1 Example: Get Current Record

// After ZOHO.embeddedApp.init() resolves:
ZOHO.CRM.API.getRecord({ Entity: "Leads", RecordID: recordId })
  .then(function(data) {
    // Forward data to Flutter via a custom DOM event
    window.dispatchEvent(
      new CustomEvent("zoho-data", { detail: data })
    );
  });

7.2 Receiving Data in Flutter (dart:js_interop)

import 'dart:js_interop';

void listenForZohoData() {
  window.addEventListener('zoho-data', (event) {
    final detail = (event as CustomEvent).detail;
    // Use detail in your Flutter widget state
  }.toJS);
}


8. Troubleshooting

Symptom

Solution

Widget shows blank panel in Zoho

Check browser console for errors. Ensure flutter_bootstrap.js is present in the uploaded ZIP.

Zoho SDK Init Failed in console

Verify the widget is opened inside Zoho CRM, not directly in a browser. Check that the Zoho SDK CDN URL is accessible.

Panel does not fill the Zoho frame

Confirm the CSS reset (margin:0, height:100%) is applied and ZOHO.CRM.UI.Resize is being called in the PageLoad callback.

$FLUTTER_BASE_HREF appears literally

You are viewing index.html directly. Run flutter build web to let the Flutter tool replace the placeholder.

Flutter assets return 404

Rebuild with the correct --base-href flag matching the Zoho widget deployment path.


9. Quick Reference

Item

Value

Zoho SDK CDN URL

https://live.zwidgets.com/js-sdk/1.2/ZohoEmbededAppSDK.min.js

SDK global object

window.ZOHO

Init method

ZOHO.embeddedApp.init()

Page load event

ZOHO.embeddedApp.on("PageLoad", callback)

Resize API

ZOHO.CRM.UI.Resize({ height, width })

Flutter container id

flutter_container

Flutter entry script

flutter_bootstrap.js

Build output directory

build/web/

Build command

flutter build web


End of Document

On this page