Skip to main content

flexi_kline 2.6.1 copy "flexi_kline: ^2.6.1" to clipboard
flexi_kline: ^2.6.1 copied to clipboard

A flexible Flutter candlestick (K-line) chart framework with pluggable indicators, drawing tools, styles and gestures for stock and crypto markets.

2.6.1 #

  • Add TextLayoutCache.debugMissCount (debug-only, @visibleForTesting) so tests can assert that cached label frames do not re-lay out text.
  • Add profile-mode timeline events for the grid, chart, draw and cross layers and for chart range, min/max and pane painting, collectable via FlutterTimeline.debugCollect(); the existing Flexi-PaintChart event is now emitted in profile mode only instead of in every build mode.
  • Add profile-mode Flexi-Merge / Flexi-Compute timeline events for each data pipeline merge and compute batch, and isDataPipelineIdle (@visibleForTesting) on the controller to tell when a data change has been fully merged and computed.

2.6.0 #

  • Raise minimum SDK to Dart 3.11 / Flutter 3.41: TickerModeData, Ticker.forceFrames and TickerMode.getValuesNotifier, used since 2.5.5, first appear in Flutter 3.41, so the previously declared 3.35 floor could not compile (Breaking Changes).
  • Add CandleIndicator.dateMark (DateMarkConfig, DateMarkPosition): after moveToDateTime completes, a date bubble is painted at the target candle and follows pan and zoom until holdDuration expires; it is on by default, so turn dateMark.show off to opt out; customize it with CandlePaintObject.buildDateMarkSpan or paintDateMark; custom CandleBasePaintObject implementations receive didMoveToDate(ts).
  • Fix the latest candle painting outside the main chart when a pushed update exceeds the visible price range: the visible min/max is now recomputed after each data merge.
  • Fix syncFlexiKlineConfig hiding autoActivate indicators that are not in the config children/sub; sync now matches mountIndicators.
  • Fix the last price tap area sitting half a label lower than the painted label: CandlePaintObject.offViewPriceLabelRect is now the painted rect and offViewPriceLabelSize a read-only getter (Breaking Changes).
  • Fix the calculatePaintChartRange and setChartZoomSlideBarRect post-frame callbacks writing to disposed notifiers when a frame lands after dispose.
  • Ignore startDraw and onDrawConfirm when the kline data is empty, so no draw tool can be entered or confirmed with no data to anchor to.
  • Export the painting layer: LabelPainter, LabelStyle, TextLayoutCache, ImagePainter, ImageStyle, ImageSpan, AnchoredBorder, BubbleBorder, PointBuffer, PaintResource and AlignmentPlace (Alignment.place); the extension/render/ drawing API stays available alongside it.
  • Add PaintResourceOwner to PaintObject, DrawObject and KlineBindingBase: resources registered with own() are disposed with the owner, and PaintObject also resets them on an indicator update before didUpdateIndicator; DrawObjectMixin now requires it (on DrawStateObject, PaintResourceOwner) (Breaking Changes).
  • Add ShapeImage, a color-cached drawn icon for inline ImageSpans, with built-in TriangleImage, ChevronImage, DiamondImage, SquareImage, CircleImage, PlusImage, MinusImage, CrossImage, StarImage and BellImage.
  • Replace the off-view latest price arrow glyph ▸ with a TriangleImage, independent of the platform font; add CandlePaintObject.createOffViewPriceArrow to swap the arrow and buildOffViewPriceSpan to replace the label content.
  • Add TextAreaConfig.toLabelStyle to resolve a config into a LabelStyle with theme fallback colors; add TextAreaConfig.gradient and TextAreaConfig.shadows with JSON support, applied only through toLabelStyle.
  • Paint the core labels (axis and cross ticks, time axis, candle high/low, latest and last price, countdown, draw tool ticks and the cross tooltip) with cached LabelPainters: borders are drawn inside the bounds, text limited by its drawable bounds ellipsizes, and labels are positioned by measured height instead of TextAreaConfig.areaHeight (may shift by up to 1px).
  • Fix the countdown label: its colors resolve from CandleIndicator.countdown before the theme, and it is no longer narrower than the latest price label nor clamped into drawableRect.
  • Batch candle bars into one drawRawPoints call or path per trend and add PaintCandleChartMixin.addCandleBar and flushCandleBars; hollow bodies now always use candleHollowBarBorderWidth and every wick candleLineWidth.
  • Add BaseIndicatorConfig, a ChangeNotifier skeleton implementing IIndicatorConfig.
  • Remove IFlexiKlineDataCenter.createFlexiKlineConfig: the framework never called it (Breaking Changes).
  • Rename onPanEnd to onChartPanEnd, and onPaintObjectDragCancel to cancelPaintObjectDrag since it is a command issued across bindings, not a gesture event (Breaking Changes).

2.5.6 #

  • Fix FormatException: NaN is not a valid format thrown from paintGridLines after a few seconds of continuous panning: a Decimal-backed range from a combine child left ComputeMode.fast holding a Decimal endpoint, whose scale then grew every frame until toDouble() returned NaN.
  • Add MinMax.reset and normalize the range to klineData.computeMode in setMinMax and setZoomMinMax, the write entries feeding dyFactor; MainPaintObject.updateMinMax seeds through setMinMax instead of assigning _minMax directly.
  • Keep a MinMax endpoint's compute mode owned by the range itself: updateMinMax, updateMinMaxBy, updateMinMaxByNum and updateMinMaxByDecimal now reset the incoming value to the endpoint's mode, since assignment is the only way around FlexiNum's left-operand rule.
  • Add FlexiNum.clampScale and apply it in MinMax.lerp and MinMax.scaleAroundCenter: Decimal multiplication adds up operand scales, so feeding either result back in grows the scale until toDouble() overflows.
  • Add MinMax.isFinite and reject a non-finite range in setMinMax, keeping the previous one: such a range maps to no coordinate and cannot be converted to Decimal; same stance as _applyZoomFactor, which now shares the getter.

