-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Ability to show gaps in data? #26
Comments
You can have multiple bars on the LineChart, then make two separted and combine them in your chart, |
check LineBarsData , you can have multiple LineChartBarData |
Got it! Thank you! |
Hello @imaNNeoFighT, Thanks for the library! It's great so far. Is it possible to get real nullable coordinates added without having to create multiple instances of lines? This is a standard feature of a lot of different charting libraries. In almost all of them the best practice is: { x: 5, y: null },
{ x: 6, y: null } See: |
Okay fine, we need this in the line chart, but It is weird when we have rounded line chart |
Nice, we will consider it. |
That's what I did for null spots: Skip null spots: () {
return LineChart(LineChartData(
lineBarsData: LineChartBarData(/* ... */).listWith(spots.splitByNull()),
minX: spots.map((it) => it.x).min(), // optional: Avoid trimming to keep the original range of x-axis
maxX: spots.map((it) => it.x).max(), // optional: Avoid trimming to keep the original range of x-axis
// ...
));
}
extension LineChartBarDataX<T extends LineChartBarData> on T {
/// Skip spots while [spots] contain null y
List<T> listWith(Iterable<List<FlSpot>> lists) {
final data = this;
return lists.map((spots) => data.copyWith(spots: spots)).toList();
}
}
extension IterableFlSpotX<T extends FlSpot> on Iterable<T> {
Iterable<List<T>> splitByNull() => splitBy((it) => it.y == null);
}
/// ref. https://github.com/yongjhih/dartx/blob/77ef87a/lib/src/iterable.dart#L1012
extension IterableX<T> on Iterable<T> {
Iterable<List<E>> splitBy(bool test(E it)) {
final lists = fold<List<List<E>>>([[]], (that, it) {
if (!test(it)) {
that.last.add(it);
} else {
if (that.last.isNotEmpty) {
that.add([]);
}
}
return that;
});
return lists.where((it) => it.isNotEmpty);
}
} Interpolate with the dashes for null spots: () {
return LineChart(LineChartData(
lineBarsData: LineChartBarData(/* ... */).listDashableWith(spots),
minX: spots.map((it) => it.x).min(), // optional: Avoid trimming to keep the original range of x-axis
maxX: spots.map((it) => it.x).max(), // optional: Avoid trimming to keep the original range of x-axis
// ...
));
}
extension LineChartBarDataX<T extends LineChartBarData> on T {
/// Skip spots while [spots] contain null y
List<T> listWith(Iterable<List<FlSpot>> lists) {
final data = this;
return lists.map((spots) => data.copyWith(spots: spots)).toList();
}
/// Return simple dashed [T]
T dashed({
List<int> dashArray = const [1, 2],
double opacity = 0.5,
}) {
return copyWith(
dashArray: dashArray,
belowBarData: belowBarData?.copyWith(
colors: belowBarData?.colors?.map((it) => it. opacityFactor(opacity))?.toList(),
));
}
/// Dashed [spots] while which contain null y
List<T> listDashableWith(Iterable<FlSpot> spots, {
List<int> dashArray = const [1, 2],
double opacity = 0.5,
}) {
final repeatedSpots = spots.repeatByNull();
final _dashed = dashed(dashArray: dashArray, opacity: opacity);
if (repeatedSpots.isNotEmpty) {
return repeatedSpots.map((it) => it.first.dashed ? _dashed.copyWith(spots: it) : copyWith(spots: it)).toList();
} else {
final values = spots.map((it) => it.x);
return <T>[
_dashed.copyWith(spots: <FlSpot>[
FlSpot(values.min() ?? 0, 0),
FlSpot(values.max() ?? 0, 0),
])
];
}
}
}
extension IterableFlSpotX<T extends FlSpot> on Iterable<T> {
Iterable<List<T>> splitByNull() => splitBy((it) => it.y == null);
}
extension IterableFlSpotsX on Iterable<FlSpot> {
Iterable<List<FlSpotDashable>> repeatByNull() =>
map((it) => FlSpotDashable(it.x, it.y))
.repeatBy((it) => it.y == null, (that, it) => FlSpotDashable(that.x, that.y, dashed: true))
.where((it) => it.isNotEmpty);
}
class FlSpotDashable extends FlSpot {
const FlSpotDashable(double x, double y, {this.dashed = false}) : super(x, y);
final bool dashed;
@override
FlSpotDashable copyWith({
double x,
double y,
bool dashed = false
}) {
return FlSpotDashable(
x ?? this.x,
y ?? this.y,
dashed: dashed ?? this.dashed,
);
}
@override
String toString() {
return "{x: ${x}, y: ${y}, dashed: $dashed}";
}
}
extension BarAreaDataX<T extends BarAreaData> on T {
BarAreaData copyWith({
bool show,
List<Color> colors,
Offset gradientFrom,
Offset gradientTo,
List<double> gradientColorStops,
BarAreaSpotsLine spotsLine,
double cutOffY,
bool applyCutOffY,
}) => BarAreaData(
show: show ?? this.show ?? false,
colors: colors ?? this.colors ?? const [Colors.blueGrey],
gradientFrom: gradientFrom ?? this.gradientFrom ?? const Offset(0, 0),
gradientTo: gradientTo ?? this.gradientTo ?? const Offset(1, 0),
gradientColorStops: gradientColorStops ?? this.gradientColorStops,
spotsLine: spotsLine ?? this.spotsLine ?? const BarAreaSpotsLine(),
cutOffY: cutOffY ?? this.cutOffY,
applyCutOffY: applyCutOffY ?? this.applyCutOffY ?? false,
);
}
/// ref. https://github.com/yongjhih/dartx/blob/77ef87a/lib/src/iterable.dart#L1012
extension IterableX<T> on Iterable<T> {
Iterable<List<E>> splitBy(bool test(E it)) {
final lists = fold<List<List<E>>>([[]], (that, it) {
if (!test(it)) {
that.last.add(it);
} else {
if (that.last.isNotEmpty) {
that.add([]);
}
}
return that;
});
return lists.where((it) => it.isNotEmpty);
}
Iterable<List<T>> repeatBy(bool test(T it), T repeat(T repeater, T element)) {
if (isEmpty) {
return [[]];
}
final lists = skip(1).fold<List<List<T>>>([[first]], (that, it) {
if (test(it)) { // it == null
// repeating for current
if (test(that.last.last)) { // it == null && last == null
// skip
} else { // it == null && last != null
// repeat by repeater
that.add([repeat(that.last.last, it), it]);
}
} else { // it != null
if (test(that.last.last)) { // it != null && last == null
that.last.last = repeat(it, that.last.last);
that.add([it]);
} else { // it != null && last != null
that.last.add(it);
}
}
return that;
});
if (test(lists.last.last)) { // it != null && last == null
final repeater = lists.last.lastOrNullWhere((it) => !test(it));
if (repeater != null) {
lists.last.last = repeat(repeater, lists.last.last);
}
}
return lists.where((it) => it.every((that) => !test(that)));
}
}
extension ColorX<T extends Color> on T {
Color opacityFactor(double factor) =>
withOpacity(opacity * factor);
} I'm still looking for this project will support the nullable spots by itself. |
@yongjhih nulls should be in the next release |
Implemented in 0.8.6, check it out! |
How can I use null values?
My use case requires valid X values with null Y values |
FlSpot.nullSpot |
Again, thanks for this plugin. You're doing an awesome job!
Is it possible to show gaps or null data? I've played around and haven't found how one might do that.
Visually, something along these lines:
Implementation wise, this might look like:
Thanks again!
The text was updated successfully, but these errors were encountered: