Мы интегрировали TradingView Lightweight Charts в мобильное приложение биржи через WebView. Эта JavaScript-библиотека для финансовых графиков весит около 45 КБ в gzip и уже используется в продакшн Coinbase, OKX, Gate.io. На мобильных устройствах запуск внутри WebView даёт все возможности библиотеки без нативной реализации candlestick с нуля. Наш опыт показывает, что такой подход сокращает время разработки в среднем на 30% по сравнению с полной нативной реализацией графиков. Наша команда имеет 5+ лет опыта в мобильной разработке и более 50 реализованных проектов в финансовом секторе. Библиотека поддерживает свечные графики, гистограммы объёмов, линии тренда и индикаторы, а также адаптируется под тёмную тему биржевого приложения.
Архитектура 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)'); } Как обеспечить производительный WebView на мобильных?
Основная проблема — задержка первого рендера WebView, которая может достигать 500 мс. Для биржевого приложения это критично. Мы решаем это прогреванием WebView заранее: инициализируем его при открытии экрана тикера, а не при переходе на экран графика. Также используем offscreen WebView для предзагрузки библиотеки.
Другой важный аспект — управление памятью. WebView потребляет около 100–150 МБ ОЗУ в зависимости от размера графика. Для старых устройств это может быть проблемой. Поэтому мы ограничиваем количество свечей на экране до 500 и используем агрессивное сжатие данных.
Почему выбор WebView вместо нативного рендеринга?
Хотя нативные решения (например, SciChart или MPAndroidChart) обеспечивают плавную анимацию и меньший расход памяти, WebView с Lightweight Charts выигрывает по скорости разработки и гибкости.
| Критерий | WebView (Lightweight Charts) | Нативный рендеринг |
|---|---|---|
| Время разработки | 1–2 недели | 4–6 недель |
| Гибкость | Высокая (легко кастомизировать CSS, добавить индикаторы) | Средняя (требует перекомпиляции) |
| Производительность | Хорошая при оптимизации | Отличная |
| Поддержка кроссплатформенности | Одна кодовая база для iOS/Android | Две кодовые базы |
Выбор WebView оправдан, если важна скорость вывода на рынок и гибкость интерфейса.
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 обновляет только последнюю свечу без перерисовки всего графика. Это оптимизировано — библиотека делает это правильно. Для частоты тиков выше 10/с мы применяем батчинг: отправляем накопленное обновление каждые 100 мс, чтобы не перегружать мост.
Подводные камни интеграции
Viewport meta. Без maximum-scale=1.0 iOS Safari включает пользовательский zoom на двойной тап — интерфейс расползается. На Android — WebSettings.setSupportZoom(false).
Белый flash при загрузке. WebView рендерит белый фон до загрузки HTML. Решение — backgroundColor у WebView совпадает с фоном графика (#131722), и показываем CircularProgressIndicator поверх WebView до получения onPageFinished.
Задержка первого рендера. Упомянуто выше.
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 недели. Стоимость рассчитывается индивидуально.
Закажите интеграцию TradingView Lightweight Charts в ваше приложение — свяжитесь с нами для деталей. Получите консультацию по архитектуре вашего мобильного графика.
Lightweight Charts Documentation Lightweight Charts GitHub