2.5.5 #

  • Add TickerProviderPaintObjectMixin so a PaintObject can drive AnimationController without a Widget-provided vsync; animations freeze while the object is out of the paint tree or the chart is offscreen.
  • Add PaintObject.paintPlaceholder, drawn right after grid lines on frames where kline data is not ready; the default is empty, so existing indicators need no change.
  • Add PaintContext.tickerModeListenable and FlexiKlineController.setTickerMode so PaintObject animations follow the ambient TickerMode.
  • Add Path.takeFraction to take the leading portion of a path by arc length, for linear growth animations.
  • Add FlexiKlineObserver and FlexiKlineEvent as a read-only telemetry channel: register with addObserver / removeObserver to receive type, timestamp and a flat data map from existing interaction entry points; observer exceptions never reach the gesture or paint path.
  • Fix candle and time indicators being removable via external hide*Indicator or dispose*PaintObject calls, which could crash the chart.
  • Fix DrawDirection being applied only when a drawable bound is passed in drawText, drawImageView, drawImageText and drawRectBackground: center did not center on the offset, and drawRectBackground ignored rtl without drawableSize.
  • Fix the boundary clamp ignoring container height in drawText, drawImageView, drawImageText and drawTooltipInfos, which let the container overflow the bottom of the drawable bound.
  • Fix drawImageText reserving image width and spacing for an empty imgSize, which pushed the text outside the background and could trip an assertion.
  • Dispose the TextPainter created by drawText and drawImageText.

2.5.4 #

  • Add DrawContext.isSelectedDrawObject so a DrawObject can branch on selection state, which lives in DrawState rather than on the object itself (Breaking Changes).
  • Rename OverlayObject.isEditing to isCompleted: it only means all points are placed, so an overlay loaded from storage and never selected also returns true (Breaking Changes).
  • Remove OverlayObject.isStarted: it had no callers and its condition was inverted (Breaking Changes).
  • Rename DrawStateObject.setMoveing to setMoving (Breaking Changes).
  • Rename drawPointerListenable to drawingPointerListenable; document that it only pushes while dragging a point of a completed overlay, so it is not a mirror of drawState.pointer (Breaking Changes).
  • Add DrawObject.resetInteraction to leave an interaction without discarding the object; dispose delegates to it and stays the hook for subclasses that release resources.
  • Add shouldPaintDraw combining the draw layer's three gates: DrawConfig.enable, visibility and content; DrawPainter and paintDraw now read the same predicate.
  • Fix isDrawOnTop and isDrawOnBottom throwing instead of returning false when no object is selected.
  • Fix removeDrawObject(object:) leaving drawState pointing at an object that was already removed and disposed.
  • Fix removeAllDrawObjects writing an empty list back to storage right after deleting the key.
  • Fix startDraw dropping an unfinished object without clearing its pointer when the same tool is tapped again.
  • Remove LoadingConfig from SettingConfig and MagnifierConfig from DrawConfig; loading and magnifier styling is now fully controlled via FlexiLoading and FlexiMagnifier prefab constructor parameters instead of the configuration center (Breaking Changes).
  • Raise minimum SDK to Dart 3.9 / Flutter 3.35; bump json_annotation to ^4.12.0 (Breaking Changes).

2.5.3 #

  • Fix draw toolbar re-clamping during canvas resize by using the last known toolbar size instead of reading a widget size during build.
  • Add useThemeColor and borderOpacity to PointConfig to support multiple circle point styles (plain dot, filled + translucent ring, filled + solid border); move hardcoded opacity from call sites into config defaults (Breaking Changes).
  • Bake white into DrawConfig.drawPoint.color and DrawConfig.ticksText style color; DrawConfig.crosspoint no longer carries a borderWidth by default.
  • Replace DrawObjectMixin.valueTicksSize (Size?) with valueTicksRect (Rect?); draw value tick texts before their gap background so the background width matches the current frame (Breaking Changes).

2.5.2 #

  • Redesign FlexiKlineWidget customization: replace ad-hoc view parameters with a unified FlexiKlineWidgetBuilder typedef (BuildContext, FlexiKlineController) → Widget?; add mainBackgroundBuilder, mainForegroundBuilder, loadingBuilder, exitZoomButtonBuilder, drawToolbarBuilder and magnifierBuilder; extract FlexiDraggableDrawToolbar, FlexiPannableDrawToolbar, FlexiLoading, FlexiExitZoomButton and FlexiMagnifier into flexi_kline_prefabs.dart as standalone prefab widgets (Breaking Changes).
  • Remove FlexiKlineWidget.indicator named constructor; pass candle / time / mainIndicators / subIndicators to the default constructor directly (Breaking Changes).
  • Remove mainForegroundViewBuilder, mainBackgroundView, drawToolbarInitHeight, keepDrawToolbarFullyVisible, exitZoomButtonAlignment and exitZoomButtonPadding; replace defaultBottomOffset with toolbarSize in draw toolbar prefabs; add klineContext to FlexiDraggableDrawToolbar; both toolbars listen to canvasRectListenable and re-clamp position on canvas resize (Breaking Changes).
  • Add hasDrawOverlay; DrawPainter skips the whole layer when no overlay exists, so Cross movement no longer re-runs draw overlays.
  • Make internal painting helpers private: paintCrossLine, paintTooltip on CrossBinding; drawOverlayObjectList, drawStateOverlayObject on DrawBinding.
  • Remove @protected from dispose, onThemeChanged, onLanguageChanged and startLastPriceCountDownTimer to clarify they are part of the user-facing API.

