Skip to content
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

top and bottom horizontalLines in ExtraLinesData not rendering since ^0.60.0 #1255

Closed
mzdm opened this issue Feb 5, 2023 · 7 comments · Fixed by #1266
Closed

top and bottom horizontalLines in ExtraLinesData not rendering since ^0.60.0 #1255

mzdm opened this issue Feb 5, 2023 · 7 comments · Fixed by #1266
Labels

Comments

@mzdm
Copy link

mzdm commented Feb 5, 2023

Hello,
when I upgraded to ^0.60.0 from ^0.55.2 top and bottom lines are not visible.

ExtraLinesData(
        extraLinesOnTop: false,
        horizontalLines: Mood.values
            .mapIndexed(
              (i, _) => HorizontalLine(
                y: i.toDouble(),
                color: NepanikarColors.primary.withOpacity(0.2),
                strokeWidth: 1,
                dashArray: [5],
              ),
            )
            .toList(),
      )

Expected / before:

Current:

Full code of chart since the code is open sourced: https:/cesko-digital/nepanikar/blob/main/lib/widgets/mood/mood_chart.dart

@imaNNeo
Copy link
Owner

imaNNeo commented Feb 8, 2023

Can you please provide me a reproducible code?

@mr-follow
Copy link

import 'dart:math';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';

const double barWidth = 5;

final timeStamps_1 = [
  "2023-02-07T01:00:00+00:00",
  "2023-02-08T01:00:00+00:00",
  "2023-02-09T01:00:00+00:00",
  "2023-02-10T01:00:00+00:00",
  "2023-02-10T17:44:21+00:00"
];

final equity_1 = <double>[25.0901, 25.593, 24.8272, 24.4687, 149.0118];

final timeStamps_2 = [
  "2023-02-07T01:00:00+00:00",
  "2023-02-08T01:00:00+00:00",
  "2023-02-09T01:00:00+00:00",
  "2023-02-10T01:00:00+00:00",
  "2023-02-10T17:44:21+00:00"
];

final equity_2 = <double>[166.67, 169.4868, 171.258, 173.4746, 289.3755];

final timeStamps_3 = [
  "2023-02-07T01:00:00+00:00",
  "2023-02-08T01:00:00+00:00",
  "2023-02-09T01:00:00+00:00",
  "2023-02-10T01:00:00+00:00",
  "2023-02-10T17:44:21+00:00"
];

final equity_3 = <double>[511.4307, 524.1206, 514.721, 512.718, 746.5674];

final timeStamps_4 = [
  "2023-02-07T01:00:00+00:00",
  "2023-02-08T01:00:00+00:00",
  "2023-02-09T01:00:00+00:00",
  "2023-02-10T01:00:00+00:00",
  "2023-02-10T17:44:21+00:00"
];

final equity_4 = <double>[0, 0, 0, 0, 0];

class ChartRawData {
  ChartRawData({required List<String> dateTimes, required this.equity})
      : dateTimes = dateTimes.map((e) => DateTime.parse(e).toLocal()).toList();
  final List<double> equity;
  final List<DateTime> dateTimes;
}

ChartSpotData _buildFlSpots({
  required ChartRawData raw,
}) {
  assert(raw.dateTimes.length == raw.equity.length);
  final spots = <FlSpot>[];

  raw.dateTimes.asMap().forEach((index, date) {
    spots.add(FlSpot(date.millisecondsSinceEpoch.toDouble(), raw.equity[index]));
  });

  final maxX = spots.reduce((first, second) => first.x > second.x ? first : second);
  final minX = spots.reduce((first, second) => first.x < second.x ? first : second);
  final maxY = spots.reduce((first, second) => first.y > second.y ? first : second);
  final minY = spots.reduce((first, second) => first.y < second.y ? first : second);

  return ChartSpotData(
    spots: spots,
    minY: minY.y,
    maxY: maxY.y,
    maxX: maxX.x,
    minX: minX.x,
  );
}

class ChartLineData {
  ChartLineData({
    required this.line,
    required this.minY,
    required this.maxY,
    required this.maxX,
    required this.minX,
  });

