flutter_chen_kchart

Flutter 生态可用于生产环境的 K 线图表库 / A Production-Ready K-Line Chart for Flutter

Pub Version License: MIT

🚀 Why flutter_chen_kchart?

A Flutter candlestick chart library focused on practical trading/charting scenarios. Built with CustomPainter, supports cross-platform rendering and interactive chart operations.

选择理由 / Why Choose Us:

  • 🏆 Flutter 生态首个 真正达到可商用的 K线图表库
  • 🚀 原生性能 CustomPainter 实现,丝滑流畅
  • 🌍 全平台支持 iOS/Android/Web/Windows/macOS/Linux
  • 🔧 功能完整 开源包内提供常见图表与交互能力
  • 📈 持续更新 长期维护,功能不断完善

4.x 核心新增能力 / Highlights

  • 完整数据链路:支持直接传入 K 线,也支持 KChartDataSource 管理首次加载、历史分页、实时订阅、断线重连、缓存和缺口修复。
  • 专业坐标轴与视口:支持普通、百分比、对数价格轴,固定价格间隔、价格轴拖拽、自定义显示时区,以及跳转时间、回到最新和 fit content。
  • 多 Pane 与指标系统:支持加权 Pane、自定义指标计算与绘制,以及 MA、EMA、BOLL、SAR、MACD、KDJ、RSI、WR、CCI、OBV、StochRSI 等内置指标。
  • 交易场景叠加:支持订单、持仓开仓价、强平价、未实现盈亏、TP/SL 分组、成交历史 B/S 标记,以及异步改价确认。
  • 完整绘图工作流:支持 OHLC 磁铁吸附、端点编辑、延长线/射线、撤销重做、事务合并、序列化持久化,以及数据变化后的时间锚点重定位。
  • 大数据优化:实时指标增量计算、可视区裁剪、成交标记索引与文本缓存,可用于长历史 K 线和大量成交记录。

效果展示 / Effect Display

🌐 在线演示 / Online Demo

🌐 App演示 / App Demo

基础K线图表 交互选择功能
绘图工具演示 高级绘图功能

📦 快速开始 / Quick Start

1. 安装 / Install

dependencies:
  flutter_chen_kchart: ^4.7.0

2. 基本用法 / Basic Usage

import 'package:flutter_chen_kchart/k_chart.dart';

final KChartController _controller = KChartController();
final DrawingToolManager _drawingManager =
    DrawingToolManager(historyLimit: 100);

KChartWidget(
  datas,
  isTrendLine: false,
  controller: _controller,
  enableTheme: true,
  colorVisionMode: ChartColorVisionMode.colorBlindFriendly,
  minScale: 0.1,
  maxScale: 5.0,
  scaleSensitivity: 2.5,
  onScaleChanged: (scale) {
    print('Current scale: ${(scale * 100).toInt()}%');
  },
  enableDrawingTools: true,
  drawingToolManager: _drawingManager,
  enablePerformanceMode: true,
);

3. 数据格式与更新 / Data Contract & Updates

每根 K 线最少需要 time/open/high/low/close/vol。价格和成交量应转换为 doubletime 使用 Unix 毫秒时间戳:

final bar = KLineEntity.fromCustom(
  time: int.parse(item[0].toString()),
  open: double.parse(item[1].toString()),
  high: double.parse(item[2].toString()),
  low: double.parse(item[3].toString()),
  close: double.parse(item[4].toString()),
  vol: double.parse(item[5].toString()),
);
  • time 建议直接传交易所返回的 UTC Unix 毫秒时间戳。时间戳本身不携带时区,图表只在格式化时间轴和十字线时间时转换显示时区。
  • timeZoneOffset: null 使用设备当前时区;Duration.zero 显示 UTC;例如 Duration(hours: 8) 固定显示 UTC+8。
  • 数据必须按时间从旧到新排列。normalizeKChartBars 可以排序、移除无时间数据并按时间去重。
  • 图表不负责把逐笔成交聚合成 K 线。业务端按当前周期请求或生成 OHLCV 后传入,因此分钟、小时、日、周和月等周期都可以显示。
  • interval 是业务周期标识并会传给 KChartDataSource。秒、分、时、日、周固定周期还能用于数据质量和缺口检测;自然月不是固定毫秒长度,不执行固定间隔判断。