2.5.1 #

  • Add bottom tick label for count-mode horizontal grid: dividedPositions now includes endpoints; paintHorizontalGridLines and paintVerticalGridLines skip lines on frame boundaries automatically.
  • Add showTopTick / showBottomTick parameters to paintYAxisTicks for controlling edge tick label visibility.
  • Rename evenPositions to dividedPositions, positionsByCount to tickPositions, computePriceTicks to computeNiceTicks (Breaking Changes).
  • Remove resolveDysByCount; sub-pane indicators now use tickPositions directly (Breaking Changes).
  • Hide niceStep and NiceStep from the public export surface (Breaking Changes).

2.5.0 #

Gesture system rewrite (Breaking Changes) #

  • Remove GestureData, GestureType and GestureState; controller gesture APIs now take plain values (Offset, delta, ratio) instead of a gesture envelope.
  • Split onChartScale into onChartScaleBy (cumulative-ratio, touch pinch) and onChartScaleTo (one-shot ratio, wheel/Web trackpad). Add onChartPanStep for discrete horizontal panning and onChartZoomStep for incremental Y-axis zoom.
  • Change onChartMove signature to onChartMove(Offset delta, {double smoothFactor}).
  • Split onCrossStart into onCrossToggle (tap: open/close) and onCrossFollow (hover: open/move). Change onCrossUpdate, onDrawUpdate, onDrawConfirm, onDrawMoveStart, onGridResizeUpdate, onChartZoomUpdate to take plain Offset/double values.
  • Replace GestureConfig.zoomSpeed with maxZoomPerGesture; rename isManualSetZoomRect to useCustomZoomRect; remove the isConvert parameter of onChartZoomStart; signalScaleFactor now affects X-axis scaling only.
  • Add GestureConfig.panClaimRatio, dragClaimSlopFactor, scaleClaimSlopFactor, signalScaleFactor and scaleSessionTimeout for fine-grained gesture tuning.
  • Remove setMultiTouch, isMultiTouchListenable, FlexiGestureOwner.zoomingMove and mainOriginPadding.

Touch gesture overhaul (Breaking Changes) #

  • Resolve gesture ownership once from PointerDown and claim the arena before an enclosing Scrollable; hit tests use the down position instead of a slop-displaced recognition position, making small targets reachable again.
  • Dispatch chart pan and scale by intent (finger span then direction cone) instead of pointer count; two fingers moving horizontally now pan, vertical/diagonal drags yield to outer scroll views.
  • Separate the pointer session from the recognizer segment so lifting one finger no longer starts inertia mid-gesture; cancelled drags roll back correctly.
  • Remove long-press gesture handling from non-touch devices; enableLongPress no longer affects mouse/trackpad input.

Non-touch (mouse / trackpad) improvements #

  • Rewrite onPointerSignal via GestureBinding.pointerSignalResolver so the chart inside a Scrollable no longer triggers page scroll simultaneously.
  • Pan the chart with horizontally dominant scroll signals (Web trackpad slide, Shift + wheel); dispatch each signal to exactly one action (pan or zoom).
  • Allow trackpad two-finger horizontal slide without misidentifying it as pinch zoom.
  • Resize sub panes by hover + drag on the divider line (resizeRow cursor) instead of long-press.
  • Exit Y-axis zoom with ESC or right-click; cancel cross when starting a draw-object drag.
  • Fix trackpad pinch simultaneously panning the chart; fix cancelled mouse drag starting inertial fling.
  • Add hitTestGridResize for hover cursor feedback; change hover cursor over hittable PaintObject to grab.

Y-axis zoom redesign (Breaking Changes) #

  • Scale the visible price range instead of shrinking the chart area through padding; padding keeps its declared value and mainChartRect stays put, so panning while zoomed no longer re-fits the Y axis.
  • Bound the reachable Y-axis zoom span with SettingConfig.minZoomSpanRatio (default 0.05) and maxZoomSpanRatio (default 20); clamp instead of reject so the wheel no longer deadlocks at the bound.
  • Track zoom as a cumulative factor derived from the range snapshot on press, unifying touch and scroll-wheel behaviour; dragging back to the start restores the original range.
  • Move zoom state (setZoomMinMax, clearZoomMinMax, hasZoomMinMax) from geometry mixin to MainPaintObject; combine children proxy parent minMax via override.
  • Shift the visible price range on vertical drag while zoomed, with horizontal inertial pan available in the same gesture.
  • Hand the Y axis back to auto-fit on symbol change; keep the user-set range on interval change.
  • Fix price range becoming non-finite after repeated zoom-out; fix repeated touch drags compressing range without limit.

