Candlestick Chart 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
Candlestick Chart in Mobile Exchange App
Complex
~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

Candlestick Chart Implementation in Mobile Exchange App

Candlestick chart is technically the most complex chart type for mobile app. Not because of OHLC math, but because of the set of requirements that are mandatory for exchange UX: smooth zoom/pan across 10,000+ candles, crosshair with coordinates on touch, real-time update without redrawing entire chart, timeframe switching without flicker, correct behavior in landscape and portrait orientation. All these together — and most ready libraries start breaking.

Why Standard Libraries Don't Work

fl_chart — no candlestick out of box. Can assemble from custom BarChartRod, but it's a hack without zoom and acceptable performance.

syncfusion_flutter_charts — has SfCartesianChart with CandleSeries, supports zoom/pan, data updates. Workable for most tasks. But commercial license ($995+/year per developer) makes it impractical for startups.

TradingView Lightweight Charts in WebView — most common approach in production large exchange apps. Library written for production trading UI, optimized for large data, supports all needed features. WebView overhead exists but negligible on modern devices.

Native implementation via CanvasCustomPainter (Flutter) or CALayer (iOS) or Canvas (Android). Maximum performance, full control. Requires significant development. Justified if candlestick is product's central element.

Native Implementation on Flutter via CustomPainter

For app where chart is main screen, native implementation gives 60fps on any device. Key components:

Data Structure

class Candle {
  final int timestamp;  // Unix timestamp in ms
  final double open;
  final double high;
  final double low;
  final double close;
  final double volume;

  bool get isBullish => close >= open;
}

Render Pipeline

CustomPainter with shouldRepaint — called on any data change. To avoid redrawing entire chart on new candle:

class CandlestickPainter extends CustomPainter {
  final List<Candle> candles;
  final CandleChartController controller; // stores offset and scale

  @override
  void paint(Canvas canvas, Size size) {
    final visibleRange = controller.getVisibleRange(candles.length, size.width);
    final visibleCandles = candles.sublist(visibleRange.start, visibleRange.end);

    final priceRange = _calculatePriceRange(visibleCandles);
    final candleWidth = size.width / visibleCandles.length * controller.scale;

    for (var i = 0; i < visibleCandles.length; i++) {
      _drawCandle(canvas, visibleCandles[i], i, candleWidth, size.height, priceRange);
    }

    if (controller.crosshairVisible) {
      _drawCrosshair(canvas, controller.crosshairPosition, size);
    }
  }

  void _drawCandle(Canvas canvas, Candle c, int index, double width, double height, PriceRange range) {
    final x = index * width + width / 2;
    final paint = Paint()
      ..color = c.isBullish ? const Color(0xFF26A69A) : const Color(0xFFEF5350)
      ..strokeWidth = 1.5;

    // Wick (shadow)
    final highY = range.toY(c.high, height);
    final lowY = range.toY(c.low, height);
    canvas.drawLine(Offset(x, highY), Offset(x, lowY), paint);

    // Body
    final openY = range.toY(c.open, height);
    final closeY = range.toY(c.close, height);
    final bodyPaint = Paint()..color = paint.color;

    final bodyRect = Rect.fromLTRB(
      x - width * 0.35, min(openY, closeY),
      x + width * 0.35, max(openY, closeY),
    );
    // Hollow candles for bullish:
    if (c.isBullish) {
      canvas.drawRect(bodyRect, bodyPaint..style = PaintingStyle.stroke);
    } else {
      canvas.drawRect(bodyRect, bodyPaint..style = PaintingStyle.fill);
    }
  }

  @override
  bool shouldRepaint(CandlestickPainter old) =>
      old.candles != candles || old.controller != controller;
}

Gesture Handling: Pan and Pinch-Zoom

GestureDetector with onScaleStart/Update for pinch-zoom, onPanUpdate for time axis scroll:

GestureDetector(
  onScaleUpdate: (details) {
    setState(() {
      controller.scale = (controller.scale * details.scale).clamp(0.5, 10.0);
      controller.offset += details.focalPointDelta.dx;
    });
  },
  child: CustomPaint(painter: CandlestickPainter(candles, controller)),
)

Clamp scale is important: without limits user goes into one-candle-fills-screen mode.

Crosshair on Long Press

GestureDetector(
  onLongPressStart: (details) {
    controller.crosshairVisible = true;
    controller.crosshairPosition = details.localPosition;
    // Calculate nearest candle to touch position
    final candleIndex = controller.positionToIndex(details.localPosition.dx, candles.length);
    if (candleIndex < candles.length) {
      _showCandleInfo(candles[candleIndex]);
    }
  },
  onLongPressMoveUpdate: (details) {
    controller.crosshairPosition = details.localPosition;
    // update info panel
  },
  onLongPressEnd: (_) => controller.crosshairVisible = false,
)

Real-Time Last Candle Update

WebSocket connection to exchange gives tick data. On new tick — update only last candle, don't rebuild entire list:

void onTickReceived(Tick tick) {
  if (_candles.isEmpty) return;
  final last = _candles.last;

  // Update OHLC of last candle
  _candles[_candles.length - 1] = last.copyWith(
    high: max(last.high, tick.price),
    low: min(last.low, tick.price),
    close: tick.price,
    volume: last.volume + tick.volume,
  );

  // New timeframe — new candle
  if (tick.timestamp >= last.timestamp + timeframe.milliseconds) {
    _candles.add(Candle.fromTick(tick));
    if (_candles.length > maxCandlesInMemory) _candles.removeAt(0);
  }

  // Notify only painter, not entire screen
  _chartController.notifyListeners();
}

ValueNotifier + ValueListenableBuilder only around CustomPaint — only canvas redraws, not AppBar and side panels.

Timeframe Switching

Buttons 1m / 5m / 15m / 1h / 4h / 1D / 1W. On switch — request new candle set, apply crossfade animation to remove "flicker":

AnimatedSwitcher(
  duration: const Duration(milliseconds: 200),
  child: CandlestickWidget(
    key: ValueKey(selectedTimeframe), // on key change — animation
    candles: _candles,
  ),
)

Technical Indicators (Optional)

MA (Moving Average), EMA, Bollinger Bands, RSI — each drawn as additional layer in CustomPainter. RSI and MACD — on separate bottom CustomPaint with fixed height and shared horizontal time axis with main chart.

Scope of Work

  • Candlestick CustomPainter implementation or TradingView integration in WebView
  • Pan/zoom gestures with proper limits
  • Crosshair with info panel
  • Real-time update via WebSocket
  • Timeframe switching
  • Technical indicators (MA, EMA, Bollinger Bands — by agreement)
  • Volume bars on bottom panel

Timeframe

WebView + TradingView Lightweight Charts: 1–2 weeks (including WebSocket integration). Native CustomPainter with full exchange chart functionality: 3–5 weeks. Cost calculated individually.