直接数据模式没有额外的 setDataappendDataupdateLastData API,数据由业务状态管理并通过 datas 传入。推荐始终替换 List 引用:

// 以下方法放在持有图表数据的 State 中。
List<KLineEntity> bars = [];

void setData(List<KLineEntity> incoming) {
  final next = normalizeKChartBars(incoming);
  DataUtil.calculate(next);
  setState(() => bars = next);
}

void prependHistory(List<KLineEntity> olderBars) {
  // 加载更多得到的是更早数据,必须放在已有数据前面再统一排序去重。
  final next = normalizeKChartBars([...olderBars, ...bars]);
  DataUtil.calculate(next);
  setState(() => bars = next);
}

void updateRealtimeBar(KLineEntity realtimeBar) {
  // 同一 candle time 会替换;新的 candle time 会按时间插入。
  final next = upsertKChartRealtimeBar(bars, realtimeBar);
  final time = realtimeBar.time;
  final changedIndex =
      time == null ? -1 : findKChartBarIndexByTime(next, time);
  DataUtil.calculateFrom(next, changedIndex < 0 ? 0 : changedIndex);
  setState(() => bars = next);
}

如果必须原地修改同一个 List,修改 OHLCV 后递增 dataRevision;只有原地改变时间轴且列表长度、首尾时间都不变时,才递增 dataTopologyRevision。使用 KChartDataSource 时,排序去重、历史合并、实时替换和指标增量重算由库内部处理。

4. 画线撤销与持久化 / Drawing History & Persistence

_drawingManager.undo();
_drawingManager.redo();

_drawingManager.modeManager
  ..setMagnetMode(true)
  ..setMagnetSnapThreshold(50); // 屏幕像素 / logical pixels

final savedDrawings = _drawingManager.serializeTools();
_drawingManager.deserializeTools(savedDrawings); // 加载后建立新的历史基线

_drawingManager.updateToolProperties('trend-id', {
  'extendLeft': true,
  'extendRight': true,
});

// 也可通过 KChartController 操作当前图表
_controller.undoDrawing();
_controller.redoDrawing();

新增、移动、端点编辑、样式、显隐、删除和清空均支持撤销/重做。磁铁模式会在新建和端点编辑时吸附到最近 K 线的 OHLC,十字选择器会实时显示吸附位置。直接修改 DrawingTool 时,请用 beginHistoryTransaction()commitHistoryTransaction() 包裹一次完整操作,避免拖动的每一帧形成独立历史。

数据源分页、实时补洞或使用新 datas 列表前插历史 K 线时,图表会按 K 线时间自动重定位画线锚点。若业务端必须原地修改同一个列表,请保留修改前的副本并调用 _drawingManager.rebaseToData(previousBars, currentBars)


🛠️ 配置参数 / Configuration