Grid line and tick system redesign (Breaking Changes) #

  • Move grid lines from the grid layer into the main candle object; the grid layer now only draws borders, pane separators and drag affordances.
  • Add IPaintObject.paintGridLines as the indicator-level grid entry, painting both axes in one pass and returning resolved positions.
  • Add sealed GridTickMode with three modes: count(divisions), size(spacing) and nice(targetDivisions).
  • Round main price axis ticks to readable numbers (nice mode, now the default); tick labels paint above indicators instead of being covered by them.
  • Rename GridAxis to GridBorder; sink tick config into indicators as CandleBaseIndicator.horizontalGrid / verticalGrid (GridAxisConfig).
  • Rename PaintYAxisTicksMixin to PaintGridTicksMixin; rename nice_tick_util.dart to grid_tick_util.dart.
  • Expose PaintContext.gridVerticalDxs so sub indicators can align vertical lines with the main pane.

Framework and layout (Breaking Changes) #

  • Remove _tmpPadding, setPadding and padding parameter from doUpdateLayout; padding returns indicator.padding directly; combine children proxy parent padding.
  • Separate smooth display value from auto-computed target; smoothMinMax() no longer writes _minMax.
  • Move didChangeTheme declaration to IndicatorObject with @mustCallSuper.
  • Cache default Paint objects in PaintStyleMixin; clear on theme change.
  • Make paintDxOffset read-only on the controller; writes go through one clamped, notifying write point.
  • Remove PaintContext.reportChartZoomSlideBarRect; the zoom hit area is now derived from tick-label width.
  • Remove CandleBasePaintObject.paintYAxisTickLines and paintYAxisTickLabels.

Data validation #

  • Replace KlineData.canPaintChart with checkStartAndEnd(start, end) on BaseData; move to BaseData alongside start/end accessors.
  • Validate the effective range in candle painting methods after applying defaults.
  • Guard dyFactor against non-positive chartRect.height; clamp candleMinWidth to at least 1.

Draw overlays and config API (Breaking Changes) #

  • Persist draw overlays on every change instead of only on storeFlexiKlineConfig(), so a finished drawing survives a controller rebuild or app restart.
  • Refresh the draw overlay tree in syncFlexiKlineConfig(): overlays use their own storage key, which a shared FlexiKlineConfig instance could not carry between controllers.
  • Apply the current DrawConfig to existing overlays on sync; they kept the snapshot taken at construction, so updateDrawConfig never reached them.
  • Exit the draw state on sync, since rebuilding disposes the object drawState may point at.
  • Write back a point's ts / value when a tap confirms it during editing; only the screen offset was updated, so the point jumped back on sync.
  • Rename reloadFlexiKlineConfig to syncFlexiKlineConfig: it is not the inverse of storeFlexiKlineConfig and reads no storage when getFlexiKlineConfig() returns a shared instance (Breaking Changes).
  • Remove the storeDrawOverlays parameter of storeFlexiKlineConfig: overlays persist on change, so it was a redundant re-write (Breaking Changes).
  • Move the storeFlexiKlineConfig declaration off KlineBindingBase into SettingBinding: the framework never calls it (Breaking Changes).

Geometry utilities #

  • Add clipLineToRect(A, B, rect), which clips the full line through two points to a rect and returns its two boundary points.
  • Fix reflectToRectSide returning rect.top where rect.left was meant, which put the left-side intersection at the wrong x for any rect whose origin is not (0, 0).
  • Fix isInsideOfPolygon never testing the closing edge, so an n-gon was judged by n-1 edges and points outside the last edge read as inside; callers no longer need to repeat the first vertex.

Other #

  • Add MinMax.scaleAroundCenter and MinMax.shift.
  • Add Rect.distanceFromBottom.
  • Rename scaledSingal to scaledSignal (public API typo fix).

2.4.1 #

  • Fix the blank band above the candles left after hiding main-area indicators, which survived config reload and data refresh and could only be cleared by rebuilding the controller.
  • Move the main-area tips height out of padding into a dedicated geometry input consumed by topRect: the tips area follows activation and declaration changes on the next paint with no explicit reset, and padding keeps its declared value throughout (Breaking Changes).
  • Remove the MainPaintObject.paintTips override that returned topRect.size: it had no caller and closed a feedback loop between the tips height and the padding it derived from (Breaking Changes).