  final LineChartBarData line;
  final double minY;
  final double maxY;
  final double maxX;
  final double minX;
}

class ChartSpotData {
  ChartSpotData({
    required this.spots,
    required this.minY,
    required this.maxY,
    required this.maxX,
    required this.minX,
  });

  final List<FlSpot> spots;
  final double minY;
  final double maxY;
  final double maxX;
  final double minX;
}

LineChartData get mockLineChartData {
  final orangeLine = _lineChartBarData_1;
  final yellowLine = _lineChartBarData_2;
  final blueLine = _lineChartBarData_3;
  final greenLine = _lineChartBarData_4;

  final minY = [orangeLine.minY, yellowLine.minY, blueLine.minY, greenLine.minY].reduce(min);
  final maxY = [orangeLine.maxY, yellowLine.maxY, blueLine.maxY, greenLine.maxY].reduce(max);
  final maxX = [orangeLine.maxX, yellowLine.maxX, blueLine.maxX, greenLine.maxX].reduce(max);
  final minX = [orangeLine.minX, yellowLine.minX, blueLine.minX, greenLine.minX].reduce(min);

  return LineChartData(
    clipData: FlClipData.none(),
    // lineTouchData: lineTouchData1,
    gridData: gridData,
    titlesData: titlesData,
    borderData: borderData,
    extraLinesData: ExtraLinesData(horizontalLines: [
      _makeHorizontalLine(orangeLine.maxY, maxY: maxY, minY: minY),
      _makeHorizontalLine(yellowLine.maxY, maxY: maxY, minY: minY),
      _makeHorizontalLine(blueLine.maxY, maxY: maxY, minY: minY), // for some reason it draws off screen
      _makeHorizontalLine(greenLine.maxY, maxY: maxY, minY: minY),
    ], extraLinesOnTop: true),
    lineBarsData: [orangeLine.line, yellowLine.line, blueLine.line, greenLine.line],
    minY: minY,
    maxY: maxY,
    maxX: maxX,
    minX: minX,
  );
}

// handles an issue where the when the Y is the same as the maxY/minY it doesn't draw
HorizontalLine _makeHorizontalLine(double y, {required double maxY, required double minY,}) {
  // bug: https:/imaNNeo/fl_chart/issues/1255
  return HorizontalLine(
    y: y,
    dashArray: [5, 10],
    color: Colors.grey.shade400,
  );
}

ChartLineData get _lineChartBarData_1 {
  final spotData = _buildFlSpots(
    raw: ChartRawData(
      dateTimes: timeStamps_1,
      equity: equity_1,
    ),
  );

  final line = LineChartBarData(
    isCurved: true,
    preventCurveOverShooting: true,
    color: Colors.orange,
    barWidth: barWidth,
    isStrokeCapRound: true,
    dotData: FlDotData(show: true),
    belowBarData: BarAreaData(show: false),
    spots: spotData.spots,
  );

  return ChartLineData(
    line: line,
    minY: spotData.minY,
    maxY: spotData.maxY,
    maxX: spotData.maxX,
    minX: spotData.minX,
  );
}

ChartLineData get _lineChartBarData_2 {
  final spotData = _buildFlSpots(
    raw: ChartRawData(
      dateTimes: timeStamps_2,
      equity: equity_2,
    ),
  );

  final line = LineChartBarData(
    isCurved: true,
    preventCurveOverShooting: true,
    color: Colors.yellow,
    barWidth: barWidth,
    isStrokeCapRound: true,
    dotData: FlDotData(show: true),
    belowBarData: BarAreaData(show: false),
    spots: spotData.spots,
  );

  return ChartLineData(
    line: line,
    minY: spotData.minY,
    maxY: spotData.maxY,
    maxX: spotData.maxX,
    minX: spotData.minX,
  );
}

ChartLineData get _lineChartBarData_3 {
  final spotData = _buildFlSpots(
    raw: ChartRawData(
      dateTimes: timeStamps_3,
      equity: equity_3,
    ),
  );

  final line = LineChartBarData(
    isCurved: true,
    preventCurveOverShooting: true,
    color: Colors.cyan,
    barWidth: barWidth,
    isStrokeCapRound: true,
    dotData: FlDotData(show: true),
    belowBarData: BarAreaData(show: false),
    spots: spotData.spots,
  );

  return ChartLineData(
    line: line,
    minY: spotData.minY,
    maxY: spotData.maxY,
    maxX: spotData.maxX,
    minX: spotData.minX,
  );
}