参数/Property 类型/Type 默认值/Default 说明/Description
minScale double 0.3 最小缩放比例 / Min scale
maxScale double 3.0 最大缩放比例 / Max scale
scaleSensitivity double 2.5 缩放灵敏度 / Scale sensitivity
enablePinchZoom bool true 双指缩放 / Pinch zoom
enableScrollZoom bool true 滚轮缩放 / Mouse wheel zoom
enableTheme bool true 启用主题系统 / Enable theme
colorVisionMode ChartColorVisionMode? null 颜色视觉模式,可启用色盲友好配色 / Color-vision mode
enableDrawingTools bool false 启用绘图工具 / Drawing tools
drawingToolManager DrawingToolManager? null 外部画线管理器,支持撤销、重做和持久化 / External drawing manager
enablePerformanceMode bool false 性能优化 / Performance mode
controller KChartController? null 控制器 / Controller
onScaleChanged Function(double)? null 缩放回调 / Scale callback
dataSource KChartDataSource? null 数据源协议 / Data source protocol
symbol String? null 数据源模式交易对 / Data source symbol
interval String? null 数据源模式周期 / Data source interval
dataRevision int 0 原地修改 K 线内容时递增 / In-place bar content revision
dataTopologyRevision int 0 原地修改 K 线时间轴时递增 / In-place bar timestamp revision
initialBarLimit int 500 首次请求数量 / Initial bar count
paginationBarLimit int 500 历史分页数量 / History page size
autoSubscribe bool true 自动订阅实时K线 / Auto realtime subscription
autoScrollToLatest bool true 位于最新边缘时实时数据自动跟随 / Auto-follow realtime latest edge
autoRepairRealtimeGaps bool true 实时数据出现缺口时自动请求历史补洞 / Auto-repair realtime gaps
realtimeGapRepairLimit int 500 实时缺口补洞请求数量 / Realtime gap repair page size
realtimeReconnectPolicy KChartRealtimeReconnectPolicy default 实时订阅断线重连策略 / Realtime reconnect policy
onDataLoadStateChanged Function(KChartDataLoadState)? null 数据加载、分页、订阅状态回调 / Data load state callback
priceAxisMode KChartPriceAxisMode normal 价格轴模式:普通、百分比、对数 / Price axis mode
priceAxisRange KChartPriceRange? null 手动价格范围 / Manual price range
enablePriceAxisDrag bool false 启用价格轴拖拽缩放 / Drag price axis
timeZoneOffset Duration? null 固定显示时区;null 使用设备本地时区 / Fixed display offset; null uses device local time
showInfoDialog bool true 显示十字线详情面板 / Show crosshair details
materialInfoDialog bool true true 使用实色边框样式,false 使用渐变浮层样式 / Material or gradient info dialog
chartStyle.priceAxisTickInterval double? null 固定 Y 轴价格间隔;为空时使用自然刻度 / Fixed Y-axis price step
chartStyle.priceAxisTickMode KChartPriceAxisTickMode nice 自然刻度或旧版等分刻度 / Nice or legacy evenly-spaced ticks
onVisibleRangeChanged Function(KChartVisibleRange)? null 可见范围回调 / Visible range callback
onPriceAxisRangeChanged Function(KChartPriceRange)? null 价格轴范围回调 / Price range callback
panes List null Pane 布局配置 / Pane layout
indicatorRegistry KChartIndicatorRegistry? null 自定义指标注册表 / Indicator registry
indicatorConfigs List const [] 指标参数配置 / Indicator configs
tradingOverlay KChartTradingOverlay? null 交易叠加:订单线、持仓线、成交点 / Trading overlays
tradingLabels KChartTradingLabels? null 交易线标签文案,默认跟随 isChinese / Trading label text
tradeMarkers List const [] 外部买卖单 B/S 标记 / External buy/sell markers
tradeMarkerRevision int 0 原地修改 marker 列表时递增,用于刷新缓存 / Marker cache revision
onTradingLineDragUpdate Function(KChartTradingLine, double)? null 交易线拖拽中回调 / Trading line drag update
onTradingLineDragConfirm FutureOr<bool> Function(KChartTradingLine, double, double)? null 改价提交前异步确认 / Async amend confirmation
tradingLineDragConfirmTimeout Duration 15s 改价确认超时,Duration.zero 表示禁用 / Amend confirmation timeout
onTradingLineDragEnd Function(KChartTradingLine, double)? null 交易线拖拽结束回调 / Trading line drag end
onTradingLineTap Function(KChartTradingLine)? null 交易线点击回调 / Trading line tap
onTradeMarkerTap Function(KChartTradeMarker)? null 成交点点击回调 / Trade marker tap
onCrossLineChanged Function(KChartCrossLineSelection)? null 十字线选中变化回调 / Crosshair selection callback
onCrossLineHidden VoidCallback? null 十字线隐藏回调 / Crosshair hidden callback

更多参数详见源码和注释。

数据源协议 / Data Source Protocol

KChartWidget 仍然支持直接传入 List<KLineEntity>。如果你希望图表自己管理首次加载、左滑分页、实时订阅和取消订阅,可以实现 KChartDataSource

class ExchangeKChartDataSource extends KChartDataSource {
  @override
  Future<KChartBarsResult> getBars(KChartBarsRequest request) async {
    final bars = await fetchBarsFromYourApi(
      symbol: request.symbol,
      interval: request.interval,
      beforeTime: request.beforeTime,
      limit: request.limit,
    );

    return KChartBarsResult(
      bars: bars,
      hasMore: bars.isNotEmpty,
      nextBeforeTime: bars.isEmpty ? null : bars.first.time,
    );
  }

  @override
  Stream<KLineEntity> subscribeBars(KChartSubscription subscription) {
    return connectYourRealtimeStream(
      subscription.symbol,
      subscription.interval,
    );
  }

