Simple candlestick chart drawing of flutter custom series

The previous article talked about the related process of flutter customization.

Continue to practice flutter's custom K line today:

We can implement a simple candlestick chart interface by customizing Painter:

  1. Create a custom Painter for drawing candlestick charts:
import 'dart:ui';

import 'package:flutter/material.dart';

class KLinePainter extends CustomPainter {
  final List<dynamic> data;
  final double itemWidth;
  final double scaleFactor;

  KLinePainter({required this.data, required this.itemWidth, required this.scaleFactor});

  @override
  void paint(Canvas canvas, Size size) {
    // 设置画笔
    final linePaint = Paint()
      ..color = Colors.grey
      ..strokeWidth = 0.5;

    final textPainter = TextPainter(textDirection: TextDirection.ltr, textAlign: TextAlign.left);

    // 计算价格范围和最大成交量
    num highestPrice = double.minPositive;
    num lowestPrice = double.maxFinite;
    num highestVolume = double.minPositive;

    for (var item in data) {
      if (item['high'] > highestPrice) {
        highestPrice = item['high'];
      }
      if (item['low'] < lowestPrice) {
        lowestPrice = item['low'];
      }
      if (item['vol'] > highestVolume) {
        highestVolume = item['vol'];
      }
    }

    // 计算价格和成交量的缩放比例
    double priceScale = (size.height - 20) / (highestPrice - lowestPrice);
    double volumeScale = size.height * 0.2 / highestVolume;

    // 绘制K线图
    for (int i = 0; i < data.length; i++) {
      var item = data[i];

      double open = (item['open'] - lowestPrice) * priceScale;
      double close = (item['close'] - lowestPrice) * priceScale;
      double high = (item['high'] - lowestPrice) * priceScale;
      double low = (item['low'] - lowestPrice) * priceScale;
      double vol = item['vol'] * volumeScale;

      // 设置画笔颜色
      linePaint.color = close >= open ? Colors.green : Colors.red;

      // 绘制实体
      double halfWidth = itemWidth * scaleFactor / 2;
      double centerX = i * itemWidth * scaleFactor + halfWidth;
      canvas.drawRect(
        Rect.fromCenter(
          center: Offset(centerX, size.height - (open + close) / 2 - 10),
          width: itemWidth * scaleFactor,
          height: (open - close).abs(),
        ),
        linePaint,
      );

      // 绘制上下影线
      canvas.drawLine(Offset(centerX, size.height - high - 10), Offset(centerX, size.height - low - 10), linePaint);
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}
  1. Create a StatefulWidget that handles zooming and scrolling:
class KLineChart extends StatefulWidget {
  final List<dynamic> data;

  KLineChart({required this.data});

  @override
  _KLineChartState createState() => _KLineChartState();
}

class _KLineChartState extends State<KLineChart> {
  double _scaleFactor = 1.0;
  double _itemWidth = 10.0;
  late double _baseScaleFactor;

  @override
  void initState() {
    super.initState();
    _baseScaleFactor = _scaleFactor;
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onScaleStart: (details) {
        _baseScaleFactor = _scaleFactor;
      },
      onScaleUpdate: (details) {
        setState(() {
          // 修改这里,减小缩放速度
          _scaleFactor = _baseScaleFactor * (1.0 + (details.scale - 1.0) / 2);
        });
      },
      child: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: CustomPaint(
          size: Size(widget.data.length * _itemWidth * _scaleFactor, MediaQuery.of(context).size.height),
          painter: KLinePainter(data: widget.data, itemWidth: _itemWidth, scaleFactor: _scaleFactor),
        ),
      ),
    );
  }
}
  1. Where you need to use the K-line chart, call KLineChartthe component and pass in the K-line data:
List<dynamic> kLineData = [
  // 你的K线数据
];

KLineChart(data: kLineData);

In this way, you can realize a simple K-line interface that can be slid left and right, zoomed in and out.

Here we look at the effect of the realization:
I first simulated some K-line data:

List<dynamic> generateKLineData(int count) {
  List<dynamic> data = [];
  Random random = Random();
  double open = 100.0;
  double close, high, low;
  num volume;

  for (int i = 0; i < count; i++) {
    close = open + random.nextDouble() * 20 - 10;
    high = max(open, close) + random.nextDouble() * 5;
    low = min(open, close) - random.nextDouble() * 5;
    volume = random.nextInt(10000) + 1000;

    data.add({
      'open': open,
      'close': close,
      'high': high,
      'low': low,
      'vol': volume,
    });

    open = close;
  }

  return data;
}

Then quote directly:

SliverToBoxAdapter(
  child: SizedBox(
    height: 300.w,
    child: KLineChart(data: generateKLineData(100)),
  ),
)

After running, the effect is as follows:

picture.png

It can be slid left and right, scalable, and only includes basic functions. It really needs time-sharing charts, various indicators, and even custom indicator drawing, etc. You can further improve and optimize according to your needs.

Technologists work together every day, let's encourage each other!

Guess you like

Origin blog.csdn.net/qq_28563283/article/details/131124976