GithubHelp home page GithubHelp logo

Comments (12)

Vingtoft avatar Vingtoft commented on May 12, 2024 4

How can I use null values?
If null is parsed as Y value, the following exception is thrown:

Unhandled Exception: type 'Null' is not a subtype of type 'double'

My use case requires valid X values with null Y values

from fl_chart.

shamilovtim avatar shamilovtim commented on May 12, 2024 3

Definitely but I think it conveys the proper feeling from the business logic side. The users didn't chart something that they were supposed to do, which caused the problem.

We could offer settings to interpolate with a dashed line and/or a pure null option:

Pure null
Screen Shot 2020-01-10 at 2 29 42 PM

Interpolated with a dashed line:
Screen Shot 2020-01-10 at 2 30 14 PM

I would be happy to help with this feature if we go with flutter for our project

from fl_chart.

shamilovtim avatar shamilovtim commented on May 12, 2024 2

@yongjhih nulls should be in the next release

from fl_chart.

shamilovtim avatar shamilovtim commented on May 12, 2024 1

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:

  1. https://google.github.io/charts/flutter/example/line_charts/simple_nulls
  2. https://formidable.com/open-source/victory/gallery/victory-line-with-null-data/

from fl_chart.

yongjhih avatar yongjhih commented on May 12, 2024 1

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.

from fl_chart.

imaNNeo avatar imaNNeo commented on May 12, 2024

You can have multiple bars on the LineChart, then make two separted and combine them in your chart,

from fl_chart.

imaNNeo avatar imaNNeo commented on May 12, 2024

check LineBarsData , you can have multiple LineChartBarData

from fl_chart.

stx avatar stx commented on May 12, 2024

Got it! Thank you!

from fl_chart.

imaNNeo avatar imaNNeo commented on May 12, 2024

Okay fine, we need this in the line chart, but It is weird when we have rounded line chart

from fl_chart.

imaNNeo avatar imaNNeo commented on May 12, 2024

Nice, we will consider it.
Thanks for reporting!

from fl_chart.

imaNNeo avatar imaNNeo commented on May 12, 2024

Implemented in 0.8.6, check it out!

from fl_chart.

cryosx avatar cryosx commented on May 12, 2024

How can I use null values? If null is parsed as Y value, the following exception is thrown:

Unhandled Exception: type 'Null' is not a subtype of type 'double'

My use case requires valid X values with null Y values

FlSpot.nullSpot

from fl_chart.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.