  @override
  Future<void> unsubscribeBars(String subscriptionId) async {
    await closeYourRealtimeStream(subscriptionId);
  }
}

final controller = KChartController();

KChartWidget(
null,
controller: controller,
dataSource: ExchangeKChartDataSource(),
symbol: 'BTCUSDT',
interval: '1h',
autoRepairRealtimeGaps: true,
realtimeGapRepairLimit: 500,
realtimeReconnectPolicy: const KChartRealtimeReconnectPolicy(
maxAttempts: 5,
initialDelay: Duration(seconds: 1),
maxDelay: Duration(seconds: 30),
),
onDataLoadStateChanged: (state) {
if (state.isError) {
print('${state.phase} failed: ${state.error}');
}
if (state.isReconnecting) {
print('reconnect #${state.reconnectAttempt} after ${state.retryDelay}');
}
if (state.qualityReport?.hasIssues == true) {
print('data quality: ${state.qualityReport}');
}
},
isTrendLine: false,
)

项目侧也可以通过 KChartController 主动刷新或加载历史数据,适合“重试”“下拉刷新”“加载更早历史”这类业务入口:

await controller.reloadData(clearCache: true);

if (controller.canLoadMoreData && !controller.isLoadingMoreData) {
final appended = await controller.loadMoreData();
print('loaded older bars: $appended');
}

if (!controller.isAtLatest) {
await controller.scrollToLatest();
}

库本体只定义协议、缓存、状态事件和合并逻辑,不绑定任何具体交易所或网络库。交易所 API、WebSocket、鉴权、代理等仍然放在业务层或示例层实现。

KChartDataLoadState.qualityReport 会报告无时间戳、重复时间、乱序、非法 OHLC/成交量、非预期间隔和历史缺口等数据质量问题。项目可以用它做补洞、fallback、埋点或调试真实行情源。

当实时订阅合并后检测到固定周期缺口时,KChartWidget 默认会用最新实时 K 线时间作为 beforeTime 触发一次历史补洞请求,并把补洞结果通过 history 阶段状态回调返回。

实时 K 线 append/update 时,内置指标会从受影响的 K 线开始增量重算,避免每个 tick 都对全量历史执行完整指标计算。自定义指标可以实现 calculateFrom;未实现时会回退到 calculate 全量计算,以保证兼容性和结果正确性。

时间轴与价格轴 / Time & Price Axis

KChartController 支持读取和控制当前视图范围,适合项目中做“跳转到某笔成交时间”“回到最新”“fit content”“同步多图表”等能力:

final controller = KChartController();

KChartWidget(
datas,
controller: controller,
priceAxisMode: KChartPriceAxisMode.percentage,
chartStyle: ChartStyle().copyWith(
gridRows: 4,
// 设置后按固定价差绘制;不设置时默认使用自然刻度。
priceAxisTickInterval: 50,
),
// null: 系统本地时区;Duration.zero: UTC;Duration(hours: 8): UTC+8。
timeZoneOffset: const Duration(hours: 8),
enablePriceAxisDrag: true,
onVisibleRangeChanged: (range) {
print('${range.startTime} - ${range.endTime}');
},
onCrossLineChanged: (selection) {
print('${selection.index} ${selection.time} ${selection.price}');
},
onCrossLineHidden: () {
print('crosshair hidden');
},
isTrendLine: false,
);

await controller.jumpToTime(
tradeTime,
alignment: KChartTimeAlignment.center,
);

await controller.setLogicalRange(
const KChartLogicalRange(from: 100, to: 180),
);

await controller.fitContent();
controller.setPriceRange(const KChartPriceRange(min: 60000, max: 72000));
controller.clearPriceRange();

final x = controller.timeToCoordinate(tradeTime);
final price = controller.coordinateToPrice(120);
final y = controller.priceToCoordinate(65000);
final nearestTime = controller.coordinateToTime(180);

controller.showCrossLineAtTime(tradeTime);
controller.showCrossLineAtIndex(120, price: 65000);
controller.showCrossLineAtCoordinate(const Offset(180, 120));
controller.hideCrossLine();

gridRows 控制自动刻度的目标密度,不等同于固定价差。需要“每 50 USDT 一格”时设置 priceAxisTickInterval: 50;需要完全保留旧版按屏幕等分的行为时设置 priceAxisTickMode: KChartPriceAxisTickMode.evenlySpaced。价格轴默认会移除多余尾零,当前价和十字线仍严格遵循 precision

