fix(kline): 稀疏K线数据时居中显示

当K线数量较少(如1分钟图刚开始)时,K线不再拉伸填满屏幕,
而是使用默认宽度并在可用区域内水平居中显示。

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-19 19:55:19 -08:00
parent f51aa44cd9
commit f1b2ec7ede
3 changed files with 46 additions and 16 deletions

View File

@ -79,12 +79,17 @@ class _KlineChartWidgetState extends State<KlineChartWidget> {
// K 线
final idealWidth = chartWidth / widget.klines.length;
//
_candleWidth = idealWidth.clamp(_minCandleWidth, _maxCandleWidth);
// 使
if (idealWidth > _maxCandleWidth) {
// K 线使
_candleWidth = 8.0;
} else {
_candleWidth = idealWidth.clamp(_minCandleWidth, _maxCandleWidth);
}
_prevCandleWidth = _candleWidth;
// K 线
_scrollToCenter();
// K 线
_scrollToEnd();
}
/// 使 K 线
@ -390,6 +395,7 @@ class _KlineChartWidgetState extends State<KlineChartWidget> {
crossLineIndex: _showCrossLine ? _crossLineIndex - visibleData.startIndex : -1,
candleWidth: visibleData.candleWidth,
isDark: AppColors.isDark(context),
leftOffset: visibleData.leftOffset,
),
),
// 线
@ -430,6 +436,7 @@ class _KlineChartWidgetState extends State<KlineChartWidget> {
crossLineIndex: _showCrossLine ? _crossLineIndex - visibleData.startIndex : -1,
candleWidth: visibleData.candleWidth,
isDark: AppColors.isDark(context),
leftOffset: visibleData.leftOffset,
),
),
),
@ -570,6 +577,16 @@ class _KlineChartWidgetState extends State<KlineChartWidget> {
return _VisibleData(klines: [], startIndex: 0, candleWidth: _candleWidth);
}
// K线总宽度padding
const rightPadding = 50.0; // painter rightPadding
const leftPadding = 8.0; // painter leftPadding
final availableWidth = chartWidth - rightPadding - leftPadding;
final totalWidth = widget.klines.length * _candleWidth;
// K线总宽度小于可用宽度时K线居中显示
// = ( - K线总宽度) / 2
final leftOffset = totalWidth < availableWidth ? (availableWidth - totalWidth) / 2 : 0.0;
// K线范围
final int startIndex = (_scrollX / _candleWidth).floor().clamp(0, widget.klines.length - 1);
final int visibleCount = (chartWidth / _candleWidth).ceil() + 1; // +1 K线可见
@ -579,6 +596,7 @@ class _KlineChartWidgetState extends State<KlineChartWidget> {
klines: widget.klines.sublist(startIndex, endIndex),
startIndex: startIndex,
candleWidth: _candleWidth,
leftOffset: leftOffset,
);
}
@ -771,6 +789,12 @@ class _VisibleData {
final List<Kline> klines;
final int startIndex;
final double candleWidth;
final double leftOffset; // K线数据稀疏时的左侧偏移
_VisibleData({required this.klines, required this.startIndex, required this.candleWidth});
_VisibleData({
required this.klines,
required this.startIndex,
required this.candleWidth,
this.leftOffset = 0.0,
});
}

View File

