imaNNeo / fl_chart

FL Chart is a highly customizable Flutter chart library that supports Line Chart, Bar Chart, Pie Chart, Scatter Chart, and Radar Chart.
https://flchart.dev
MIT License
6.87k stars 1.78k forks source link

line chart shows negative y-axis, but there are no negative numbers #1593

Open williamluotao opened 9 months ago

williamluotao commented 9 months ago

** Don't make a duplicate issue. You can search in issues to make sure there isn't any already opened issue with your concern.

Describe the bug A clear and concise description of what the bug is.

To Reproduce Provide us a completely reproducible code (contains the main function) in a main.dart file, it helps us to find the bug immediately.

Screen Shot 2024-02-22 at 19 05 11

Screenshots If applicable, add screenshots, or videoshots to help explain your problem.

Versions

harshpjoshi commented 8 months ago

@williamluotao I am able to fixing this issue by adding some Path logic. Here is the PR:1594 @imaNNeo Please check if it is a valid fix for same. Thanks

imaNNeo commented 8 months ago

There is a solution at the moment, you can set preventCurveOverShooting: true in your LineChartBarData. But I will take a look at the @harshpjoshi's solution. Please provide me a reproducible code.

williamluotao commented 8 months ago

There is a solution at the moment, you can set preventCurveOverShooting: true in your LineChartBarData. But I will take a look at the @harshpjoshi's solution. Please provide me a reproducible code.