ChartLineData get _lineChartBarData_4 {
  final spotData = _buildFlSpots(
    raw: ChartRawData(
      dateTimes: timeStamps_4,
      equity: equity_4,
    ),
  );

  final line = LineChartBarData(
    isCurved: true,
    preventCurveOverShooting: true,
    color: Colors.green,
    barWidth: barWidth,
    isStrokeCapRound: true,
    dotData: FlDotData(show: true),
    belowBarData: BarAreaData(show: false),
    spots: spotData.spots,
  );

  return ChartLineData(
    line: line,
    minY: spotData.minY,
    maxY: spotData.maxY,
    maxX: spotData.maxX,
    minX: spotData.minX,
  );
}

FlBorderData get borderData => FlBorderData(
      show: true,
      border: const Border(
        bottom: BorderSide(color: Colors.transparent),
        // BorderSide(color: AppColors.primary.withOpacity(0.2), width: 4),
        left: BorderSide(color: Colors.transparent),
        right: BorderSide(color: Colors.transparent),
        top: BorderSide(color: Colors.transparent),
      ),
    );

FlTitlesData get titlesData => FlTitlesData(
      bottomTitles: AxisTitles(
        sideTitles: SideTitles(showTitles: false),
      ),
      rightTitles: AxisTitles(
        sideTitles: SideTitles(showTitles: false),
      ),
      topTitles: AxisTitles(
        sideTitles: SideTitles(showTitles: false),
      ),
      leftTitles: AxisTitles(
        sideTitles: SideTitles(showTitles: false),
      ),
    );

FlGridData get gridData => FlGridData(show: false);

LineTouchData get lineTouchData2 => LineTouchData(
      enabled: false,
    );

@mr-follow
Copy link

@imaNNeo That code would show the missing top & bottom lines. Hope that helps and thanks for the amazing package!

@mr-follow
Copy link

Showing the chart:

    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.white,
        appBar: AppBar(
          title: const Text('Follow Chart Example'),
        ),
        body: Center(
          child: Container(
            padding: const EdgeInsets.all(8),
            decoration: const BoxDecoration(
              color: Colors.white,
            ),
            height: 280,
            child: FollowChart(data: mockLineChartData),
          ),
        ),
      ),
    );

@imaNNeo
Copy link
Owner

imaNNeo commented Feb 12, 2023

Your reproducible code is not valid.
I get this error:
image

Please give me a code to put it in my main.dart file without any outside dependency.

@JoshMart
Copy link
Contributor

Here's a complete file that should work. There are 2 horizontal lines that should be displayed but are not because they match where the top and bottom of the chart would be drawn. This is related to work I did. I'll open a PR so you can review the fix.

import 'package:flutter/material.dart';
import 'dart:math';
import 'package:fl_chart/fl_chart.dart';

final timeStamps_1 = [
  "2023-02-07T01:00:00+00:00",
  "2023-02-08T01:00:00+00:00",
  "2023-02-09T01:00:00+00:00",
  "2023-02-10T01:00:00+00:00",
  "2023-02-10T17:44:21+00:00"
];

const double barWidth = 5;

final equity_1 = <double>[25.0901, 25.593, 24.8272, 24.4687, 149.0118];

final timeStamps_2 = [
  "2023-02-07T01:00:00+00:00",
  "2023-02-08T01:00:00+00:00",
  "2023-02-09T01:00:00+00:00",
  "2023-02-10T01:00:00+00:00",
  "2023-02-10T17:44:21+00:00"
];

final equity_2 = <double>[166.67, 169.4868, 171.258, 173.4746, 289.3755];

final timeStamps_3 = [
  "2023-02-07T01:00:00+00:00",
  "2023-02-08T01:00:00+00:00",
  "2023-02-09T01:00:00+00:00",
  "2023-02-10T01:00:00+00:00",
  "2023-02-10T17:44:21+00:00"
];

