Skip to Content
GuidesMobile Integration

Mobile Integration

Embed GoCharting SDK inside a React Native, Flutter, Android, or iOS WebView. The SDK posts chart events to your native shell; your app owns chrome (toolbars, sheets, order tickets) and drives the chart with injected JavaScript.

Overview

FlagWhat you get
touchMode: trueFull JS mobile Chart-tab UX (TopBar + BottomBar + JS ContextMenu + Layers)
isNativeApp: trueMobile canvas only — JS bars hidden, JS ContextMenu suppressed, every TerminalMobile appCallback also posted via sendToNative

Compare touchMode true vs false

Interactive lab with a TradingView-style Desktop / Mobile device preview:

  • Desktop — single chart with the desktop shell (touchMode: false)
  • Mobile — two phone frames side by side: left touchMode: true, right touchMode: false (same narrow size so the chrome difference is obvious)

Open lab in a new tab

Source: examples/touchmode-compare-lab.html in the SDK package.

For native shells, use isNativeApp: true. Prefer not combining with touchMode unless you also want the JS toolbars.

Bridge flow (native WebView):

  1. SDK (isNativeApp: true) posts events via sendToNative({ type, ... })
  2. Native WebView bridge receives them and drives host toolbars / sheets / menus
  3. Host calls back into the chart with evaluateJavascript / inject JS

1. Web page loaded in the WebView

Host a minimal page that creates the chart. Keep a global handle so native code can call chart APIs.

<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" /> <style> html, body, #chart { margin: 0; height: 100%; width: 100%; overflow: hidden; } </style> </head> <body> <div id="chart"></div> <script src="https://gocharting.com/sdk/library/index.umd.js"></script> <script> const { createChart } = window.GoChartingSDK; // Expose for native → web commands window.chart = createChart("#chart", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1m", datafeed: window.myDatafeed, // your datafeed licenseKey: "YOUR_LICENSE_KEY", isNativeApp: true, // Optional: also handle events in JS appCallback: (type, msg) => { console.log("[appCallback]", type, msg); }, }); </script> </body> </html>

With isNativeApp: true the SDK:

  • Renders the mobile chart canvas (no JS TopBar / BottomBar)
  • Suppresses the JS ContextMenu and emits OPEN_CONTEXT_MENU
  • Keeps Layers sheets in JS (for now)
  • Posts every TerminalMobile appCallback to the native bridge

2. How the bridge works (web → native)

Internally the SDK uses sendToNative / sendAppCallbackToNative:

// Pseudocode of what the SDK does on every TerminalMobile appCallback function sendToNative(message) { const payload = JSON.stringify(message); if (window.ReactNativeWebView) { window.ReactNativeWebView.postMessage(payload); return; } if (window.Flutter) { window.Flutter.postMessage(payload); return; } if (window.Android) { window.Android.postMessage(payload); return; } if (window.webkit?.messageHandlers?.ios) { window.webkit.messageHandlers.ios.postMessage(message); return; } }

Message shape

{ "type": "OPEN_CONTEXT_MENU", "chartId": "MAIN_CHART", "x": 120, "y": 340, "targetType": "object", "objectId": "drawing-123", "details": { "type": "object", "objectId": "drawing-123" }, "security": { "symbol": "BTCUSDT" }, "interval": "1m" }
  • type is always the appCallback event name (OPEN_CONTEXT_MENU, PLACE_ORDER, …).
  • If the payload had its own type (e.g. hit-test "chart" / "object"), it is preserved as targetType.

Common events

typeWhen
OPEN_CONTEXT_MENULong-press / right-click on the chart
DOWNLOAD_MORE_DATAUser pans past available history
PLACE_ORDERBuy / Sell from OHLC (or trading UI)
CHART_SELECTEDChart pane selected (multi-chart)
OPEN_SYMBOL_SEARCHSymbol tap requesting search UI

See the Configuration API and Events for the full event surface.

3. Native host — receive messages

React Native (react-native-webview)

Inject window.ReactNativeWebView automatically. Listen with onMessage:

