上篇文章讲了flutter自定义的相关流程,
今天继续练习下flutter的自定义K线:
我们可以通过自定义Painter来实现一个简单的K线图界面:
- 创建一个自定义的Painter,用于绘制K线图:
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});@overridevoid 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);}}@overridebool shouldRepaint(CustomPainter oldDelegate) {return true;}
}
- 创建一个StatefulWidget,用于处理缩放和滚动:
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;@overridevoid initState() {super.initState();_baseScaleFactor = _scaleFactor;}@overrideWidget 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),),),);}
}
- 在需要使用K线图的地方,调用
KLineChart
组件并传入K线数据:
List<dynamic> kLineData = [// 你的K线数据
];KLineChart(data: kLineData);
这样,你就可以实现一个简单的可左右滑动、放大缩小的K线界面。
这里我们看看实现的效果:
我先模拟了一些K线数据:
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;
}
之后直接引用:
SliverToBoxAdapter(child: SizedBox(height: 300.w,child: KLineChart(data: generateKLineData(100)),),
)
运行后如下效果:
可左右滑动,可伸缩,仅包含基本功能,真正的需要分时图,各种的指标,甚至是自定义的指标绘制等等,你可以根据需求进一步完善和优化。
技术人日拱一卒,共勉!