2.4.0 #

  • Fix a permanent blank band above the candles after main-area indicators are hidden: tips-driven padding.top now resets whenever the activation set or indicator declarations change, then regrows on the next frame.
  • Serialize candle merging and indicator calculation through one data pipeline bound to the current KlineData, driven by a fixed calculation tick (FlexiKlineController.calculationInterval, default 500ms): the two phases never modify one KlineData concurrently, and switching spec destroys the old pipeline together with its pending queue (Breaking Changes).
  • Replace updateKlineData(spec, list, reset:) with replaceKlineData, updateLatestKlineData and appendHistoryKlineData, so the caller declares intent instead of the pipeline inferring direction from timestamps (Breaking Changes).
  • Move indicator calculation out of PaintObject into IndicatorCalculator: ComputedIndicator now requires createCalculator(dataIndex) and answers shouldRecompute from calcParam, while ComputedPaintObject only reads slots and paints (Breaking Changes).
  • Remove KlineData.enqueueWaitingData, hasWaitingData and waitingDataLength; pre-mount input is held by the pipeline instead of by KlineData (Breaking Changes).
  • Add evictInactiveKlineDataCache() and drop non-current cached KlineData when computed slot layout or indicator params change, so a cached snapshot cannot carry stale indicator values.
  • Fix the accumulated dirty range not translating when updateLatestKlineData inserts a new head candle, which silently skipped recompute for incremental indicators (MA/EMA/VolMA/BOLL/KDJ/WR/CCI/OBV).
  • Route a cache-miss switchKlineData through the same path as a cache hit: it now clears the chart, resets the viewport and cancels the active cross; markRepaintAll covers the chart+cross+draw repaint triple.
  • Replace IConfiguration.generateFlexiKlineConfig with getFlexiKlineConfig and saveFlexiKlineConfig; implementations mixing in FlexiKlineConfigurationMixin need no change (Breaking Changes).
  • Remove the autoSave constructor parameter and all implicit persistence, which no longer happens on dispose or onThemeChanged: callers must invoke storeFlexiKlineConfig() explicitly or user adjustments are silently lost (Breaking Changes).
  • Add reloadFlexiKlineConfig([config]) so one controller can catch up with another, fixing landscape config changes not taking effect after returning to portrait; override getFlexiKlineConfig to return a cached instance when several controllers share one runtime config.
  • Fix evicting the head of a full sub-indicator queue leaving its key in the config, which made the reload diff rotate the sub queue on every call.
  • Add PaintObject drag hooks handleDragStart/handleDragUpdate/handleDragEnd/handleDragCancel plus the side-effect-free hitTestDragStart an indicator must also implement to be draggable inside a scrollable container: the first object claiming the landing position owns the whole gesture, and the framework suppresses chart pan, inertial pan, loadMore and Cross updates for its duration.
  • Add FlexiKlineController.hitTestPaintObjectDrag and hitTestDrawObjectDrag, mirroring the hit rules of onPaintObjectDragStart and onDrawMoveStart without claiming the drag.
  • Dispatch PaintObject taps and drag starts by position and descending zIndex so the visually topmost object answers first: main-area hits iterate the new MainPaintObject.reversedPaintableChildren, MainPaintObject.handleTap is gone, and a hit region must lie within its owning pane (Breaking Changes).
  • Change MainPaintObject.paintableChildren to return Iterable<PaintObject> instead of Set<PaintObject>, which stops allocating a set per call in line chart mode and stops handing out the live children set; use the new isPaintable for membership queries (Breaking Changes).
  • Fix main-area indicators hidden by line chart mode staying tappable through their last painted hit regions: tap and drag-start dispatch now exclude unpaintable objects like the paint path does.
  • Cancel an active Cross after a PaintObject consumes a tap, while preserving Cross-owned tooltip taps.
  • Add PaintContext.requestReleasePaintObject, one channel a PaintObject uses to have the framework drop what it holds for that object, covering both leaving the paint tree and voluntarily aborting an in-flight interaction, so onExitTree no longer enumerates framework-side references one by one (Breaking Changes).
  • Fix FlexiStateNotifier.updateValue notifying twice when the value actually changes; it now notifies exactly once and still notifies on in-place mutation.
  • Resolve touch gesture ownership once from the first PointerDown — draw drawing/editing, Cross, PaintObject, zoom slider and zooming move — and claim the gesture arena before an enclosing Scrollable, which a landed gesture could never win because single-finger pan slop is by definition twice the outer hit slop; hit tests and drag baselines both use the PointerDown position instead of the recognition position a slop away, so small targets are reachable again (DrawConfig.hitTestMinDistance is 10px against a 36px kPanSlop) and the first drag update covers the pre-recognition displacement. Blank-area drags still scroll the outer view.
  • Set DragStartBehavior.down on the non-touch detector so onPanStart reports the PointerDown position for non-mouse pointers, and enable onPointerCancel there so a cancelled drag rolls back instead of being committed by onPanEnd.
  • Replace touch-path gestureArena.sweep calls with owner-driven Scale claims: a move-then-release on a claimed target is now a drag rather than a tap once the displacement passes the claim slop, which on touch is half the outer hit slop instead of kPanSlop; smaller displacements stay taps (Breaking Changes).
  • Drive every landed gesture through onScaleUpdate instead of Listener.onPointerMove, deriving coordinates from a fixed anchor plus the first pointer's total displacement so the per-frame throttle cannot drop movement; a Cross drag anchors on the authoritative crossOffset rather than the gesture data left by the last onTapUp, and long press yields the arena for Cross and in-progress drawing, the two owners long press itself does nothing for.
  • Keep a landed gesture's owner and its first-pointer coordinates when a second finger joins, instead of cancelling the gesture or snapping it to the two-pointer centroid; chart scale still uses the centroid (Breaking Changes).
  • Dispatch chart pan and scale by intent instead of pointer count — finger span first, then a |dx| > |dy| × panClaimRatio direction cone — so two fingers moving together horizontally pan instead of winning the arena and doing nothing, and a horizontally dominant drag claims at the outer hit slop (18px) instead of kPanSlop (36px) while vertical and shallow diagonal drags yield to an enclosing scroll view (Breaking Changes).
  • Switch chart pan to chart scale mid-gesture once the finger span changes by more than kScaleSlop, resetting the pan smooth factor on the way; the reverse switch is refused because span stops being a usable baseline once scaling began.
  • Claim the arena for chart scale on any pointer's displacement rather than only the first pointer's, compared against the quantity the outer Scrollable uses (spanDelta × 2 > hitSlop × scaleClaimSlopFactor), which fixes vertical pinch losing to an enclosing scroll view and having made two-finger zoom appear to require a horizontal grip.
  • Add GestureConfig.panClaimRatio (default 2, clamped to 110), dragClaimSlopFactor (default 0.5, clamped to 0.10.9) and scaleClaimSlopFactor (default 1, clamped to 0.5~4); the two slop factors are ratios of the outer hit slop rather than pixel values, because touchSlop is platform-supplied and often below kTouchSlop on Android where a hardcoded threshold fails on some devices.
  • Remove setMultiTouch and isMultiTouchListenable: arena claiming replaced the need for hosts to swap in NeverScrollableScrollPhysics, and disabling the outer scroll view also swallowed the two-finger vertical drag it should receive (Breaking Changes).
  • Separate the pointer session from the recognizer segment, which ScaleGestureRecognizer splits on every pointer count change: commit a scale segment's candleWidth on every scale end, but keep inertial pan and loadMore for the end of the whole session so lifting one finger no longer starts inertia mid-gesture, and accumulate PointerCancel across the session so a cancelled chart pan skips inertial pan and a cancelled PaintObject drag rolls back (Breaking Changes).
  • Take over an in-flight inertial pan on the next touch instead of swallowing that gesture, which the previous "last gesture not finished" guard dropped after a fling (Breaking Changes).
  • Resolve ScalePosition.auto once per pointer session, so lifting one finger of a pinch and spreading again no longer moves the scale anchor from middle to left.
  • Restore the pan smooth factor when a position animation is interrupted, which animateToPosition's completion callback never did: the leftover value kept the Y-axis min/max interpolating and clipped overlays to canvasRect instead of mainRect.
  • Reorder landed gesture ownership so the zoom slider and zooming move yield to draw, Cross and PaintObject, and gate the zoom slider's PointerDown claim on the same chain: dragging with the crosshair shown no longer moves the chart and leaves the crosshair stuck, and starting a zoom requires leaving Cross first (Breaking Changes).
  • Stop a Cross drag from dismissing the Cross on release, and stop dragging an in-progress drawing point from confirming it: both are entered and left by tapping, and panning in between only moves them (Breaking Changes).
  • Stop a chart fallback gesture from calling draw callbacks after a failed draw claim degraded it to chart pan, so dragging blank space with an overlay selected pans the chart instead of moving every point of that overlay (Breaking Changes).
  • Fix pinch zoom jumping on the first frame: set DragStartBehavior.start on the chart's scale recognizer so _initialSpan is rebased on accept and ScaleUpdateDetails.scale starts from 1.0.