Pane 与指标 / Panes & Indicators

默认仍然保持原有布局:主图、成交量、单副图。传入 panes 后,可以启用多个副图并调整高度权重:

KChartWidget(
datas,
isTrendLine: false,
panes: const [
KChartPaneConfig.main(weight: 3),
KChartPaneConfig.volume(weight: 1),
KChartPaneConfig.indicator(
indicatorId: KChartIndicatorIds.macd,
weight: 1,
),
KChartPaneConfig.indicator(
indicatorId: KChartIndicatorIds.rsi,
weight: 1,
),
],
);

内置副图指标当前支持 MACDKDJRSIWRCCI。项目也可以注册自定义指标计算,计算结果会写入每根 KLineEntity.indicatorValues;如果在定义里声明 series,还可以直接作为自定义副图绘制:

final registry = KChartIndicatorRegistry([
  KChartIndicatorDefinition(
    id: 'MY_SIGNAL',
    name: 'My Signal',
    defaultParameters: const {'offset': 0},
    series: const [
      KChartIndicatorSeries(
        valueKey: 'value',
        label: 'Signal',
      ),
      KChartIndicatorSeries(
        valueKey: 'hist',
        label: 'Histogram',
        type: KChartIndicatorSeriesType.histogram,
      ),
    ],
    calculate: (bars, config) {
      final offset = config.parameters['offset'] ?? 0;
      for (var i = 0; i < bars.length; i++) {
        final bar = bars[i];
        bar.indicatorValues['MY_SIGNAL.value'] = bar.close + offset;
        bar.indicatorValues['MY_SIGNAL.hist'] =
        i.isEven ? offset.toDouble() : -offset.toDouble();
      }
    },
    calculateFrom: (bars, config, startIndex) {
      final offset = config.parameters['offset'] ?? 0;
      for (var i = startIndex; i < bars.length; i++) {
        final bar = bars[i];
        bar.indicatorValues['MY_SIGNAL.value'] = bar.close + offset;
        bar.indicatorValues['MY_SIGNAL.hist'] =
        i.isEven ? offset.toDouble() : -offset.toDouble();
      }
    },
  ),
]);

KChartWidget(
null,
dataSource: source,
symbol: 'BTCUSDT',
interval: '1h',
indicatorRegistry: registry,
indicatorConfigs: const [
KChartIndicatorConfig(
id: 'MY_SIGNAL',
parameters: {'offset': 10},
),
],
panes: const [
KChartPaneConfig.main(weight: 3),
KChartPaneConfig.indicator(indicatorId: 'MY_SIGNAL'),
],
isTrendLine: false,
);

内置指标也可以通过 indicatorConfigs 覆盖计算参数。常用参数 key 包括:BOLL.period/multiplierMACD.fast/slow/signal/multiplierKDJ.period/kSmoothing/dSmoothingRSI.periodWR.periodCCI.period

KChartWidget(
datas,
isTrendLine: false,
indicatorConfigs: const [
KChartIndicatorConfig(
id: KChartIndicatorIds.boll,
parameters: {'period': 20, 'multiplier': 2},
),
KChartIndicatorConfig(
id: KChartIndicatorIds.rsi,
parameters: {'period': 7},
),
KChartIndicatorConfig(
id: KChartIndicatorIds.macd,
parameters: {'fast': 12, 'slow': 26, 'signal': 9},
),
],
);

交易叠加 / Trading Overlay

业务侧可以把订单、持仓开仓价、强平价、止盈止损传给 tradingOverlay,把成交历史通过 tradeMarkers 传给 KChartWidget。库负责绘制、显隐、点击命中、拖拽预览和改价确认;真实下单、撤单和网络提交仍由业务层处理。

  • KChartTradingLine.order:通过 status 显示 Pending、Open、Partial、Filled、Canceled 或 Rejected。
  • KChartTradingLine.position:表示开仓价,可通过 unrealizedPnlunrealizedPnlPercent 显示未实现盈亏。
  • KChartTradingLine.liquidation:显示强平价。
  • KChartTradingLine.takeProfit / stopLoss:通过相同 groupId 与持仓组成 TP/SL 分组;KChartTradingLineGroup 会绘制组连接提示,也可整组隐藏。
  • showOrdersshowPositionsshowLiquidationPriceshowTakeProfitStopLossshowTradeHistory:分别控制各类交易元素显隐;单条 line / marker 也有 visible
  • maxMarkersPerBar:限制单根 K 线同时绘制的成交标记,超出部分聚合到最后一个标记,默认 6
  • tradingLabels:可覆盖订单状态、持仓、强平和确认中文案;未传时自动跟随 isChinese
  • onTradingLineDragConfirm:松手后异步确认,返回 true 才触发 onTradingLineDragEnd;拒绝、异常、超时、订单失效或交易对切换都会恢复原价,迟到结果会被忽略。