@ -12,6 +12,7 @@ class KlinePainter extends CustomPainter {
final int crossLineIndex;
final double? candleWidth; // K线宽度
final bool isDark; //
final double leftOffset; // K线稀疏时的左侧偏移
static const Color _green = Color(0xFF10B981);
static const Color _red = Color(0xFFEF4444);
@ -42,6 +43,7 @@ class KlinePainter extends CustomPainter {
this.crossLineIndex = -1,
this.candleWidth,
this.isDark = false,
this.leftOffset = 0.0,
});
@override
@ -137,7 +139,7 @@ class KlinePainter extends CustomPainter {
final color = isUp ? _green : _red;
final paint = Paint()..color = color;
final x = leftPadding + i * actualCandleWidth + actualCandleWidth / 2;
final x = leftPadding + leftOffset + i * actualCandleWidth + actualCandleWidth / 2;
final yOpen = priceToY(open);
final yClose = priceToY(close);
final yHigh = priceToY(high);
@ -178,7 +180,7 @@ class KlinePainter extends CustomPainter {
canvas,
entry.value,
_maColors[colorIndex % _maColors.length],
leftPadding,
leftPadding + leftOffset,
actualCandleWidth,
priceToY,
);
@ -194,7 +196,7 @@ class KlinePainter extends CustomPainter {
canvas,
entry.value,
_maColors[colorIndex % _maColors.length],
leftPadding,
leftPadding + leftOffset,
actualCandleWidth,
priceToY,
);
@ -204,14 +206,14 @@ class KlinePainter extends CustomPainter {
// BOLL线
if (bollData != null) {
_drawLine(canvas, bollData!['middle']!, _bollMiddleColor, leftPadding, actualCandleWidth, priceToY);
_drawLine(canvas, bollData!['upper']!, _bollUpperColor, leftPadding, actualCandleWidth, priceToY, isDashed: true);
_drawLine(canvas, bollData!['lower']!, _bollLowerColor, leftPadding, actualCandleWidth, priceToY, isDashed: true);
_drawLine(canvas, bollData!['middle']!, _bollMiddleColor, leftPadding + leftOffset, actualCandleWidth, priceToY);
_drawLine(canvas, bollData!['upper']!, _bollUpperColor, leftPadding + leftOffset, actualCandleWidth, priceToY, isDashed: true);
_drawLine(canvas, bollData!['lower']!, _bollLowerColor, leftPadding + leftOffset, actualCandleWidth, priceToY, isDashed: true);
}
// 线
if (crossLineIndex >= 0 && crossLineIndex < klines.length) {
_drawCrossLine(canvas, size, crossLineIndex, leftPadding, actualCandleWidth, priceToY);
_drawCrossLine(canvas, size, crossLineIndex, leftPadding + leftOffset, actualCandleWidth, priceToY);
}
// MA图例
@ -452,6 +454,7 @@ class KlinePainter extends CustomPainter {
oldDelegate.bollData != bollData ||
oldDelegate.crossLineIndex != crossLineIndex ||
oldDelegate.candleWidth != candleWidth ||
oldDelegate.isDark != isDark;
oldDelegate.isDark != isDark ||
oldDelegate.leftOffset != leftOffset;
}
}

View File

@ -9,6 +9,7 @@ class KlineVolumePainter extends CustomPainter {
final int crossLineIndex;
final double? candleWidth;
final bool isDark; //
final double leftOffset; // K线稀疏时的左侧偏移
static const Color _green = Color(0xFF10B981);
static const Color _red = Color(0xFFEF4444);
@ -23,6 +24,7 @@ class KlineVolumePainter extends CustomPainter {
this.crossLineIndex = -1,
this.candleWidth,
this.isDark = false,
this.leftOffset = 0.0,
});
@override
@ -91,7 +93,7 @@ class KlineVolumePainter extends CustomPainter {
final isUp = close >= open;
final color = isUp ? _green : _red;
final x = leftPadding + i * actualCandleWidth + actualCandleWidth / 2;
final x = leftPadding + leftOffset + i * actualCandleWidth + actualCandleWidth / 2;
final barHeight = (volume / maxVolume) * chartHeight;
final y = size.height - bottomPadding - barHeight;
@ -106,7 +108,7 @@ class KlineVolumePainter extends CustomPainter {
// 线
if (crossLineIndex >= 0 && crossLineIndex < klines.length) {
final x = leftPadding + crossLineIndex * actualCandleWidth + actualCandleWidth / 2;
final x = leftPadding + leftOffset + crossLineIndex * actualCandleWidth + actualCandleWidth / 2;
canvas.drawLine(
Offset(x, 0),
Offset(x, size.height),
@ -141,6 +143,7 @@ class KlineVolumePainter extends CustomPainter {
return oldDelegate.klines != klines ||
oldDelegate.crossLineIndex != crossLineIndex ||
oldDelegate.candleWidth != candleWidth ||
oldDelegate.isDark != isDark;
oldDelegate.isDark != isDark ||
oldDelegate.leftOffset != leftOffset;
}
}