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 is not rendered properly on iOS #1690

Closed mihakosi closed 4 months ago

mihakosi commented 5 months ago

When using line chart on iOS, the chart is not rendered properly. More specifically, each stroke narrows towards the end and looks like a sharp tip. Because of this, the chart looks jagged:

image

On Android and macOS, on the other hand, the strokes are always of the same width and look perfectly aligned:

image

Setting isStrokeCapRound and isStrokeJoinRound to true doesn't solve the issue.

Here is the code that can be used to reproduce the issue:

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: Scaffold(
        appBar: AppBar(),
        body: Padding(
          padding: const EdgeInsets.symmetric(
            vertical: 16,
          ),
          child: Scaffold(
            body: SizedBox(
              width: MediaQuery.of(context).size.width,
              height: 80,
              child: Padding(
                padding: EdgeInsets.zero,
                child: LineChart(
                  LineChartData(
                    lineTouchData: const LineTouchData(
                      enabled: false,
                    ),
                    lineBarsData: [
                      LineChartBarData(
                        spots: const [
                          FlSpot(0, 3),
                          FlSpot(2.6, 2),
                          FlSpot(4.9, 5),
                          FlSpot(6.8, 3.1),
                          FlSpot(8, 4),
                          FlSpot(9.5, 3),
                          FlSpot(11, 4),
                          FlSpot(12, 3),
                          FlSpot(13.2, 2),
                          FlSpot(15.4, 5),
                          FlSpot(18.2, 3.1),
                          FlSpot(18.9, 4),
                          FlSpot(19, 3),
                        ],
                        barWidth: 2,
                        color: const Color(0xFF0099FF),
                        belowBarData: BarAreaData(
                          show: true,
                          applyCutOffY: false,
                          gradient: const LinearGradient(
                            begin: Alignment.topCenter,
                            end: Alignment.bottomCenter,
                            colors: [
                              Color(0x800099FF),
                              Color(0x80FFFFFF),
                            ],
                          ),
                        ),
                        dotData: const FlDotData(
                          show: false,
                        ),
                      ),
                    ],
                    titlesData: const FlTitlesData(
                      show: false,
                    ),
                    borderData: FlBorderData(
                      show: false,
                    ),
                    gridData: const FlGridData(
                      show: false,
                    ),
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Versions:

imaNNeo commented 5 months ago

It seems it is a flutter engine-related issue So, let's wait to see what happens

mihakosi commented 5 months ago

OK, thank you. Let's hope this gets resolved soon.

For anyone that might be looking for a solution, I have discovered the following workaround: you can use stack to render the same chart twice (one on top of the other), once in the reversed order and once in the original order, like this:

SizedBox(
  width: MediaQuery.of(context).size.width,
  height: 200,
  child: Stack(
    children: [
      // First chart, drawing in the reversed order
      LineChart(
        LineChartData(
          lineTouchData: const LineTouchData(
            enabled: false,
          ),
          lineBarsData: [
            LineChartBarData(
              spots: chartData.reversed.toList(),
              dotData: const FlDotData(
                show: false,
              ),
            ),
          ],
        ),
      ),
      // Second chart, drawing in the original order
      LineChart(
        LineChartData(
          lineTouchData: const LineTouchData(
            enabled: false,
          ),
          lineBarsData: [
            LineChartBarData(
              spots: chartData,
              dotData: const FlDotData(
                show: false,
              ),
            ),
          ],
        ),
      ),
    ],
  ),
)

You end up with two charts, on the first one the stroke ends are pointed towards the left side of the screen and on the other one towards the right side of the screen. But because they are displayed in a stack they are perfectly aligned and look like a properly rendered chart.

You can also limit this workaround to iOS to avoid unnecessarily impacting the performance of the app on other devices:

if (Platform.isIOS) {
  // Draw two charts in a stack, like in the code above
} else {
  // Draw chart normally
}
ruan65 commented 4 months ago

I am also interested in solving this problem...

techouse commented 4 months ago

Duplicate of https://github.com/imaNNeo/fl_chart/issues/1625