TradingView Lightweight Charts Integration in Mobile Exchange App

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
TradingView Lightweight Charts Integration in Mobile Exchange App
Medium
~3-5 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1054
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

TradingView Lightweight Charts Integration in Mobile Exchange App

TradingView Lightweight Charts — JavaScript library for financial charts weighing ~45KB gzip. Used in production by Coinbase, OKX, Gate.io. On mobile — runs inside WebView, giving all library capabilities without implementing native candlestick from scratch.

Architecture: WebView Bridge

Integration built on bidirectional bridge: native app sends data to WebView via JavaScript, WebView signals back on events (candle tap, crosshair movement).

On Flutter — webview_flutter (official from Google):

// WebViewController initialization
late final WebViewController _webViewController;

@override
void initState() {
  super.initState();
  _webViewController = WebViewController()
    ..setJavaScriptMode(JavaScriptMode.unrestricted)
    ..addJavaScriptChannel(
      'FlutterBridge',
      onMessageReceived: (message) {
        final data = jsonDecode(message.message);
        if (data['type'] == 'crosshair') {
          _onCrosshairUpdate(data['candle']);
        }
      },
    )
    ..loadFlutterAsset('assets/chart/index.html');
}

// Send data to WebView
Future<void> setChartData(List<Candle> candles) async {
  final json = jsonEncode(candles.map((c) => {
    'time': c.timestamp ~/ 1000, // Lightweight Charts expects seconds
    'open': c.open,
    'high': c.high,
    'low': c.low,
    'close': c.close,
  }).toList());
  await _webViewController.runJavaScript('window.setData($json)');
}

HTML/JS Part: Initialization and API

Minimal assets/chart/index.html:

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { background: #131722; overflow: hidden; }
    #chart { width: 100vw; height: 100vh; }
  </style>
</head>
<body>
  <div id="chart"></div>
  <script src="lightweight-charts.standalone.production.js"></script>
  <script>
    const chart = LightweightCharts.createChart(document.getElementById('chart'), {
      layout: { background: { color: '#131722' }, textColor: '#d1d4dc' },
      grid: { vertLines: { color: '#1e2130' }, horzLines: { color: '#1e2130' } },
      crosshair: { mode: LightweightCharts.CrosshairMode.Normal },
      rightPriceScale: { borderColor: '#2a2e39' },
      timeScale: { borderColor: '#2a2e39', timeVisible: true, secondsVisible: false },
    });

    const candleSeries = chart.addCandlestickSeries({
      upColor: '#26a69a', downColor: '#ef5350',
      borderDownColor: '#ef5350', borderUpColor: '#26a69a',
      wickDownColor: '#ef5350', wickUpColor: '#26a69a',
    });

    const volumeSeries = chart.addHistogramSeries({
      color: '#26a69a',
      priceFormat: { type: 'volume' },
      priceScaleId: 'volume',
      scaleMargins: { top: 0.8, bottom: 0 },
    });

    // Subscribe to crosshair — send data to Flutter
    chart.subscribeCrosshairMove(param => {
      if (param.seriesData.has(candleSeries)) {
        const candle = param.seriesData.get(candleSeries);
        FlutterBridge.postMessage(JSON.stringify({ type: 'crosshair', candle }));
      }
    });

    // Called from Flutter
    window.setData = function(candles) {
      candleSeries.setData(candles);
      // Volume separately
      const volumeData = candles.map(c => ({
        time: c.time,
        value: c.volume || 0,
        color: c.close >= c.open ? '#26a69a44' : '#ef535044',
      }));
      volumeSeries.setData(volumeData);
      chart.timeScale().fitContent();
    };

    window.updateLastCandle = function(candle) {
      candleSeries.update(candle);
    };

    window.setTimeframe = function(timeframe) {
      // Request new data handled on Flutter side
      FlutterBridge.postMessage(JSON.stringify({ type: 'timeframe_change', timeframe }));
    };
  </script>
</body>
</html>

Real-Time Updates

WebSocket tick → Flutter → call updateLastCandle in WebView:

void onTickReceived(Tick tick) {
  _updateLocalCandle(tick);
  final candleJson = jsonEncode({
    'time': _lastCandle.timestamp ~/ 1000,
    'open': _lastCandle.open,
    'high': _lastCandle.high,
    'low': _lastCandle.low,
    'close': _lastCandle.close,
  });
  _webViewController.runJavaScript('window.updateLastCandle($candleJson)');
}

candleSeries.update() in Lightweight Charts updates only last candle without redrawing entire chart. This is optimized — library does it right.

Integration Pitfalls

Viewport meta. Without maximum-scale=1.0 iOS Safari enables user zoom on double-tap — interface falls apart. On Android — WebSettings.setSupportZoom(false).

White flash on load. WebView renders white background before HTML loads. Solution — backgroundColor of WebView matches chart background (#131722), show CircularProgressIndicator over WebView until onPageFinished.

First render delay. WebView initializes longer than native widgets — 200-500ms. For exchange this is unpleasant. Solution: initialize WebView early (when opening ticker screen, not when navigating to chart screen), warm up via offscreen WebView.

Keyboard and Focus. WebView captures focus — native keyboard and gestures may conflict. Explicitly disable text input in WebView: webViewController.setOnPlatformPermissionRequest and don't enable JavaScript form elements.

JavaScript Bridge on iOS. On iOS WKWebView (under WebView hood) asynchronously delivers JS messages. With fast tick stream (>10/sec) — message queue may create lag. Solution: batching updates on Flutter side, send not each tick but accumulated update every 100ms.

Technical Indicators

Lightweight Charts supports adding arbitrary line series over main chart. MA(20) — compute on Flutter, pass array to addLineSeries().setData():

List<Map> calculateMA(List<Candle> candles, int period) {
  final result = <Map>[];
  for (var i = period - 1; i < candles.length; i++) {
    final avg = candles.sublist(i - period + 1, i + 1)
        .map((c) => c.close)
        .reduce((a, b) => a + b) / period;
    result.add({'time': candles[i].timestamp ~/ 1000, 'value': avg});
  }
  return result;
}

Scope of Work

  • WebView setup with correct parameters for iOS and Android
  • HTML/JS template with Lightweight Charts, theme and series setup
  • Bidirectional Flutter ↔ WebView bridge
  • Real-time updates via WebSocket
  • Crosshair with OHLCV display in native Flutter panel
  • Timeframe switching
  • Volume bars
  • Basic indicators (MA, EMA — by agreement)

Timeframe

Basic integration with WebSocket and crosshair: 5–8 days. Full screen with timeframe switching, indicators, iOS/Android adaptation: 2–3 weeks. Cost calculated individually.