final equity_3 = <double>[511.4307, 524.1206, 514.721, 512.718, 746.5674];

final timeStamps_4 = [
  "2023-02-07T01:00:00+00:00",
  "2023-02-08T01:00:00+00:00",
  "2023-02-09T01:00:00+00:00",
  "2023-02-10T01:00:00+00:00",
  "2023-02-10T17:44:21+00:00"
];

final equity_4 = <double>[0, 0, 0, 0, 0];

class ChartRawData {
  ChartRawData({required List<String> dateTimes, required this.equity})
      : dateTimes = dateTimes.map((e) => DateTime.parse(e).toLocal()).toList();
  final List<double> equity;
  final List<DateTime> dateTimes;
}

ChartSpotData _buildFlSpots({
  required ChartRawData raw,
}) {
  assert(raw.dateTimes.length == raw.equity.length);
  final spots = <FlSpot>[];

  raw.dateTimes.asMap().forEach((index, date) {
    spots
        .add(FlSpot(date.millisecondsSinceEpoch.toDouble(), raw.equity[index]));
  });

  final maxX =
      spots.reduce((first, second) => first.x > second.x ? first : second);
  final minX =
      spots.reduce((first, second) => first.x < second.x ? first : second);
  final maxY =
      spots.reduce((first, second) => first.y > second.y ? first : second);
  final minY =
      spots.reduce((first, second) => first.y < second.y ? first : second);

  return ChartSpotData(
    spots: spots,
    minY: minY.y,
    maxY: maxY.y,
    maxX: maxX.x,
    minX: minX.x,
  );
}

class ChartLineData {
  ChartLineData({
    required this.line,
    required this.minY,
    required this.maxY,
    required this.maxX,
    required this.minX,
  });

  final LineChartBarData line;
  final double minY;
  final double maxY;
  final double maxX;
  final double minX;
}

class ChartSpotData {
  ChartSpotData({
    required this.spots,
    required this.minY,
    required this.maxY,
    required this.maxX,
    required this.minX,
  });

  final List<FlSpot> spots;
  final double minY;
  final double maxY;
  final double maxX;
  final double minX;
}

LineChartData get mockLineChartData {
  final orangeLine = _lineChartBarData_1;
  final yellowLine = _lineChartBarData_2;
  final blueLine = _lineChartBarData_3;
  final greenLine = _lineChartBarData_4;

  final minY = [orangeLine.minY, yellowLine.minY, blueLine.minY, greenLine.minY]
      .reduce(min);
  final maxY = [orangeLine.maxY, yellowLine.maxY, blueLine.maxY, greenLine.maxY]
      .reduce(max);
  final maxX = [orangeLine.maxX, yellowLine.maxX, blueLine.maxX, greenLine.maxX]
      .reduce(max);
  final minX = [orangeLine.minX, yellowLine.minX, blueLine.minX, greenLine.minX]
      .reduce(min);

  return LineChartData(
    clipData: FlClipData.none(),
    // lineTouchData: lineTouchData1,
    gridData: gridData,
    titlesData: titlesData,
    borderData: borderData,
    extraLinesData: ExtraLinesData(horizontalLines: [
      _makeHorizontalLine(orangeLine.maxY),
      _makeHorizontalLine(yellowLine.maxY),
      _makeHorizontalLine(blueLine.maxY), // for some reason it draws off screen
      _makeHorizontalLine(greenLine.maxY),
    ], extraLinesOnTop: true),
    lineBarsData: [
      orangeLine.line,
      yellowLine.line,
      blueLine.line,
      greenLine.line
    ],
    minY: minY,
    maxY: maxY,
    maxX: maxX,
    minX: minX,
  );
}

// handles an issue where the when the Y is the same as the maxY/minY it doesn't draw
HorizontalLine _makeHorizontalLine(double y) {
  // bug: https:/imaNNeo/fl_chart/issues/1255
  return HorizontalLine(
    y: y,
    dashArray: [5, 10],
    color: Colors.grey.shade400,
  );
}