2.3.2 #

  • Add crossOffsetListenable so external consumers can subscribe to Cross focus changes.
  • Constrain multiline drawText and drawImageText layouts to their drawableRect, while retaining existing single-line width behavior.
  • Change FlexiKlineController.moveToDateTime(DateTime) to an awaitable API returning Future<int?>: it completes after the movement animation and returns the resolved candle index, or null when the request cannot complete (Breaking Changes).

2.3.1 #

  • Add optional TipsConfig.lineWidth to override the width of the indicator line a tips represents (color still derived from style); when null it falls back to the indicator-level lineWidth.
  • Add defaultAuxiliaryLineWidth constant (0.5) and apply it to auxiliary lines (crosshair, grid, high/low and latest-price mark lines), replacing scattered 0.5 literals and distinguishing them from data lines (defaultIndicatorLineWidth).
  • Simplify magnifier CircleBorder resolution: only the border color falls back to the theme grid-line color when transparent; the configured width and style are now respected (previously width <= 0 was forced to 1 and BorderStyle.none to solid). MagnifierConfig.shapeSide default is now BorderSide(color: transparent, width: 0.5).
  • Fix TooltipConfig JSON deserialization to default hitTestMargin to 2, matching the constructor default.

2.3.0 #

  • Improve FlexiLayoutMode.fixed canvas size resolution: resolve width and height independently (parent constraints first, fixedSize fallback) to cover more constraint combinations.
  • Relax initialFixedSize assert in fixed layout mode: only height must be finite when provided; width can be resolved from parent constraints.
  • Add isMounted guard in paintChart to prevent accessing uninitialized PaintObjects during early render frames.
  • Align paint naming by drawing position: rename indicator top-bar hook paintTooltip to paintTips; Cross-layer floating box APIs (TooltipInfo, TooltipConfig, CrossBinding.paintTooltip) remain unchanged (Breaking Changes).
  • Rename allowPaintExtraOutsideMainRect to allowOverlayOutsideMainRect in SettingConfig and JSON serialization (Breaking Changes).
  • Add default no-op paintTips implementation on base PaintObject.
  • Remove redundant computeVisibleMinMax and paintTips overrides from TimePaintObject.
  • Add Indicator.autoActivate to control auto-show on mount or false→true update; Direct/Computed default false, External default true, Candle/Time/Main fixed true; turning to false does not auto-hide (Breaking Changes).
  • Defer PaintObject creation to activation time: External indicators are no longer eagerly created on declaration; keepAlive only controls dispose-on-hide (Breaking Changes).
  • mountIndicators now activates the deduplicated union of persisted keys and autoActivate declarations; updateIndicators returns pending activation keys for showMainIndicator/showSubIndicator.
  • Fix computed slot capacity as a high-water mark so deleting middle indicators does not shrink slots array below surviving high-index data.
  • Add FlexiKlineController.moveToDateTime(DateTime) with animated viewport positioning; add KlineData.indexAtOrBefore for nearest loaded candle lookup.
  • Unify Cross tooltip rendering via Canvas.drawTooltipInfos for aligned two-column layout; add TooltipInfo.onTap with configurable TooltipConfig.hitTestMargin and TooltipConfig.spacing (Breaking Changes).
  • Remove TooltipInfo.riseOrFall in favor of valueStyle at the data layer (Breaking Changes).
  • Stabilize tooltip width during crossing via session-level minContentWidth tracking.