import { WebView } from "react-native-webview"; function ChartScreen() { const onMessage = (event) => { const msg = JSON.parse(event.nativeEvent.data); switch (msg.type) { case "OPEN_CONTEXT_MENU": // Show your native action sheet at msg.x / msg.y openContextMenu(msg); break; case "PLACE_ORDER": openOrderTicket(msg); break; case "DOWNLOAD_MORE_DATA": requestMoreBars(msg); break; default: console.log("chart event", msg.type, msg); } }; return ( <WebView source={{ uri: "https://your.app/chart.html" }} onMessage={onMessage} javaScriptEnabled domStorageEnabled allowsInlineMediaPlayback style={{ flex: 1 }} /> ); }

Flutter (webview_flutter + JS channel)

Register a channel named Flutter so window.Flutter.postMessage works:

import 'dart:convert'; import 'package:webview_flutter/webview_flutter.dart'; late final WebViewController controller; void initChartWebView() { controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..addJavaScriptChannel( 'Flutter', onMessageReceived: (JavaScriptMessage message) { final msg = jsonDecode(message.message) as Map<String, dynamic>; switch (msg['type']) { case 'OPEN_CONTEXT_MENU': openContextMenu(msg); break; case 'PLACE_ORDER': openOrderTicket(msg); break; default: debugPrint('chart event ${msg['type']}'); } }, ) ..loadRequest(Uri.parse('https://your.app/chart.html')); }

Android (Kotlin — JavascriptInterface)

Expose an object named Android with postMessage(String):

class ChartBridge(private val activity: Activity) { @JavascriptInterface fun postMessage(payload: String) { val msg = JSONObject(payload) when (msg.getString("type")) { "OPEN_CONTEXT_MENU" -> activity.runOnUiThread { openContextMenu(msg) } "PLACE_ORDER" -> activity.runOnUiThread { openOrderTicket(msg) } } } } webView.settings.javaScriptEnabled = true webView.addJavascriptInterface(ChartBridge(this), "Android") webView.loadUrl("https://your.app/chart.html")

iOS (Swift — WKScriptMessageHandler)

Register a handler named ios (window.webkit.messageHandlers.ios):

class ChartMessageHandler: NSObject, WKScriptMessageHandler { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { guard let body = message.body as? [String: Any], let type = body["type"] as? String else { return } switch type { case "OPEN_CONTEXT_MENU": openContextMenu(body) case "PLACE_ORDER": openOrderTicket(body) default: print("chart event", type) } } } let contentController = WKUserContentController() contentController.add(ChartMessageHandler(), name: "ios") let config = WKWebViewConfiguration() config.userContentController = contentController let webView = WKWebView(frame: .zero, configuration: config) webView.load(URLRequest(url: URL(string: "https://your.app/chart.html")!))

Note: On iOS the SDK posts the object (not a JSON string). On RN / Flutter / Android the payload is a JSON string.

4. Native → WebView (drive the SDK)

Keep window.chart (or your own global) from step 1. Inject JavaScript from the host:

React Native

const webRef = useRef(null); const setSymbol = (symbol) => { webRef.current?.injectJavaScript(` window.chart && window.chart.setSymbol(${JSON.stringify(symbol)}); true; `); }; const setInterval = (interval) => { webRef.current?.injectJavaScript(` window.chart && window.chart.setInterval(${JSON.stringify(interval)}); true; `); }; <WebView ref={webRef} /* ... */ />;

Flutter

Future<void> setSymbol(String symbol) async { final js = 'window.chart && window.chart.setSymbol(${jsonEncode(symbol)});'; await controller.runJavaScript(js); }

Android

fun setSymbol(symbol: String) { val escaped = JSONObject.quote(symbol) webView.evaluateJavascript( "window.chart && window.chart.setSymbol($escaped);", null ) }

iOS

func setSymbol(_ symbol: String) { let js = "window.chart && window.chart.setSymbol(\(String(reflecting: symbol)));" webView.evaluateJavaScript(js, completionHandler: nil) }

Typical commands you’ll wire to your native chrome:

Native UI actionInjected call
Symbol pickedwindow.chart.setSymbol("EXCHANGE:SYMBOL")
Interval changedwindow.chart.setInterval("5m")
Theme togglewindow.chart.setTheme("dark")
Layout / chart typeUse the matching chart API methods from the Chart API

5. End-to-end flow example

Long-press on a drawing

  1. User long-presses → SDK emits OPEN_CONTEXT_MENU (JS menu not shown).
  2. sendToNative posts { type: "OPEN_CONTEXT_MENU", targetType: "object", objectId, x, y, ... }.
  3. Native shows an action sheet (Edit / Delete / …).
  4. On “Delete”, native injects JS that calls your chart / layers API (or dispatches a custom event your page listens for).

Buy from OHLC

  1. User taps Buy → PLACE_ORDER appCallback.
  2. Native receives { type: "PLACE_ORDER", ... } and opens your order ticket.
  3. After fill, native updates positions via your trading bridge / datafeed as usual.

6. Checklist

  • Load chart HTML with isNativeApp: true
  • Expose window.chart (or equivalent) for injectJS
  • Register the bridge name your platform expects (ReactNativeWebView / Flutter / Android / ios)
  • Parse JSON on RN / Flutter / Android; read object on iOS
  • Handle at least OPEN_CONTEXT_MENU, PLACE_ORDER, DOWNLOAD_MORE_DATA
  • Build native chrome for symbol search, intervals, drawings, orders
Last updated on