forked from imaNNeo/fl_chart
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathline_chart_data.dart
More file actions
1402 lines (1234 loc) · 50.3 KB
/
line_chart_data.dart
File metadata and controls
1402 lines (1234 loc) · 50.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// coverage:ignore-file
import 'dart:ui';
import 'package:equatable/equatable.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:fl_chart/src/extensions/color_extension.dart';
import 'package:fl_chart/src/extensions/gradient_extension.dart';
import 'package:fl_chart/src/utils/lerp.dart';
import 'package:flutter/material.dart' hide Image;
/// [LineChart] needs this class to render itself.
///
/// It holds data needed to draw a line chart,
/// including bar lines, spots, colors, touches, ...
class LineChartData extends AxisChartData with EquatableMixin {
/// [LineChart] draws some lines in various shapes and overlaps them.
/// lines are defined in [lineBarsData], sometimes you need to fill space between two bars
/// with a color or gradient, you can use [betweenBarsData] to achieve that.
///
/// It draws some titles on left, top, right, bottom sides per each axis number,
/// you can modify [titlesData] to have your custom titles,
/// also you can define the axis title (one text per axis) for each side
/// using [axisTitleData], you can restrict the y axis using [minY] and [maxY] value,
/// and restrict x axis using [minX] and [maxX].
///
/// It draws a color as a background behind everything you can set it using [backgroundColor],
/// then a grid over it, you can customize it using [gridData],
/// and it draws 4 borders around your chart, you can customize it using [borderData].
///
/// You can annotate some regions with a highlight color using [rangeAnnotations].
///
/// You can modify [lineTouchData] to customize touch behaviors and responses.
///
/// you can show some tooltipIndicators (a popup with an information)
/// on top of each [LineChartBarData.spots] using [showingTooltipIndicators],
/// just put line indicator number and spots indices you want to show it on top of them.
///
/// [LineChart] draws some horizontal or vertical lines on above or below of everything,
/// they are useful in some scenarios, for example you can show average line, you can fill
/// [extraLinesData] property to have your extra lines.
///
/// [clipData] forces the [LineChart] to draw lines inside the chart bounding box.
LineChartData({
this.lineBarsData = const [],
this.betweenBarsData = const [],
super.titlesData = const FlTitlesData(),
super.extraLinesData = const ExtraLinesData(),
this.lineTouchData = const LineTouchData(),
this.showingTooltipIndicators = const [],
super.gridData = const FlGridData(),
super.borderData,
super.rangeAnnotations = const RangeAnnotations(),
double? minX,
double? maxX,
super.baselineX,
double? minY,
double? maxY,
super.baselineY,
super.clipData = const FlClipData.none(),
super.backgroundColor,
super.rotationQuarterTurns,
}) : super(
minX: minX ?? double.nan,
maxX: maxX ?? double.nan,
minY: minY ?? double.nan,
maxY: maxY ?? double.nan,
);
/// [LineChart] draws some lines in various shapes and overlaps them.
final List<LineChartBarData> lineBarsData;
/// Fills area between two [LineChartBarData] with a color or gradient.
final List<BetweenBarsData> betweenBarsData;
/// Handles touch behaviors and responses.
final LineTouchData lineTouchData;
/// You can show some tooltipIndicators (a popup with an information)
/// on top of each [LineChartBarData.spots] using [showingTooltipIndicators],
/// just put line indicator number and spots indices you want to show it on top of them.
///
/// An important point is that you have to disable the default touch behaviour
/// to show the tooltip manually, see [LineTouchData.handleBuiltInTouches].
final List<ShowingTooltipIndicators> showingTooltipIndicators;
/// Lerps a [BaseChartData] based on [t] value, check [Tween.lerp].
@override
LineChartData lerp(BaseChartData a, BaseChartData b, double t) {
if (a is LineChartData && b is LineChartData) {
return LineChartData(
minX: lerpDouble(a.minX, b.minX, t),
maxX: lerpDouble(a.maxX, b.maxX, t),
baselineX: lerpDouble(a.baselineX, b.baselineX, t),
minY: lerpDouble(a.minY, b.minY, t),
maxY: lerpDouble(a.maxY, b.maxY, t),
baselineY: lerpDouble(a.baselineY, b.baselineY, t),
backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
borderData: FlBorderData.lerp(a.borderData, b.borderData, t),
clipData: b.clipData,
extraLinesData:
ExtraLinesData.lerp(a.extraLinesData, b.extraLinesData, t),
gridData: FlGridData.lerp(a.gridData, b.gridData, t),
titlesData: FlTitlesData.lerp(a.titlesData, b.titlesData, t),
rangeAnnotations:
RangeAnnotations.lerp(a.rangeAnnotations, b.rangeAnnotations, t),
lineBarsData:
lerpLineChartBarDataList(a.lineBarsData, b.lineBarsData, t)!,
betweenBarsData:
lerpBetweenBarsDataList(a.betweenBarsData, b.betweenBarsData, t)!,
lineTouchData: b.lineTouchData,
showingTooltipIndicators: b.showingTooltipIndicators,
rotationQuarterTurns: b.rotationQuarterTurns,
);
} else {
throw Exception('Illegal State');
}
}
/// Copies current [LineChartData] to a new [LineChartData],
/// and replaces provided values.
LineChartData copyWith({
List<LineChartBarData>? lineBarsData,
List<BetweenBarsData>? betweenBarsData,
FlTitlesData? titlesData,
RangeAnnotations? rangeAnnotations,
ExtraLinesData? extraLinesData,
LineTouchData? lineTouchData,
List<ShowingTooltipIndicators>? showingTooltipIndicators,
FlGridData? gridData,
FlBorderData? borderData,
double? minX,
double? maxX,
double? baselineX,
double? minY,
double? maxY,
double? baselineY,
FlClipData? clipData,
Color? backgroundColor,
int? rotationQuarterTurns,
}) =>
LineChartData(
lineBarsData: lineBarsData ?? this.lineBarsData,
betweenBarsData: betweenBarsData ?? this.betweenBarsData,
titlesData: titlesData ?? this.titlesData,
rangeAnnotations: rangeAnnotations ?? this.rangeAnnotations,
extraLinesData: extraLinesData ?? this.extraLinesData,
lineTouchData: lineTouchData ?? this.lineTouchData,
showingTooltipIndicators:
showingTooltipIndicators ?? this.showingTooltipIndicators,
gridData: gridData ?? this.gridData,
borderData: borderData ?? this.borderData,
minX: minX ?? this.minX,
maxX: maxX ?? this.maxX,
baselineX: baselineX ?? this.baselineX,
minY: minY ?? this.minY,
maxY: maxY ?? this.maxY,
baselineY: baselineY ?? this.baselineY,
clipData: clipData ?? this.clipData,
backgroundColor: backgroundColor ?? this.backgroundColor,
rotationQuarterTurns: rotationQuarterTurns ?? this.rotationQuarterTurns,
);
/// Used for equality check, see [EquatableMixin].
@override
List<Object?> get props => [
lineBarsData,
betweenBarsData,
titlesData,
extraLinesData,
lineTouchData,
showingTooltipIndicators,
gridData,
borderData,
rangeAnnotations,
minX,
maxX,
baselineX,
minY,
maxY,
baselineY,
clipData,
backgroundColor,
rotationQuarterTurns,
];
}
enum LineChartGradientArea {
/// The gradient area will be around the line only, meaning
/// the gradient will exactly wrap around the curve.
rectAroundTheLine,
/// The entire chart area will be used as the gradient area for the curve.
wholeChart;
}
/// Holds data for drawing each individual line in the [LineChart]
class LineChartBarData with EquatableMixin {
/// [BarChart] draws some lines and overlaps them in the chart's view,
/// You can have multiple lines by splitting them,
/// put a [FlSpot.nullSpot] between each section.
/// each line passes through [spots], with hard edges by default,
/// [isCurved] makes it curve for drawing, and [curveSmoothness] determines the curve smoothness.
///
/// [show] determines the drawing, if set to false, it draws nothing.
///
/// [mainColors] determines the color of drawing line, if one color provided it applies a solid color,
/// otherwise it gradients between provided colors for drawing the line.
/// Gradient happens using provided [colorStops], [gradientFrom], [gradientTo].
/// if you want it draw normally, don't touch them,
/// check [LinearGradient] for understanding [colorStops]
///
/// [barWidth] determines the thickness of drawing line,
///
/// if [isCurved] is true, in some situations if the spots changes are in high values,
/// an overshooting will happen, we don't have any idea to solve this at the moment,
/// but you can set [preventCurveOverShooting] true, and update the threshold
/// using [preventCurveOvershootingThreshold] to achieve an acceptable curve,
/// check this [issue](https://github.com/imaNNeo/fl_chart/issues/25)
/// to overshooting understand the problem.
///
/// [isStrokeCapRound] determines the shape of line's cap.
///
/// [isStrokeJoinRound] determines the shape of the line joins.
///
/// [belowBarData], and [aboveBarData] used to fill the space below or above the drawn line,
/// you can fill with a solid color or a linear gradient.
///
/// [LineChart] draws points that the line is going through [spots],
/// you can customize it's appearance using [dotData].
///
/// there are some indicators with a line and bold point on each spot,
/// you can show them by filling [showingIndicators] with indices
/// you want to show indicator on them.
///
/// [LineChart] draws the lines with dashed effect if you fill [dashArray].
///
/// If you want to have a Step Line Chart style, just set [isStepLineChart] true,
/// also you can tweak the [LineChartBarData.lineChartStepData].
LineChartBarData({
this.spots = const [],
this.show = true,
Color? color,
this.gradient,
this.gradientArea = LineChartGradientArea.rectAroundTheLine,
this.barWidth = 2.0,
this.isCurved = false,
this.curveSmoothness = 0.35,
this.preventCurveOverShooting = false,
this.preventCurveOverShootingX = false,
this.preventCurveOverShootingY = false,
this.preventCurveOvershootingThreshold = 10.0,
this.isStrokeCapRound = false,
this.isStrokeJoinRound = false,
BarAreaData? belowBarData,
BarAreaData? aboveBarData,
this.dotData = const FlDotData(),
this.errorIndicatorData =
const FlErrorIndicatorData<LineChartSpotErrorRangeCallbackInput>(),
this.showingIndicators = const [],
this.dashArray,
this.shadow = const Shadow(color: Colors.transparent),
this.isStepLineChart = false,
this.lineChartStepData = const LineChartStepData(),
}) : color =
color ?? ((color == null && gradient == null) ? Colors.cyan : null),
belowBarData = belowBarData ?? BarAreaData(),
aboveBarData = aboveBarData ?? BarAreaData(),
assert(
!(preventCurveOverShooting &&
(preventCurveOverShootingX || preventCurveOverShootingY)),
'preventCurveOverShooting cannot be used together with preventCurveOverShootingX or preventCurveOverShootingY. '
'Use either preventCurveOverShooting for both axes, or the specific axis variants.',
) {
FlSpot? mostLeft;
FlSpot? mostTop;
FlSpot? mostRight;
FlSpot? mostBottom;
FlSpot? firstValidSpot;
try {
firstValidSpot =
spots.firstWhere((element) => element != FlSpot.nullSpot);
} catch (_) {
// There is no valid spot
}
if (firstValidSpot != null) {
for (final spot in spots) {
if (spot.isNull()) {
continue;
}
if (mostLeft == null || spot.x < mostLeft.x) {
mostLeft = spot;
}
if (mostRight == null || spot.x > mostRight.x) {
mostRight = spot;
}
if (mostTop == null || spot.y > mostTop.y) {
mostTop = spot;
}
if (mostBottom == null || spot.y < mostBottom.y) {
mostBottom = spot;
}
}
mostLeftSpot = mostLeft!;
mostTopSpot = mostTop!;
mostRightSpot = mostRight!;
mostBottomSpot = mostBottom!;
}
}
/// This line goes through this spots.
///
/// You can have multiple lines by splitting them,
/// put a [FlSpot.nullSpot] between each section.
final List<FlSpot> spots;
/// We keep the most left spot to prevent redundant calculations
late final FlSpot mostLeftSpot;
/// We keep the most top spot to prevent redundant calculations
late final FlSpot mostTopSpot;
/// We keep the most right spot to prevent redundant calculations
late final FlSpot mostRightSpot;
/// We keep the most bottom spot to prevent redundant calculations
late final FlSpot mostBottomSpot;
/// Determines to show or hide the line.
final bool show;
/// If provided, this [LineChartBarData] draws with this [color]
/// Otherwise we use [gradient] to draw the background.
/// It throws an exception if you provide both [color] and [gradient]
final Color? color;
/// If provided, this [LineChartBarData] draws with this [gradient].
/// Otherwise we use [color] to draw the background.
/// It throws an exception if you provide both [color] and [gradient]
final Gradient? gradient;
/// Only effective if [gradient] is provided.
///
/// It will be used to determine the area of the gradient.
final LineChartGradientArea gradientArea;
/// Determines thickness of drawing line.
final double barWidth;
/// If it's true, [LineChart] draws the line with curved edges,
/// otherwise it draws line with hard edges.
final bool isCurved;
/// If [isCurved] is true, it determines smoothness of the curved edges.
final double curveSmoothness;
/// Prevent overshooting when draw curve line with high value changes.
/// check this [issue](https://github.com/imaNNeo/fl_chart/issues/25)
final bool preventCurveOverShooting;
/// Prevent overshooting when draw curve line on the X-axis only.
/// When true, prevents control points from extending beyond data points horizontally,
/// while allowing natural Y-axis curve smoothing.
final bool preventCurveOverShootingX;
/// Prevent overshooting when draw curve line on the Y-axis only.
/// When true, prevents control points from extending beyond data points vertically,
/// while allowing natural X-axis curve smoothing.
final bool preventCurveOverShootingY;
/// Applies threshold for [preventCurveOverShooting] algorithm.
final double preventCurveOvershootingThreshold;
/// Determines the style of line's cap.
final bool isStrokeCapRound;
/// Determines the style of line joins.
final bool isStrokeJoinRound;
/// Fills the space blow the line, using a color or gradient.
final BarAreaData belowBarData;
/// Fills the space above the line, using a color or gradient.
final BarAreaData aboveBarData;
/// Responsible to showing [spots] on the line as a circular point.
final FlDotData dotData;
/// Holds data for showing error indicators on the spots in this line.
final FlErrorIndicatorData<LineChartSpotErrorRangeCallbackInput>
errorIndicatorData;
/// Show indicators based on provided indexes
final List<int> showingIndicators;
/// Determines the dash length and space respectively, fill it if you want to have dashed line.
final List<int>? dashArray;
/// Drops a shadow behind the bar line.
final Shadow shadow;
/// If sets true, it draws the chart in Step Line Chart style, using [LineChartBarData.lineChartStepData].
final bool isStepLineChart;
/// Holds data for representing a Step Line Chart, and works only if [isStepChart] is true.
final LineChartStepData lineChartStepData;
/// Lerps a [LineChartBarData] based on [t] value, check [Tween.lerp].
static LineChartBarData lerp(
LineChartBarData a,
LineChartBarData b,
double t,
) =>
LineChartBarData(
show: b.show,
barWidth: lerpDouble(a.barWidth, b.barWidth, t)!,
belowBarData: BarAreaData.lerp(a.belowBarData, b.belowBarData, t),
aboveBarData: BarAreaData.lerp(a.aboveBarData, b.aboveBarData, t),
curveSmoothness: b.curveSmoothness,
isCurved: b.isCurved,
isStrokeCapRound: b.isStrokeCapRound,
isStrokeJoinRound: b.isStrokeJoinRound,
preventCurveOverShooting: b.preventCurveOverShooting,
preventCurveOvershootingThreshold: lerpDouble(
a.preventCurveOvershootingThreshold,
b.preventCurveOvershootingThreshold,
t,
)!,
preventCurveOverShootingX: b.preventCurveOverShootingX,
preventCurveOverShootingY: b.preventCurveOverShootingY,
dotData: FlDotData.lerp(a.dotData, b.dotData, t),
errorIndicatorData: FlErrorIndicatorData.lerp(
a.errorIndicatorData,
b.errorIndicatorData,
t,
),
dashArray: lerpIntList(a.dashArray, b.dashArray, t),
color: Color.lerp(a.color, b.color, t),
gradient: Gradient.lerp(a.gradient, b.gradient, t),
gradientArea: b.gradientArea,
spots: lerpFlSpotList(a.spots, b.spots, t)!,
showingIndicators: b.showingIndicators,
shadow: Shadow.lerp(a.shadow, b.shadow, t)!,
isStepLineChart: b.isStepLineChart,
lineChartStepData:
LineChartStepData.lerp(a.lineChartStepData, b.lineChartStepData, t),
);
/// Copies current [LineChartBarData] to a new [LineChartBarData],
/// and replaces provided values.
LineChartBarData copyWith({
List<FlSpot>? spots,
bool? show,
Color? color,
Gradient? gradient,
LineChartGradientArea? gradientArea,
double? barWidth,
bool? isCurved,
double? curveSmoothness,
bool? preventCurveOverShooting,
double? preventCurveOvershootingThreshold,
bool? preventCurveOverShootingX,
bool? preventCurveOverShootingY,
bool? isStrokeCapRound,
bool? isStrokeJoinRound,
BarAreaData? belowBarData,
BarAreaData? aboveBarData,
FlDotData? dotData,
FlErrorIndicatorData<LineChartSpotErrorRangeCallbackInput>?
errorIndicatorData,
List<int>? dashArray,
List<int>? showingIndicators,
Shadow? shadow,
bool? isStepLineChart,
LineChartStepData? lineChartStepData,
}) =>
LineChartBarData(
spots: spots ?? this.spots,
show: show ?? this.show,
color: color ?? this.color,
gradient: gradient ?? this.gradient,
gradientArea: gradientArea ?? this.gradientArea,
barWidth: barWidth ?? this.barWidth,
isCurved: isCurved ?? this.isCurved,
curveSmoothness: curveSmoothness ?? this.curveSmoothness,
preventCurveOverShooting:
preventCurveOverShooting ?? this.preventCurveOverShooting,
preventCurveOvershootingThreshold: preventCurveOvershootingThreshold ??
this.preventCurveOvershootingThreshold,
preventCurveOverShootingX:
preventCurveOverShootingX ?? this.preventCurveOverShootingX,
preventCurveOverShootingY:
preventCurveOverShootingY ?? this.preventCurveOverShootingY,
isStrokeCapRound: isStrokeCapRound ?? this.isStrokeCapRound,
isStrokeJoinRound: isStrokeJoinRound ?? this.isStrokeJoinRound,
belowBarData: belowBarData ?? this.belowBarData,
aboveBarData: aboveBarData ?? this.aboveBarData,
dashArray: dashArray ?? this.dashArray,
dotData: dotData ?? this.dotData,
errorIndicatorData: errorIndicatorData ?? this.errorIndicatorData,
showingIndicators: showingIndicators ?? this.showingIndicators,
shadow: shadow ?? this.shadow,
isStepLineChart: isStepLineChart ?? this.isStepLineChart,
lineChartStepData: lineChartStepData ?? this.lineChartStepData,
);
/// Used for equality check, see [EquatableMixin].
@override
List<Object?> get props => [
spots,
show,
color,
gradient,
gradientArea,
barWidth,
isCurved,
curveSmoothness,
preventCurveOverShooting,
preventCurveOvershootingThreshold,
preventCurveOverShootingX,
preventCurveOverShootingY,
isStrokeCapRound,
isStrokeJoinRound,
belowBarData,
aboveBarData,
dotData,
errorIndicatorData,
showingIndicators,
dashArray,
shadow,
isStepLineChart,
lineChartStepData,
];
}
/// Holds data for representing a Step Line Chart, and works only if [LineChartBarData.isStepChart] is true.
class LineChartStepData with EquatableMixin {
/// Determines the [stepDirection] of each step;
const LineChartStepData({this.stepDirection = stepDirectionMiddle});
/// Go to the next spot directly, with the current point's y value.
static const stepDirectionForward = 0.0;
/// Go to the half with the current spot y, and with the next spot y for the rest.
static const stepDirectionMiddle = 0.5;
/// Go to the next spot y and direct line to the next spot.
static const stepDirectionBackward = 1.0;
/// Determines the direction of each step;
final double stepDirection;
/// Lerps a [LineChartStepData] based on [t] value, check [Tween.lerp].
static LineChartStepData lerp(
LineChartStepData a,
LineChartStepData b,
double t,
) =>
LineChartStepData(
stepDirection: lerpDouble(a.stepDirection, b.stepDirection, t)!,
);
/// Used for equality check, see [EquatableMixin].
@override
List<Object?> get props => [stepDirection];
}
/// Holds data for filling an area (above or below) of the line with a color or gradient.
class BarAreaData with EquatableMixin {
/// if [show] is true, [LineChart] fills above and below area of each line
/// with a color or gradient.
///
/// [color] determines the color of above or below space area,
/// if one color provided it applies a solid color,
/// otherwise it gradients between provided colors for drawing the line.
/// Gradient happens using provided [gradientColorStops], [gradientFrom], [gradientTo].
/// if you want it draw normally, don't touch them,
/// check [LinearGradient] for understanding [gradientColorStops]
///
/// If [spotsLine] is provided, it draws some lines from each spot
/// to the bottom or top of the chart.
///
/// If [applyCutOffY] is true, it cuts the drawing by the [cutOffY] line.
BarAreaData({
this.show = false,
Color? color,
this.gradient,
this.spotsLine = const BarAreaSpotsLine(),
this.cutOffY = 0,
this.applyCutOffY = false,
}) : color = color ??
((color == null && gradient == null)
? Colors.blueGrey.withValues(alpha: 0.5)
: null);
final bool show;
/// If provided, this [BarAreaData] draws with this [color]
/// Otherwise we use [gradient] to draw the background.
/// It throws an exception if you provide both [color] and [gradient]
final Color? color;
/// If provided, this [BarAreaData] draws with this [gradient].
/// Otherwise we use [color] to draw the background.
/// It throws an exception if you provide both [color] and [gradient]
final Gradient? gradient;
/// holds data for drawing a line from each spot the the bottom, or top of the chart
final BarAreaSpotsLine spotsLine;
/// cut the drawing below or above area to this y value
final double cutOffY;
/// determines should or shouldn't apply cutOffY
final bool applyCutOffY;
/// Lerps a [BarAreaData] based on [t] value, check [Tween.lerp].
static BarAreaData lerp(BarAreaData a, BarAreaData b, double t) =>
BarAreaData(
show: b.show,
spotsLine: BarAreaSpotsLine.lerp(a.spotsLine, b.spotsLine, t),
color: Color.lerp(a.color, b.color, t),
gradient: Gradient.lerp(a.gradient, b.gradient, t),
cutOffY: lerpDouble(a.cutOffY, b.cutOffY, t)!,
applyCutOffY: b.applyCutOffY,
);
/// Used for equality check, see [EquatableMixin].
@override
List<Object?> get props => [
show,
color,
gradient,
spotsLine,
cutOffY,
applyCutOffY,
];
}
/// Holds data about filling below or above space of the bar line,
class BetweenBarsData with EquatableMixin {
BetweenBarsData({
required this.fromIndex,
required this.toIndex,
Color? color,
this.gradient,
}) : color = color ??
((color == null && gradient == null)
? Colors.blueGrey.withValues(alpha: 0.5)
: null);
/// The index of the lineBarsData from where the area has to be rendered
final int fromIndex;
/// The index of the lineBarsData until where the area has to be rendered
final int toIndex;
/// If provided, this [BetweenBarsData] draws with this [color]
/// Otherwise we use [gradient] to draw the background.
/// It throws an exception if you provide both [color] and [gradient]
final Color? color;
/// If provided, this [BetweenBarsData] draws with this [gradient].
/// Otherwise we use [color] to draw the background.
/// It throws an exception if you provide both [color] and [gradient]
final Gradient? gradient;
/// Lerps a [BetweenBarsData] based on [t] value, check [Tween.lerp].
static BetweenBarsData lerp(BetweenBarsData a, BetweenBarsData b, double t) {
return BetweenBarsData(
fromIndex: b.fromIndex,
toIndex: b.toIndex,
color: Color.lerp(a.color, b.color, t),
gradient: Gradient.lerp(a.gradient, b.gradient, t),
);
}
/// Used for equality check, see [EquatableMixin].
@override
List<Object?> get props => [
fromIndex,
toIndex,
color,
gradient,
];
}
/// Holds data for drawing line on the spots under the [BarAreaData].
class BarAreaSpotsLine with EquatableMixin {
/// If [show] is true, [LineChart] draws some lines on above or below the spots,
/// you can customize the appearance of the lines using [flLineStyle]
/// and you can decide to show or hide the lines on each spot using [checkToShowSpotLine].
const BarAreaSpotsLine({
this.show = false,
this.flLineStyle = const FlLine(),
this.checkToShowSpotLine = showAllSpotsBelowLine,
this.applyCutOffY = true,
});
/// Determines to show or hide all the lines.
final bool show;
/// Holds appearance of drawing line on the spots.
final FlLine flLineStyle;
/// Checks to show or hide lines on the spots.
final CheckToShowSpotLine checkToShowSpotLine;
/// Determines to inherit the cutOff properties from its parent [BarAreaData]
final bool applyCutOffY;
/// Lerps a [BarAreaSpotsLine] based on [t] value, check [Tween.lerp].
static BarAreaSpotsLine lerp(
BarAreaSpotsLine a,
BarAreaSpotsLine b,
double t,
) =>
BarAreaSpotsLine(
show: b.show,
checkToShowSpotLine: b.checkToShowSpotLine,
flLineStyle: FlLine.lerp(a.flLineStyle, b.flLineStyle, t),
applyCutOffY: b.applyCutOffY,
);
/// Used for equality check, see [EquatableMixin].
@override
List<Object?> get props => [
show,
flLineStyle,
checkToShowSpotLine,
applyCutOffY,
];
}
/// It used for determine showing or hiding [BarAreaSpotsLine]s
///
/// Gives you the checking spot, and you have to decide to
/// show or not show the line on the provided spot.
typedef CheckToShowSpotLine = bool Function(FlSpot spot);
/// Shows all spot lines.
bool showAllSpotsBelowLine(FlSpot spot) => true;
/// The callback passed to get the color of a [FlSpot]
///
/// The callback receives [FlSpot], which is the target spot,
/// [double] is the percentage of spot along the bar line,
/// [LineChartBarData] is the chart's bar.
/// It should return a [Color] that needs to be used for drawing target.
typedef GetDotColorCallback = Color Function(FlSpot, double, LineChartBarData);
/// If there is one color in [LineChartBarData.mainColors], it returns that color,
/// otherwise it returns the color along the gradient colors based on the [xPercentage].
Color _defaultGetDotColor(FlSpot _, double xPercentage, LineChartBarData bar) {
if (bar.gradient != null && bar.gradient is LinearGradient) {
return lerpGradient(
bar.gradient!.colors,
bar.gradient!.getSafeColorStops(),
xPercentage / 100,
);
}
return bar.gradient?.colors.first ?? bar.color ?? Colors.blueGrey;
}
/// If there is one color in [LineChartBarData.mainColors], it returns that color in a darker mode,
/// otherwise it returns the color along the gradient colors based on the [xPercentage] in a darker mode.
Color _defaultGetDotStrokeColor(
FlSpot spot,
double xPercentage,
LineChartBarData bar,
) {
Color color;
if (bar.gradient != null && bar.gradient is LinearGradient) {
color = lerpGradient(
bar.gradient!.colors,
bar.gradient!.getSafeColorStops(),
xPercentage / 100,
);
} else {
color = bar.gradient?.colors.first ?? bar.color ?? Colors.blueGrey;
}
return color.darken();
}
/// The callback passed to get the painter of a [FlSpot]
///
/// The callback receives [FlSpot], which is the target spot,
/// [LineChartBarData] is the chart's bar.
/// [int] is the index position of the spot.
/// It should return a [FlDotPainter] that needs to be used for drawing target.
typedef GetDotPainterCallback = FlDotPainter Function(
FlSpot,
double,
LineChartBarData,
int,
);
FlDotPainter _defaultGetDotPainter(
FlSpot spot,
double xPercentage,
LineChartBarData bar,
int index, {
double? size,
}) =>
FlDotCirclePainter(
radius: size,
color: _defaultGetDotColor(spot, xPercentage, bar),
strokeColor: _defaultGetDotStrokeColor(spot, xPercentage, bar),
);
/// This class holds data about drawing spot dots on the drawing bar line.
class FlDotData with EquatableMixin {
/// set [show] false to prevent dots from drawing,
/// if you want to show or hide dots in some spots,
/// override [checkToShowDot] to handle it in your way.
const FlDotData({
this.show = true,
this.checkToShowDot = showAllDots,
this.getDotPainter = _defaultGetDotPainter,
});
/// Determines show or hide all dots.
final bool show;
/// Checks to show or hide an individual dot.
final CheckToShowDot checkToShowDot;
/// Callback which is called to set the painter of the given [FlSpot].
/// The [FlSpot] is provided as parameter to this callback
final GetDotPainterCallback getDotPainter;
/// Lerps a [FlDotData] based on [t] value, check [Tween.lerp].
static FlDotData lerp(FlDotData a, FlDotData b, double t) => FlDotData(
show: b.show,
checkToShowDot: b.checkToShowDot,
getDotPainter: b.getDotPainter,
);
/// Used for equality check, see [EquatableMixin].
@override
List<Object?> get props => [
show,
checkToShowDot,
getDotPainter,
];
}
/// It determines showing or hiding [FlDotData] on the spots.
///
/// It gives you the checking [FlSpot] and you should decide to
/// show or hide the dot on this spot by returning true or false.
typedef CheckToShowDot = bool Function(FlSpot spot, LineChartBarData barData);
/// Shows all dots on spots.
bool showAllDots(FlSpot spot, LineChartBarData barData) => true;
enum LabelDirection { horizontal, vertical }
/// Shows a text label
abstract class FlLineLabel with EquatableMixin {
/// Draws a title on the line, align it with [alignment] over the line,
/// applies [padding] for spaces, and applies [style] for changing color,
/// size, ... of the text.
/// [show] determines showing label or not.
/// [direction] determines if the direction of the text should be horizontal or vertical.
const FlLineLabel({
required this.show,
required this.padding,
required this.style,
required this.alignment,
required this.direction,
});
/// Determines showing label or not.
final bool show;
/// Inner spaces around the drawing text.
final EdgeInsetsGeometry padding;
/// Sets style of the drawing text.
final TextStyle? style;
/// Aligns the text on the line.
final Alignment alignment;
/// Determines the direction of the text.
final LabelDirection direction;
/// Used for equality check, see [EquatableMixin].
@override
List<Object?> get props => [
show,
padding,
style,
alignment,
direction,
];
}
/// Holds data to handle touch events, and touch responses in the [LineChart].
///
/// There is a touch flow, explained [here](https://github.com/imaNNeo/fl_chart/blob/main/repo_files/documentations/handle_touches.md)
/// in a simple way, each chart's renderer captures the touch events, and passes the pointerEvent
/// to the painter, and gets touched spot, and wraps it into a concrete [LineTouchResponse].
class LineTouchData extends FlTouchData<LineTouchResponse> with EquatableMixin {
/// You can disable or enable the touch system using [enabled] flag,
///
/// [touchCallback] notifies you about the happened touch/pointer events.
/// It gives you a [FlTouchEvent] which is the happened event such as [FlPointerHoverEvent], [FlTapUpEvent], ...
/// It also gives you a [LineTouchResponse] which contains information
/// about the elements that has touched.
///
/// Using [mouseCursorResolver] you can change the mouse cursor
/// based on the provided [FlTouchEvent] and [LineTouchResponse]
///
/// if [handleBuiltInTouches] is true, [LineChart] shows a tooltip popup on top of the spots if
/// touch occurs (or you can show it manually using, [LineChartData.showingTooltipIndicators])
/// and also it shows an indicator (contains a thicker line and larger dot on the targeted spot),
/// You can define how this indicator looks like through [getTouchedSpotIndicator] callback,
/// You can customize this tooltip using [touchTooltipData], indicator lines starts from position
/// controlled by [getTouchLineStart] and ends at position controlled by [getTouchLineEnd].
/// If you need to have a distance threshold for handling touches, use [touchSpotThreshold].
const LineTouchData({
bool enabled = true,
BaseTouchCallback<LineTouchResponse>? touchCallback,
MouseCursorResolver<LineTouchResponse>? mouseCursorResolver,
Duration? longPressDuration,
this.touchTooltipData = const LineTouchTooltipData(),
this.getTouchedSpotIndicator = defaultTouchedIndicators,
this.touchSpotThreshold = 10,
this.distanceCalculator = _xDistance,
this.handleBuiltInTouches = true,
this.getTouchLineStart = defaultGetTouchLineStart,
this.getTouchLineEnd = defaultGetTouchLineEnd,
}) : super(
enabled,
touchCallback,
mouseCursorResolver,
longPressDuration,
);
/// Configs of how touch tooltip popup.
final LineTouchTooltipData touchTooltipData;
/// Configs of how touch indicator looks like.
final GetTouchedSpotIndicator getTouchedSpotIndicator;
/// Distance threshold to handle the touch event.
final double touchSpotThreshold;
/// Distance function used when finding closest points to touch point
final CalculateTouchDistance distanceCalculator;
/// Determines to handle default built-in touch responses,
/// [LineTouchResponse] shows a tooltip popup above the touched spot.
final bool handleBuiltInTouches;
/// The starting point on y axis of the touch line. By default, line starts on the bottom of
/// the chart.
final GetTouchLineY getTouchLineStart;
/// The end point on y axis of the touch line. By default, line ends at the touched point.
/// If line end is overlap with the dot, it will be automatically adjusted to the edge of the dot.
final GetTouchLineY getTouchLineEnd;
/// Copies current [LineTouchData] to a new [LineTouchData],
/// and replaces provided values.
LineTouchData copyWith({
bool? enabled,
BaseTouchCallback<LineTouchResponse>? touchCallback,
MouseCursorResolver<LineTouchResponse>? mouseCursorResolver,
Duration? longPressDuration,
LineTouchTooltipData? touchTooltipData,
GetTouchedSpotIndicator? getTouchedSpotIndicator,
double? touchSpotThreshold,
CalculateTouchDistance? distanceCalculator,
GetTouchLineY? getTouchLineStart,
GetTouchLineY? getTouchLineEnd,
bool? handleBuiltInTouches,
}) =>
LineTouchData(
enabled: enabled ?? this.enabled,
touchCallback: touchCallback ?? this.touchCallback,
mouseCursorResolver: mouseCursorResolver ?? this.mouseCursorResolver,
longPressDuration: longPressDuration ?? this.longPressDuration,
touchTooltipData: touchTooltipData ?? this.touchTooltipData,
getTouchedSpotIndicator:
getTouchedSpotIndicator ?? this.getTouchedSpotIndicator,
touchSpotThreshold: touchSpotThreshold ?? this.touchSpotThreshold,
distanceCalculator: distanceCalculator ?? this.distanceCalculator,
getTouchLineStart: getTouchLineStart ?? this.getTouchLineStart,
getTouchLineEnd: getTouchLineEnd ?? this.getTouchLineEnd,
handleBuiltInTouches: handleBuiltInTouches ?? this.handleBuiltInTouches,
);
/// Used for equality check, see [EquatableMixin].
@override