Интеграция TradingView Lightweight Charts в мобильное приложение биржи

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.
Разработка и поддержка любых видов мобильных приложений:
Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Предлагаемые услуги
Показано 1 из 1 услугВсе 1735 услуг
Интеграция TradingView Lightweight Charts в мобильное приложение биржи
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1054
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    445

Интеграция TradingView Lightweight Charts в мобильное приложение биржи

TradingView Lightweight Charts — JavaScript-библиотека для финансовых графиков весом ~45KB gzip. Её используют в продакшн Coinbase, OKX, Gate.io. На мобильном — запускается внутри WebView, что даёт все возможности библиотеки без нативной реализации candlestick с нуля.

Архитектура: WebView-мост

Интеграция строится на двустороннем мосту: нативное приложение отправляет данные в WebView через JavaScript, WebView сигнализирует обратно о событиях (tap на свечу, crosshair movement).

На Flutter — webview_flutter (официальный от Google):

// Инициализация WebViewController
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');
}

// Отправка данных в WebView
Future<void> setChartData(List<Candle> candles) async {
  final json = jsonEncode(candles.map((c) => {
    'time': c.timestamp ~/ 1000, // Lightweight Charts ожидает секунды
    'open': c.open,
    'high': c.high,
    'low': c.low,
    'close': c.close,
  }).toList());
  await _webViewController.runJavaScript('window.setData($json)');
}

HTML/JS часть: инициализация и API

Минимальный 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 },
    });

    // Подписка на crosshair — отправляем данные в Flutter
    chart.subscribeCrosshairMove(param => {
      if (param.seriesData.has(candleSeries)) {
        const candle = param.seriesData.get(candleSeries);
        FlutterBridge.postMessage(JSON.stringify({ type: 'crosshair', candle }));
      }
    });

    // Вызывается из Flutter
    window.setData = function(candles) {
      candleSeries.setData(candles);
      // Volume отдельно
      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) {
      // Запрос новых данных обрабатывается на Flutter стороне
      FlutterBridge.postMessage(JSON.stringify({ type: 'timeframe_change', timeframe }));
    };
  </script>
</body>
</html>

Real-time обновления

WebSocket тик → Flutter → вызов updateLastCandle в 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() в Lightweight Charts обновляет только последнюю свечу без перерисовки всего графика. Это оптимизировано — библиотека делает это правильно.

Подводные камни интеграции

Viewport meta. Без maximum-scale=1.0 iOS Safari включает пользовательский zoom на двойной тап — интерфейс расползается. На Android — WebSettings.setSupportZoom(false).

Белый flash при загрузке. WebView рендерит белый фон до загрузки HTML. Решение — backgroundColor у WebView совпадает с фоном графика (#131722), и показываем CircularProgressIndicator поверх WebView до получения onPageFinished.

Задержка первого рендера. WebView инициализируется дольше нативных виджетов — 200-500ms. Для биржи это неприятно. Решение: инициализировать WebView заранее (при открытии экрана тикера, не при переходе на экран графика), прогревать через offscreen WebView.

Keyboard и Focus. WebView перехватывает focus — нативная клавиатура и жесты могут конфликтовать. Явно отключаем text input в WebView: webViewController.setOnPlatformPermissionRequest и не включаем JavaScript form elements.

JavaScript Bridge на iOS. На iOS WKWebView (под капотом WebView) асинхронно доставляет сообщения из JS. При быстром потоке тиков (>10/сек) — очередь сообщений может создавать lag. Решение: батчинг обновлений на Flutter стороне, отправка не каждого тика, а накопленного обновления каждые 100ms.

Технические индикаторы

Lightweight Charts поддерживает добавление произвольных line series поверх основного графика. MA(20) — вычисляем на Flutter, передаём массивом в 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;
}

Что входит в работу

  • Настройка WebView с правильными параметрами для iOS и Android
  • HTML/JS шаблон с Lightweight Charts, настройка темы и серий
  • Двусторонний мост Flutter ↔ WebView
  • Real-time обновления через WebSocket
  • Crosshair с отображением OHLCV в нативной панели Flutter
  • Переключение таймфреймов
  • Volume bars
  • Базовые индикаторы (MA, EMA — по согласованию)

Сроки

Базовая интеграция с WebSocket и crosshair: 5–8 дней. Полноценный экран с переключением таймфреймов, индикаторами, адаптацией под iOS/Android: 2–3 недели. Стоимость рассчитывается индивидуально.