tradeMarkers.time 必须精确匹配当前图表数据里的某个 KLineEntity.time。交易所 K 线通常使用当前周期 candle 的开盘时间;如果业务成交时间是具体成交毫秒,需要业务侧先按当前周期归一到对应 candle 的 time。时间匹配不到当前 K 线数据,或者价格 / X 坐标不在当前主图可视区域内,库不会显示这个 mark:

直接传入 datas 时,替换 List 会自动触发重绘;如果业务原地修改同一个 List,请递增 dataRevision。只有原地修改 K 线时间戳且长度、首尾时间都不变时才需要递增 dataTopologyRevision,普通 OHLC 实时更新不应递增它。

示例工程从当前 symbol + interval 的真实 K 线缓存构造交易叠加:最近两根 candle 生成 B/S 成交点,最近 40 根 candle 的价格区间生成开仓、强平、订单和 TP/SL,顶部“成交”按钮可切换成交历史,拖拽订单或 TP/SL 后会弹出确认框。

KChartWidget(
datas,
isTrendLine: false,
tradingOverlay: KChartTradingOverlay(
showTradeHistory: showTradeHistory,
groups: const [
KChartTradingLineGroup(id: 'position_1', label: 'Long #1'),
],
lines: [
KChartTradingLine.position(
id: 'entry_1',
price: 65000,
side: KChartTradeSide.buy,
quantity: '0.25',
unrealizedPnl: 320.5,
unrealizedPnlPercent: 1.97,
groupId: 'position_1',
),
KChartTradingLine.liquidation(
id: 'liq_1',
price: 59000,
side: KChartTradeSide.buy,
groupId: 'position_1',
),
KChartTradingLine.order(
id: 'order_1',
price: 66200,
side: KChartTradeSide.sell,
quantity: '0.10',
status: KChartTradingOrderStatus.partiallyFilled,
),
KChartTradingLine.takeProfit(
id: 'tp_1',
price: 68000,
side: KChartTradeSide.sell,
label: 'TP',
groupId: 'position_1',
),
KChartTradingLine.stopLoss(
id: 'sl_1',
price: 62000,
side: KChartTradeSide.sell,
label: 'SL',
groupId: 'position_1',
),
],
),
tradeMarkers: const [
KChartTradeMarker.buyOrder(
id: 'buy_1',
time: 1717200000000,
price: 65000,
),
KChartTradeMarker.sellOrder(
id: 'sell_1',
time: 1717286400000,
price: 66800,
),
KChartTradeMarker(
id: 'fill_1',
time: 1717372800000,
price: 66120,
side: KChartTradeSide.buy,
label: 'Fill',
style: KChartTradeMarkerStyle.arrow,
),
],
onTradingLineDragConfirm: (line, originalPrice, proposedPrice) async {
return await showAmendConfirmation(
line: line,
originalPrice: originalPrice,
proposedPrice: proposedPrice,
);
},
onTradingLineDragEnd: (line, price) {
// Only called after confirmation; submit the amend request here.
},
onTradingLineTap: (line) {
// Open order / position detail.
},
onTradeMarkerTap: (marker) {
// Open fill detail.
},
);

大量成交点性能 / Large Marker Performance

KChartWidget 内部长期持有 KChartTradingOverlayCache:K 线变化时一次性建立 time -> index 哈希表,marker 变化时按 candle index 排序,绘制和点击时通过二分查找只取当前可视区(含 1 根 overscan)的候选,并通过最多 256 项的 LRU 缓存复用 TextPainter。因此稳定数据下每帧开销由遍历全部成交历史降为 O(log M + V),其中 M 是 marker 总数,V 是可视区 marker 数。