2.2.0 #

  • Redesign widget-level indicator declaration: FlexiKlineWidget now accepts candle, time, mainIndicators, subIndicators directly; add FlexiKlineWidget.indicator named constructor for IIndicatorConfig (Breaking Changes).
  • Simplify IConfiguration interface: remove configKey, candleIndicatorBuilder, timeIndicatorBuilder, mainIndicatorBuilders, subIndicatorBuilders; retain only theme, generateFlexiKlineConfig, drawObjectBuilders (Breaking Changes).
  • Introduce IIndicatorConfig interface to decouple indicator provision from framework configuration; user-owned storage replaces built-in persistence (Breaking Changes).
  • Rename indicator taxonomy: Normal→Direct, Data→Computed, Business→External; NormalIndicatorKey→DirectIndicatorKey, DataIndicatorKey→ComputedIndicatorKey, BusinessIndicatorKey→ExternalIndicatorKey (Breaking Changes).
  • Rename PaintObject base classes: DataPaintObject→ComputedPaintObject, BusinessPaintObject→ExternalPaintObject, NormalPaintObject→DirectPaintObject (Breaking Changes).
  • Introduce IPaintLifecycle interface with unified lifecycle hooks: initState, didChangeDependencies, didAttach, didDetach (Breaking Changes).
  • Replace syncAllIndicators()/init() with mountIndicators(); rename syncIndicators() to updateIndicators() (Breaking Changes).
  • Replace LayoutMode class hierarchy with FlexiLayoutMode enum (adapt/fixed); add FlexiKlineController.initialLayoutMode parameter (Breaking Changes).
  • Replace autoAdaptLayout bool on FlexiKlineWidget with FlexiLayoutType enum (adapt/fixed/normal) (Breaking Changes).
  • Split IPaintContext into scoped interfaces: PaintEnvironment, PaintDataScope, PaintGeometryScope, PaintRuntimeScope (Breaking Changes).
  • Split IDrawContext into scoped interfaces: DrawEnvironment, DrawDataScope, DrawGeometryScope, DrawRuntimeScope (Breaking Changes).
  • Rename curKlineData to klineData; rename cancelCross to requestCancelCross (Breaking Changes).
  • Rename FlexiKlineThemeConfigurationMixin to FlexiKlineConfigurationMixin (Breaking Changes).
  • Add FlexiKlineLifecycle enum (initial/mounted/disposed) with lifecycleListenable for controller state observation.
  • Add PaintObject.mount() lifecycle method (mirrors Flutter Element.mount); inject indicator/context/logger at mount time.
  • Add isMounted guards to controller APIs (setCanvasSize, setMainSize, onThemeChanged, etc.) to prevent late-field access before mountIndicators completes.
  • Add FlexiStateNotifier.setSilently for build-phase value initialization without triggering subscriber setState.
  • Move grid/chart/cross/draw repaint triggers onto KlineBindingBase; internalize layer repaint APIs.

2.1.1 #

  • Optimize mergeCandleList performance: add in-place fast paths for head/tail aligned updates to reduce unnecessary list copies on hot path.
  • Fix getLoadMoreSpec() from/to direction for loading more historical data.
  • Fix combine sub-indicator (e.g. MA) min/max not refreshed after add/remove, which caused lines to be drawn at chart bottom.
  • Fix candle interval chart-type lookup to use ITimeInterval equality instead of milliseconds comparison.
  • Expose chartZoomSlideBarRect on IPaintContext and indicator paint context for custom hit-testing or overlay layout.
  • Remove built-in interval constants (interval1m, interval1D, etc.) from constant.dart; use FlexiTimeInterval or custom ITimeInterval implementations instead (Breaking Changes).
  • KlineSpec.interval is now required; default limit changed from 100 to 200.
  • Default CandleIndicator no longer pre-configures line chart for 1s/1m intervals.

2.1.0 #

  • Simplify configuration system: config color fields are now nullable, theme colors are injected at paint time via .ensure() pattern (Breaking Changes).
  • Decouple theme colors from config objects: replace hardcoded color defaults with lazy injection mechanism (Breaking Changes).
  • Redesign IFlexiKlineTheme: remove scale/pixel/setDp/setSp methods and BaseFlexiKlineTheme class; rename color properties for clarity (long→longColor, short→shortColor, gridLine→gridLineColor, etc.) (Breaking Changes).
  • Rename timeBar parameter to interval for consistency across the codebase (Breaking Changes).
  • Simplify time interval interface: introduce ITimeInterval/FlexiTimeInterval to replace TimeBar enum (Breaking Changes).
  • Restructure logging system: introduce layered IFlexiLogger/FlexiLog architecture with cleaner separation of concerns (Breaking Changes).
  • Update kline data structures: add KlineSpec class and KlineLoadingState enum to replace CandleReq (Breaking Changes).
  • Add Color? extension methods (isValid, ensure, or) in style_ext.dart for nullable color handling.
  • Add themeColor params to drawing primitives (drawLineByConfig, drawCirclePoint, drawTextArea).
  • Rename TextAreaConfig.background to backgroundColor for consistency.
  • Make default configs const where possible for better performance.
  • Clean up configuration framework and improve serialization handling.

