Skip to content

Repaint when the last candle changed #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions lib/src/candle_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,29 @@ class CandleData {
return result;
}

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is CandleData &&
runtimeType == other.runtimeType &&
timestamp == other.timestamp &&
open == other.open &&
high == other.high &&
low == other.low &&
close == other.close &&
volume == other.volume &&
trends == other.trends;

@override
int get hashCode =>
timestamp.hashCode ^
open.hashCode ^
high.hashCode ^
low.hashCode ^
close.hashCode ^
volume.hashCode ^
trends.hashCode;

@override
String toString() => "<CandleData ($timestamp: $close)>";
}
63 changes: 63 additions & 0 deletions lib/src/chart_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class ChartPainter extends CustomPainter {
// Draw time labels (dates) & price labels
_drawTimeLabels(canvas, params);
_drawPriceGridAndLabels(canvas, params);
_drawCurrentPriceLabel(canvas, params);
_drawCurrentPriceLine(canvas, params);

// Draw prices, volumes & trend line
canvas.save();
Expand Down Expand Up @@ -107,6 +109,67 @@ class ChartPainter extends CustomPainter {
});
}

void _drawCurrentPriceLabel(
Canvas canvas,
PainterParams params,
) {
final currentPrice = params.currentPrice;
if (currentPrice == null) {
return;
}
final priceTp = TextPainter(
text: TextSpan(
text: getPriceLabel(currentPrice),
style: params.style.currentPriceStyle
.labelStyle,
),
)
..textDirection = TextDirection.ltr
..layout();

final dx = params.chartWidth + 4;
final dy =
params.fitPrice(currentPrice).clamp(0, params.chartHeight).toDouble() -
priceTp.height / 2;


final padding = params.style.currentPriceStyle.rectPadding;
final radius = params.style.currentPriceStyle.rectRadius;
final rectColor = params.style.currentPriceStyle.rectColor;

final rect = Rect.fromLTWH(
dx, dy, priceTp.width + 2 * padding, priceTp.height + 2 * padding);
final rrect = RRect.fromRectAndRadius(rect, Radius.circular(radius));
canvas.drawRRect(rrect, Paint()..color = rectColor);

priceTp.paint(canvas, Offset(dx + padding, dy + padding));
}

void _drawCurrentPriceLine(Canvas canvas, PainterParams params) {
final currentPrice = params.currentPrice;
if (currentPrice == null) {
return;
}
final paint = Paint()
..color = Colors.red
..strokeWidth = 1
..style = PaintingStyle.stroke;

final dashWidth = 4.0;
final dashSpace = 2.0;
double startX = 0;
final clampedPrice =
params.fitPrice(currentPrice).clamp(0, params.chartHeight).toDouble();
while (startX < params.chartWidth) {
canvas.drawLine(
Offset(startX, clampedPrice),
Offset(startX + dashWidth, clampedPrice),
paint,
);
startX += dashWidth + dashSpace;
}
}

void _drawSingleDay(canvas, PainterParams params, int i) {
final candle = params.candles[i];
final x = i * params.candleWidth;
Expand Down
33 changes: 33 additions & 0 deletions lib/src/chart_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ class ChartStyle {
/// This appears when user clicks on the chart.
final Color overlayBackgroundColor;

/// The style of current price labels (on the right of the chart).
final CurrentPriceStyle currentPriceStyle;

const ChartStyle({
this.volumeHeightFactor = 0.2,
this.priceLabelWidth = 48.0,
Expand All @@ -70,6 +73,15 @@ class ChartStyle {
fontSize: 16,
color: Colors.white,
),
this.currentPriceStyle = const CurrentPriceStyle(
labelStyle: TextStyle(
fontSize: 12,
color: Colors.white,
),
rectPadding: 4.0,
rectRadius: 2.0,
rectColor: Colors.red
),
this.priceGainColor = Colors.green,
this.priceLossColor = Colors.red,
this.volumeColor = Colors.grey,
Expand All @@ -79,3 +91,24 @@ class ChartStyle {
this.overlayBackgroundColor = const Color(0xEE757575),
});
}

class CurrentPriceStyle {
const CurrentPriceStyle({
required this.labelStyle,
required this.rectPadding,
required this.rectRadius,
required this.rectColor,
});

/// The style of current price labels (on the right of the chart).
final TextStyle labelStyle;

/// The padding around the current price rect.
final double rectPadding;

/// The radius of the current price rect.
final double rectRadius;

/// The color of the current price rect.
final Color rectColor;
}
5 changes: 5 additions & 0 deletions lib/src/interactive_chart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ class InteractiveChart extends StatefulWidget {
/// This provides the width of a candlestick at the current zoom level.
final ValueChanged<double>? onCandleResize;

/// The current price to be displayed on the right side of the chart.
final double? currentPrice;

const InteractiveChart({
Key? key,
required this.candles,
Expand All @@ -70,6 +73,7 @@ class InteractiveChart extends StatefulWidget {
this.overlayInfo,
this.onTap,
this.onCandleResize,
this.currentPrice,
}) : this.style = style ?? const ChartStyle(),
assert(candles.length >= 3,
"InteractiveChart requires 3 or more CandleData"),
Expand Down Expand Up @@ -175,6 +179,7 @@ class _InteractiveChartState extends State<InteractiveChart> {
tapPosition: _tapPosition,
leadingTrends: leadingTrends,
trailingTrends: trailingTrends,
currentPrice: widget.currentPrice,
),
),
duration: Duration(milliseconds: 300),
Expand Down
33 changes: 19 additions & 14 deletions lib/src/painter_params.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'candle_data.dart';

class PainterParams {
final List<CandleData> candles;
final double? currentPrice;
final ChartStyle style;
final Size size;
final double candleWidth;
Expand Down Expand Up @@ -35,6 +36,7 @@ class PainterParams {
required this.tapPosition,
required this.leadingTrends,
required this.trailingTrends,
this.currentPrice,
});

double get chartWidth => // width without price labels
Expand Down Expand Up @@ -84,25 +86,28 @@ class PainterParams {
double lerpField(double getField(PainterParams p)) =>
lerpDouble(getField(a), getField(b), t)!;
return PainterParams(
candles: b.candles,
style: b.style,
size: b.size,
candleWidth: b.candleWidth,
startOffset: b.startOffset,
maxPrice: lerpField((p) => p.maxPrice),
minPrice: lerpField((p) => p.minPrice),
maxVol: lerpField((p) => p.maxVol),
minVol: lerpField((p) => p.minVol),
xShift: b.xShift,
tapPosition: b.tapPosition,
leadingTrends: b.leadingTrends,
trailingTrends: b.trailingTrends,
);
candles: b.candles,
style: b.style,
size: b.size,
candleWidth: b.candleWidth,
startOffset: b.startOffset,
maxPrice: lerpField((p) => p.maxPrice),
minPrice: lerpField((p) => p.minPrice),
maxVol: lerpField((p) => p.maxVol),
minVol: lerpField((p) => p.minVol),
xShift: b.xShift,
tapPosition: b.tapPosition,
leadingTrends: b.leadingTrends,
trailingTrends: b.trailingTrends,
currentPrice:
b.currentPrice != null ? lerpField((p) => p.currentPrice!) : null);
}

bool shouldRepaint(PainterParams other) {
if (candles.length != other.candles.length) return true;

if (other.currentPrice != currentPrice) return true;

if (size != other.size ||
candleWidth != other.candleWidth ||
startOffset != other.startOffset ||
Expand Down