LineChartData _createTimeSequenceData(List dt_list) { print("dt_list ${dt_list}"); List spots = []; List showingTooltipOnSpots = [];

  List<Map<String, dynamic>> b = List.generate(
    24,
        (index) => {"recHour": index, "times": 0},
  );
  List<double> stops = [];

  for (ChartSequence cs in dt_list) {
    showingTooltipOnSpots.add(cs.times);
    stops.add((cs.times).toDouble());

    var recHour = cs.recHour;
    var times = cs.times;
    var index = b.indexWhere((element) =>
    element["recHour"] ==
        int.parse(recHour.replaceAll(RegExp(r'[^0-9]'), '')));

    if (index != -1) {
      b[index]["times"] = times;
    }
  }
  //print(b);

  for (var entry in b) {
    spots.add(
        FlSpot(entry["recHour"].toDouble(), entry["times"].toDouble()));
  }
  //print(spots);

  Widget bottomTitleWidgets(double value, TitleMeta meta,
      double chartWidth) {
    final style = TextStyle(
      fontWeight: FontWeight.bold,
      color: Colors.blue,
      fontFamily: 'Digital',
      fontSize: 18 * chartWidth / 500,
    );
    String text;
    switch (value.toInt()) {
      case 0:
        text = '00:00';
        break;
      case 1:
        text = '';
        break;
      case 2:
        text = '';
        break;
      case 3:
        text = '';
        break;
      case 4:
        text = '04:00';
        break;
      case 5:
        text = '';
        break;
      case 6:
        text = '';
        break;
      case 7:
        text = '';
        break;
      case 8:
        text = '08:00';
        break;
      case 9:
        text = '';
        break;
      case 10:
        text = '';
        break;
      case 11:
        text = '';
        break;
      case 12:
        text = '12:00';
        break;
      case 13:
        text = '';
        break;
      case 14:
        text = '';
        break;
      case 15:
        text = '';
        break;
      case 16:
        text = '16:00';
        break;
      case 17:
        text = '';
        break;
      case 18:
        text = '';
        break;
      case 19:
        text = '';
        break;
      case 20:
        text = '20:00';
        break;
      case 21:
        text = '';
        break;
      case 22:
        text = '';
        break;
      case 23:
        text = '23:59';
        break;
      default:
        return Container();
    }

    return SideTitleWidget(
      axisSide: meta.axisSide,
      child: Text(text, style: style),
    );
  }

  final Color gradientColor1 = Colors.blue;
  final Color gradientColor2 = Colors.pink;
  final Color gradientColor3 = Colors.red;
  final Color indicatorStrokeColor = Colors.white;

  final lineBarsData = [
    LineChartBarData(
      preventCurveOverShooting: true,
      showingIndicators: showingTooltipOnSpots,
      spots: spots,
      isCurved: true,
      barWidth: 4,
      shadow: const Shadow(
        blurRadius: 8,
      ),
      belowBarData: BarAreaData(
        show: true,
        gradient: LinearGradient(
          colors: [
            gradientColor1.withOpacity(0.4),
            gradientColor2.withOpacity(0.4),
            gradientColor3.withOpacity(0.4),
          ],
        ),
      ),
      dotData: const FlDotData(show: false),
      gradient: LinearGradient(
        colors: [
          gradientColor1,
          gradientColor2,
          gradientColor3,
        ],
        //stops: stops,
      ),
    ),
  ];

  final tooltipsOnBar = lineBarsData[0];

  // Customize other properties as needed
  return LineChartData(
      showingTooltipIndicators: showingTooltipOnSpots.map((index) {
        return ShowingTooltipIndicators([
          LineBarSpot(
            tooltipsOnBar,
            lineBarsData.indexOf(tooltipsOnBar),
            tooltipsOnBar.spots[index],
          ),
        ]);
      }).toList(),
      lineTouchData: LineTouchData(
        enabled: true,
        touchTooltipData: LineTouchTooltipData(
          tooltipBgColor: Colors.transparent, // Change this color as needed
            getTooltipItems: (List<LineBarSpot> lineBarsSpot) {
              return lineBarsSpot.map((LineBarSpot spot) {
                //print("dt_list ${dt_list}");
                //print("spot.spotIndex ${spot.spotIndex}");
                final int spotIndex = spot.spotIndex;
                if (spotIndex >= 0 && spotIndex < b.length) {
                  final Map<String, dynamic> entry = b[spotIndex];
                  return LineTooltipItem(
                    '${entry["recHour"]}:00(${entry["times"]})',
                    TextStyle(color: Colors.blue, fontWeight: FontWeight.bold),
                  );
                } else {
                  return LineTooltipItem('Invalid Index', TextStyle(color: Colors.red));
                }
              }).toList();
            },
        ),
        handleBuiltInTouches: true,
      ),
      lineBarsData: [
        LineChartBarData(
          spots: spots,
          isCurved: true,
          belowBarData: BarAreaData(show: false),
          color: Colors.blue,
          dotData: FlDotData(show: true),
          // Add other customization as needed
        ),
      ],
      titlesData: FlTitlesData(
        rightTitles: const AxisTitles(
          sideTitles: SideTitles(
            showTitles: false,
            reservedSize: 0,
          ),
        ),
        topTitles: const AxisTitles(
          sideTitles: SideTitles(
            showTitles: false,
            reservedSize: 0,
          ),
        ),
        leftTitles: AxisTitles(),
        bottomTitles: AxisTitles(
          sideTitles: SideTitles(
            showTitles: true,
            interval: 1,
            getTitlesWidget: (value, meta) {
              return bottomTitleWidgets(
                value,
                meta,
                MediaQuery
                    .of(context)
                    .size
                    .width,
              );
            },
            reservedSize: 30,
          ),
        ),
        // Customize titles if needed
      ),
      gridData: FlGridData(show: false),
      borderData: FlBorderData(
        show: false,
      ) // Add other customization as needed
  );
}

The test data is dt_list [chartSequence{ recHour : 13, times : 2}]

Screen Shot 2024-02-29 at 14 16 52
imaNNeo commented 7 months ago

There is a solution at the moment, you can set preventCurveOverShooting: true in your LineChartBarData. But I will take a look at the @harshpjoshi's solution. Please provide me a reproducible code.

LineChartData _createTimeSequenceData(List dt_list) { print("dt_list ${dt_list}"); List spots = []; List showingTooltipOnSpots = [];


  List<Map<String, dynamic>> b = List.generate(

You're reproducible code is not well-formatted and also it is not complete. Please give me a main.dart file content to allow me to run it directly.