2.0.0 #

  • Replace BagNum with FlexiNum for numeric representation (Breaking Changes).
  • Introduce ICandleModel interface and FlexiCandleModel; unify candle model to support custom models (Breaking Changes).
  • Refactor Indicator to generic class with typed key system: IIndicatorKey sealed class with DataIndicatorKey, BusinessIndicatorKey and NormalIndicatorKey subtypes (Breaking Changes).
  • Split PaintObjectIndicator into DataIndicator and BusinessIndicator with corresponding DataPaintObject and BusinessPaintObject (Breaking Changes).
  • Refactor paint object interfaces: rename IPaintBoundingBox to IPaintBounding, introduce IBasePainter/IComputablePainter/IBusinessPainter, add PaintObjectComputableMixin (Breaking Changes).
  • Change PaintObject initialization to lazy pattern; remove context and indicator from constructors (Breaking Changes).
  • Deprecate TimeBar enum and unify with ITimeBar (Breaking Changes).
  • Rename supportLongPress to enableLongPress in GestureConfig (Breaking Changes).
  • Refactor project structure: extract TimeBar, LayoutMode, FlexiChartType, indicator keys and paint interfaces into independent modules; rename common.dart files to types.dart / interfaces.dart.
  • Add FlexiUpdater<T> typedef and updateXxxConfig convenience methods to SettingBinding.
  • Improve type safety: replace manual casts with whereType<IComputablePainter>().
  • Add enableScale property to GestureConfig to allow disabling scale/zoom gestures.
  • Resolve two-finger zoom gesture conflicts and optimize zoom experience.
  • Add Y-axis smooth interpolation during pan to reduce coordinate jumps.
  • Add panSmoothFactor and convergenceRatio to ToleranceConfig for configurable smoothing.
  • Update inertial pan duration formula from log to sqrt for better velocity-to-duration mapping.
  • Enhance lint rules and apply fixes across codebase.

1.2.1 #

  • Refactor ChartType to FlexiChartType with improved structure and key property (Breaking Changes).
  • Add negative number constants to BagNum (minusHundred, minusFifty, minusTen, minusThree, minusTwo, minusOne).
  • Add time bar comparison methods (isSameAs, compareTimeBar) for ITimeBar.
  • Improve chart framework serialization and object handling.
  • Update flexi_formatter dependency to ^1.7.3.

1.2.0 #

  • Fix multiple naming typos in public APIs (Breaking Changes).
  • Add latest candle point marker for line chart.
  • Refactor ChartType to sealed class; replace timeChartType/minCandleWidthChartType with timeBarChartTypes/minWidthLineType for flexible chart type configuration (Breaking Changes).
  • Move hideIndicatorsInTimeChart from SettingConfig to CandleIndicator as hideIndicatorsWhenLineChart (Breaking Changes).
  • Replace LinearGradient with GradientConfig for better serialization and flexibility; rename chart painting methods (Breaking Changes).

1.1.1 #

  • Add listening for the painting range changes.
  • Optimize the minmax of chart when no market fluctuation.
  • Optimize the details of the zoom operation; Add listening to the zoom slide bar area.
  • bugfix.

1.1.0 #

  • Optimize the TimeBar and support flexible customization.
  • Support image drawing.
  • Support image and text drawing.
  • Add more painting methods.
  • bugfix.

1.0.0 #

  • Optimize the calculation performance of indicators
  • Optimize gesture detector performance.
  • Optimize configuration management: load, store, update.
  • Optimize KlineData structure.
  • Added multiple chart types and styles for candlestick charts.
  • Provide FlexiKlinePage mixin to assist development.
  • Optimize indicator chart painting performance and configuration.
  • Optimize scrolling animation.
  • bugfix.

0.9.1 #

  • Rearrange layout mode And bugfix.
  • Optimize web gestures.

0.9.0 #

  • Configuration management refactoring
  • Optimization indicator framework
  • Add keyboard event handling on non-touch devices
  • Add layout mode: normal, adapt, fixed
  • Add zoom chart feature
  • bugfix

0.8.0 #

  • New Architecture Design
  • Indicator Framework Refactoring
  • Adjust the height of the indicator chart by dragging the Grid line
  • bugfix

0.7.1 #

  • Support Draw Feature
  • Optimize gesture

0.7.0 #

  • Drawing overlay framework implementation
  • Optimize coordinate conversion of indicator chart data
  • Optimize drawing gesture processing
  • Added drawing magnifying glass

0.6.0 #

  • Support Desktop
  • Support Web
  • Refactor gesture recognition framework to adapt to Web/Desktop
  • Demo for custom indicators and replacing built-in indicators.

0.5.0 #

  • Support landscape mode.
  • Optimize zooming functionality.
  • Optimize configuration management.
  • Dispose indicators when unbinding.
  • Add secondary chart indicators.

0.4.0 #

  • Theme switching implementation.
  • Configuration framework optimization.
  • Data loading interface encapsulation.
  • Candle data merging algorithm optimization.
  • Indicator calculation performance optimization.
  • Gesture operation optimization.
  • Flutter minimum supported version

0.3.0 #

  • Configuration framework implementation
  • Optimization of metric data calculation
  • Core + framework framework optimization
  • Kline implements dynamic adjustment of width and height

0.2.0 #

  • New drawing architecture design.
  • Indicator configuration management (serialization, local storage)
  • Indicator calculation design and implementation Volume, MA, EMA, BOLL, MACD, KDJ, MAVOL

0.1.0 #

  • 项目FlexiKline
  • 整体框架
  • 基础蜡烛图绘制
  • example示例
16
likes
150
points
565
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A flexible Flutter candlestick (K-line) chart framework with pluggable indicators, drawing tools, styles and gestures for stock and crypto markets.

Homepage
Repository (GitHub)
View/report issues

Topics

#chart #kline #candlestick #stock

License

Apache-2.0 (license)

Dependencies

copy_with_extension, decimal, equatable, flexi_formatter, flutter, json_annotation

More

Packages that depend on flexi_kline