建议业务侧传入不可变列表并在数据变化时替换列表引用。追加成交记录时保留已有 marker 对象(例如 [...oldMarkers, newMarker]),缓存会只索引新增项;不要在每次刷新时重新创建全部历史 marker。如果必须原地修改 tradeMarkers,请递增 tradeMarkerRevision;如果原地修改 tradingOverlay.markers,请递增 KChartTradingOverlay.revision

仓库中的性能测试使用 20,000 根 K 线和 20,000 个 marker,并验证索引只构建一次、每帧只处理可视区候选、B/S 文本布局能够复用:

flutter test test/trading_overlay_painter_test.dart \
  --plain-name "large trade history uses index, visible-range and text caches"

深度图性能 / Depth Performance

DepthChart 会缓存规范化后的买卖盘,并按半屏像素宽度自动降采样后绘制。业务侧通常应替换 bids / asks 列表引用;如果原地修改列表,请同步递增 dataRevision,确保缓存刷新。

无障碍配色 / Accessibility Palette

ChartThemeManager.setColorVisionMode(
ChartColorVisionMode.colorBlindFriendly,
);

ChartThemeManager.setColorVisionPalette(
const ChartColorVisionPalette(
upColor: Color(0xFF1E88E5),
dnColor: Color(0xFFFB8C00),
),
);

色盲友好模式会把默认的红涨绿跌切换为更容易区分的蓝涨橙跌,并同步调整深度图、当前价格标签和信息弹层中的涨跌颜色。 如果你有自己的视觉规范,也可以通过 ChartColorVisionPalette 单独指定色盲模式下的涨跌颜色。


📊 技术指标 / Indicators

  • 主图指标:MA、EMA、BOLL、SAR
  • 成交量指标:VOL、Volume MA
  • 副图指标:MACD、KDJ、RSI、WR、CCI、OBV、StochRSI
  • 支持自定义参数、逐条曲线配色/隐藏、自定义指标注册,以及 calculateFrom 增量计算

🖌️ 绘图工具 / Drawing Tools

支持的绘图工具

  • 📈 趋势线 / 趋势角度 - 支持角度显示
  • ↕️ 垂直线 / 水平线 - 精确定位
  • ➡️ 射线 / 水平射线 - 延伸至图表边界
  • 🏹 箭头标注 - 重要位置标记
  • 十字线 - 价格时间定位

绘图模式

  • 🔄 连续绘制 - 快速添加多个图形
  • 🎯 精确控制 - 像素级精度
  • 🧲 磁铁吸附 - 新建和端点编辑时吸附最近 K 线 OHLC
  • ↩️ 撤销重做 - 新建、移动、编辑、样式、显隐和删除均可回退
  • 💾 持久化 - 支持序列化、恢复校验和数据时间轴重定位
  • 👁️ 显示隐藏 - 灵活管理绘图
  • 🗑️ 批量清除 - 一键清理

✨ 当前版本 / Current Package Scope

  • 当前源码版本为 4.7.x,依赖安装版本从 ^4.7.0 开始
  • 包含完整 K 线、深度图、数据源协议、多 Pane/指标、专业坐标轴、控制器、交易叠加、绘图工具和主题系统
  • 4.7.x 重点优化指标增量计算、实时缺口合并、程序化缩放稳定性,以及示例交易 UI、周期工具栏和深浅主题适配
  • 示例工程位于 example/
  • 详细变更记录见 CHANGELOG.md

💰 商用版获取 / Get Commercial Version

📞 联系方式 / Contact


📚 文档与示例 / Documentation


🤝 社区与支持 / Community & Support


🎯 路线图 / Roadmap

v5.x 计划

  • 完全对标TradingView/主流交易所KChart

📜 许可证 / License

  • 开源包: MIT License
  • 商用支持/定制: 请通过上方联系方式咨询

📢 结语 / Final Words

🎉 Flutter 终于有了真正可商用的 K线图表库!

🚀 不再需要 WebView,不再被 TradingView 限制

💪 让你的 Flutter 金融应用更专业、更流畅!

立即开始 / Get Started Now

  1. GitHub Star 支持我们
  2. 📦 安装免费版 体验功能
  3. 💬 联系我们 获取商用版
  4. 🚀 构建你的 专业金融应用

Made with ❤️ by Flutter Community

如果这个库对你有帮助,请在 GitHub 上给我们一个 Star ⭐