ChartLineData get _lineChartBarData_1 {
  final spotData = _buildFlSpots(
    raw: ChartRawData(
      dateTimes: timeStamps_1,
      equity: equity_1,
    ),
  );

  final line = LineChartBarData(
    isCurved: true,
    preventCurveOverShooting: true,
    color: Colors.orange,
    barWidth: barWidth,
    isStrokeCapRound: true,
    dotData: FlDotData(show: true),
    belowBarData: BarAreaData(show: false),
    spots: spotData.spots,
  );

  return ChartLineData(
    line: line,
    minY: spotData.minY,
    maxY: spotData.maxY,
    maxX: spotData.maxX,
    minX: spotData.minX,
  );
}

ChartLineData get _lineChartBarData_2 {
  final spotData = _buildFlSpots(
    raw: ChartRawData(
      dateTimes: timeStamps_2,
      equity: equity_2,
    ),
  );

  final line = LineChartBarData(
    isCurved: true,
    preventCurveOverShooting: true,
    color: Colors.yellow,
    barWidth: barWidth,
    isStrokeCapRound: true,
    dotData: FlDotData(show: true),
    belowBarData: BarAreaData(show: false),
    spots: spotData.spots,
  );

  return ChartLineData(
    line: line,
    minY: spotData.minY,
    maxY: spotData.maxY,
    maxX: spotData.maxX,
    minX: spotData.minX,
  );
}

ChartLineData get _lineChartBarData_3 {
  final spotData = _buildFlSpots(
    raw: ChartRawData(
      dateTimes: timeStamps_3,
      equity: equity_3,
    ),
  );

  final line = LineChartBarData(
    isCurved: true,
    preventCurveOverShooting: true,
    color: Colors.cyan,
    barWidth: barWidth,
    isStrokeCapRound: true,
    dotData: FlDotData(show: true),
    belowBarData: BarAreaData(show: false),
    spots: spotData.spots,
  );

  return ChartLineData(
    line: line,
    minY: spotData.minY,
    maxY: spotData.maxY,
    maxX: spotData.maxX,
    minX: spotData.minX,
  );
}

ChartLineData get _lineChartBarData_4 {
  final spotData = _buildFlSpots(
    raw: ChartRawData(
      dateTimes: timeStamps_4,
      equity: equity_4,
    ),
  );

  final line = LineChartBarData(
    isCurved: true,
    preventCurveOverShooting: true,
    color: Colors.green,
    barWidth: barWidth,
    isStrokeCapRound: true,
    dotData: FlDotData(show: true),
    belowBarData: BarAreaData(show: false),
    spots: spotData.spots,
  );

  return ChartLineData(
    line: line,
    minY: spotData.minY,
    maxY: spotData.maxY,
    maxX: spotData.maxX,
    minX: spotData.minX,
  );
}

FlBorderData get borderData => FlBorderData(
      show: true,
      border: const Border(
        bottom: BorderSide(color: Colors.transparent),
        // BorderSide(color: AppColors.primary.withOpacity(0.2), width: 4),
        left: BorderSide(color: Colors.transparent),
        right: BorderSide(color: Colors.transparent),
        top: BorderSide(color: Colors.transparent),
      ),
    );

FlTitlesData get titlesData => FlTitlesData(
      bottomTitles: AxisTitles(
        sideTitles: SideTitles(showTitles: false),
      ),
      rightTitles: AxisTitles(
        sideTitles: SideTitles(showTitles: false),
      ),
      topTitles: AxisTitles(
        sideTitles: SideTitles(showTitles: false),
      ),
      leftTitles: AxisTitles(
        sideTitles: SideTitles(showTitles: false),
      ),
    );

FlGridData get gridData => FlGridData(show: false);

LineTouchData get lineTouchData2 => LineTouchData(
      enabled: false,
    );

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.white,
        appBar: AppBar(
          title: const Text('Follow Chart Example'),
        ),
        body: Center(
          child: Container(
            padding: const EdgeInsets.all(8),
            decoration: const BoxDecoration(
              color: Colors.white,
            ),
            height: 280,
            child: LineChart(mockLineChartData),
          ),
        ),
      ),
    );
  }
}

@imaNNeo
Copy link
Owner

imaNNeo commented Apr 15, 2023

Fixed in 0.62.0. Please check it out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants