imaNNeo / imaNNeo/fl_chart

LineChart extra lines label bugs

Open
#1,425 0 comments 0 reactions 0 assignees View on GitHub
bug Line Chart
Dominant language
Dart
Stars
7.6k
Forks
2k
Avg merge
9d 1h
Merged PRs (30d)
2

Description

**Describe the bug**

- Extra lines labels will be cut off, if any border is set to be clipped
- The padding on extra-line labels does not work correctly. On VerticalExtraLines I expect top padding to move the label down, which does not happen. Horizontal lines should be moved up by adding bottom padding, which also does not happen.

**To Reproduce**

The following code can be run in the example app provided with the repository.

Code to reproduce issue

Complete code for line_chart_sample1.dart with ExtraLinesData

``` dart
import 'package:fl_chart_app/presentation/resources/app_resources.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';

class _LineChart extends StatelessWidget {
const _LineChart({required this.isShowingMainData});

final bool isShowingMainData;

@override
Widget build(BuildContext context) {
return LineChart(
isShowingMainData ? sampleData1 : sampleData2,
duration: const Duration(milliseconds: 250),
);
}

LineChartData get sampleData1 => LineChartData(
lineTouchData: lineTouchData1,
gridData: gridData,
titlesData: titlesData1,
borderData: borderData,
lineBarsData: lineBarsData1,
minX: 0,
maxX: 14,
maxY: 4,
minY: 0,
extraLinesData: ExtraLinesData(extraLinesOnTop: true, verticalLines: [
VerticalLine(
x: 1,
color: AppColors.mainTextColor2,
strokeWidth: 1,
label: VerticalLineLabel(
labelResolver: (p0) => "Line 2",
show: true,
style: const TextStyle(color: Colors.white),
padding: const EdgeInsets.only(top: 10),
alignment: Alignment.bottomCenter,
)),
], horizontalLines: [
HorizontalLine(
y: 4,
color: AppColors.mainTextColor2,
strokeWidth: 1,
label: HorizontalLineLabel(
labelResolver: (p0) => "Line 1",
show: true,
style: const TextStyle(color: Colors.white),
alignment: Alignment.bottomCenter,
padding: const EdgeInsets.only(top: 20),
))
]),
clipData: const FlClipData.all(),
);

LineChartData get sampleData2 => LineChartData(
lineTouchData: lineTouchData2,
gridData: gridData,
titlesData: titlesData2,
borderData: borderData,
lineBarsData: lineBarsData2,
minX: 0,
maxX: 14,
maxY: 6,
minY: 0,
);

LineTouchData get lineTouchData1 => LineTouchData(
handleBuiltInTouches: true,
touchTooltipData: LineTouchTooltipData(
tooltipBgColor: Colors.blueGrey.withOpacity(0.8),
),
);

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

List get lineBarsData1 => [
lineChartBarData1_1,
lineChartBarData1_2,
lineChartBarData1_3,
];

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

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

List get lineBarsData2 => [
lineChartBarData2_1,
lineChartBarData2_2,
lineChartBarData2_3,
];

Widget leftTitleWidgets(double value, TitleMeta meta) {
const style = TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
);
String text;
switch (value.toInt()) {
case 1:
text = '1m';
break;
case 2:
text = '2m';
break;
case 3:
text = '3m';
break;
case 4:
text = '5m';
break;
case 5:
text = '6m';
break;
default:
return Container();
}

return Text(text, style: style, textAlign: TextAlign.center);
}

SideTitles leftTitles() => SideTitles(
getTitlesWidget: leftTitleWidgets,
showTitles: true,
interval: 1,
reservedSize: 40,
);

Widget bottomTitleWidgets(double value, TitleMeta meta) {
const style = TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
);
Widget text;
switch (value.toInt()) {
case 2:
text = const Text('SEPT', style: style);
break;
case 7:
text = const Text('OCT', style: style);
break;
case 12:
text = const Text('DEC', style: style);
break;
default:
text = const Text('');
break;
}

return SideTitleWidget(
axisSide: meta.axisSide,
space: 10,
child: text,
);
}

SideTitles get bottomTitles => SideTitles(
showTitles: true,
reservedSize: 32,
interval: 1,
getTitlesWidget: bottomTitleWidgets,
);

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

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

LineChartBarData get lineChartBarData1_1 => LineChartBarData(
isCurved: true,
color: AppColors.contentColorGreen,
barWidth: 8,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(show: false),
spots: const [
FlSpot(1, 1),
FlSpot(3, 1.5),
FlSpot(5, 1.4),
FlSpot(7, 3.4),
FlSpot(10, 2),
FlSpot(12, 2.2),
FlSpot(13, 1.8),
FlSpot(18, 1.8),
],
);

LineChartBarData get lineChartBarData1_2 => LineChartBarData(
isCurved: true,
color: AppColors.contentColorPink,
barWidth: 8,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(
show: false,
color: AppColors.contentColorPink.withOpacity(0),
),
spots: const [
FlSpot(1, 1),
FlSpot(3, 2.8),
FlSpot(7, 1.2),
FlSpot(10, 2.8),
FlSpot(12, 2.6),
FlSpot(13, 3.9),
],
);

LineChartBarData get lineChartBarData1_3 => LineChartBarData(
isCurved: true,
color: AppColors.contentColorCyan,
barWidth: 8,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(show: false),
spots: const [
FlSpot(1, 2.8),
FlSpot(3, 1.9),
FlSpot(6, 3),
FlSpot(10, 1.3),
FlSpot(13, 2.5),
],
);

LineChartBarData get lineChartBarData2_1 => LineChartBarData(
isCurved: true,
curveSmoothness: 0,
color: AppColors.contentColorGreen.withOpacity(0.5),
barWidth: 4,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(show: false),
spots: const [
FlSpot(1, 1),
FlSpot(3, 4),
FlSpot(5, 1.8),
FlSpot(7, 5),
FlSpot(10, 2),
FlSpot(12, 2.2),
FlSpot(13, 1.8),
],
);

LineChartBarData get lineChartBarData2_2 => LineChartBarData(
isCurved: true,
color: AppColors.contentColorPink.withOpacity(0.5),
barWidth: 4,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(
show: true,
color: AppColors.contentColorPink.withOpacity(0.2),
),
spots: const [
FlSpot(1, 1),
FlSpot(3, 2.8),
FlSpot(7, 1.2),
FlSpot(10, 2.8),
FlSpot(12, 2.6),
FlSpot(13, 3.9),
],
);

LineChartBarData get lineChartBarData2_3 => LineChartBarData(
isCurved: true,
curveSmoothness: 0,
color: AppColors.contentColorCyan.withOpacity(0.5),
barWidth: 2,
isStrokeCapRound: true,
dotData: const FlDotData(show: true),
belowBarData: BarAreaData(show: false),
spots: const [
FlSpot(1, 3.8),
FlSpot(3, 1.9),
FlSpot(6, 5),
FlSpot(10, 3.3),
FlSpot(13, 4.5),
],
);
}

class LineChartSample1 extends StatefulWidget {
const LineChartSample1({super.key});

@override
State createState() => LineChartSample1State();
}

class LineChartSample1State extends State {
late bool isShowingMainData;

@override
void initState() {
super.initState();
isShowingMainData = true;
}

@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 1.23,
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(
height: 37,
),
const Text(
'Monthly Sales',
style: TextStyle(
color: AppColors.primary,
fontSize: 32,
fontWeight: FontWeight.bold,
letterSpacing: 2,
),
textAlign: TextAlign.center,
),
const SizedBox(
height: 37,
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 16, left: 6),
child: _LineChart(isShowingMainData: isShowingMainData),
),
),
const SizedBox(
height: 10,
),
],
),
IconButton(
icon: Icon(
Icons.refresh,
color: Colors.white.withOpacity(isShowingMainData ? 1.0 : 0.5),
),
onPressed: () {
setState(() {
isShowingMainData = !isShowingMainData;
});
},
)
],
),
);
}
}
```

**Screenshots**

Current | Expected
--- | ---
![Simulator Screenshot - iPhone 14 Pro - 2023-08-24 at 20 13 00](https://github.com/imaNNeo/fl_chart/assets/18733319/09f1eba7-1882-4427-820c-386402cc4085) | ![Simulator Screenshot - iPhone 14 Pro - 2023-08-24 at 20 13 36](https://github.com/imaNNeo/fl_chart/assets/18733319/d9182e1c-4bfd-44ef-907a-fe51ee2c3e40)

**Versions**
- Flutter: 3.13.0
- FlChart: 0.63.0

Contributor guide

Open the contributing guide

Research direction

Start by running the example app with the reproduced line_chart_sample1.dart configuration, including FlClipData.all(), ExtraLinesData, VerticalLineLabel, and HorizontalLineLabel. Trace the extra-line label rendering from those options and verify the labels are not clipped and that top or bottom padding moves them in the expected direction.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart, flutter
Domain
data-visualization, frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.