Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 685029d

Browse files
feat: add native hatching support for BarChartRodStackItem
Add diagonal line pattern rendering for bar chart stack items with height-independent visual density.
1 parent 0436f7f commit 685029d

File tree

6 files changed

+577
-14
lines changed

6 files changed

+577
-14
lines changed
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import 'package:fl_chart/fl_chart.dart';
2+
import 'package:flutter/material.dart';
3+
4+
/// Example demonstrating the new hatching support for BarChartRodStackItem
5+
class BarChartSample9 extends StatefulWidget {
6+
const BarChartSample9({super.key});
7+
8+
@override
9+
State<BarChartSample9> createState() => _BarChartSample9State();
10+
}
11+
12+
class _BarChartSample9State extends State<BarChartSample9> {
13+
final Duration animDuration = const Duration(milliseconds: 250);
14+
15+
@override
16+
Widget build(BuildContext context) {
17+
return AspectRatio(
18+
aspectRatio: 1,
19+
child: Padding(
20+
padding: const EdgeInsets.all(16),
21+
child: BarChart(
22+
BarChartData(
23+
alignment: BarChartAlignment.spaceAround,
24+
maxY: 30,
25+
barTouchData: BarTouchData(
26+
touchTooltipData: BarTouchTooltipData(
27+
getTooltipColor: (_) => Colors.blueGrey,
28+
tooltipHorizontalAlignment: FLHorizontalAlignment.right,
29+
tooltipMargin: -10,
30+
getTooltipItem: (group, groupIndex, rod, rodIndex) {
31+
String weekDay;
32+
switch (group.x) {
33+
case 0:
34+
weekDay = 'Monday';
35+
break;
36+
case 1:
37+
weekDay = 'Tuesday';
38+
break;
39+
case 2:
40+
weekDay = 'Wednesday';
41+
break;
42+
case 3:
43+
weekDay = 'Thursday';
44+
break;
45+
case 4:
46+
weekDay = 'Friday';
47+
break;
48+
case 5:
49+
weekDay = 'Saturday';
50+
break;
51+
case 6:
52+
weekDay = 'Sunday';
53+
break;
54+
default:
55+
throw Error();
56+
}
57+
return BarTooltipItem(
58+
'$weekDay\n',
59+
const TextStyle(
60+
color: Colors.white,
61+
fontWeight: FontWeight.bold,
62+
fontSize: 18,
63+
),
64+
children: <TextSpan>[
65+
TextSpan(
66+
text: (rod.toY - rod.fromY).toString(),
67+
style: const TextStyle(
68+
color: Colors.yellow,
69+
fontSize: 16,
70+
fontWeight: FontWeight.w500,
71+
),
72+
),
73+
],
74+
);
75+
},
76+
),
77+
),
78+
titlesData: FlTitlesData(
79+
show: true,
80+
rightTitles: const AxisTitles(
81+
sideTitles: SideTitles(showTitles: false),
82+
),
83+
topTitles: const AxisTitles(
84+
sideTitles: SideTitles(showTitles: false),
85+
),
86+
bottomTitles: AxisTitles(
87+
sideTitles: SideTitles(
88+
showTitles: true,
89+
getTitlesWidget: _getTitles,
90+
reservedSize: 38,
91+
),
92+
),
93+
leftTitles: const AxisTitles(
94+
sideTitles: SideTitles(
95+
showTitles: false,
96+
),
97+
),
98+
),
99+
borderData: FlBorderData(
100+
show: false,
101+
),
102+
barGroups: [
103+
_makeGroupData(0, 5, 12),
104+
_makeGroupData(1, 6.5, 10),
105+
_makeGroupData(2, 5, 14),
106+
_makeGroupData(3, 7.5, 15),
107+
_makeGroupData(4, 9, 11.5),
108+
_makeGroupData(5, 11.5, 16),
109+
_makeGroupData(6, 6.5, 13),
110+
],
111+
gridData: const FlGridData(show: false),
112+
),
113+
swapAnimationDuration: animDuration,
114+
),
115+
),
116+
);
117+
}
118+
119+
Widget _getTitles(double value, TitleMeta meta) {
120+
const style = TextStyle(
121+
color: Colors.white,
122+
fontWeight: FontWeight.bold,
123+
fontSize: 14,
124+
);
125+
Widget text;
126+
switch (value.toInt()) {
127+
case 0:
128+
text = const Text('M', style: style);
129+
break;
130+
case 1:
131+
text = const Text('T', style: style);
132+
break;
133+
case 2:
134+
text = const Text('W', style: style);
135+
break;
136+
case 3:
137+
text = const Text('T', style: style);
138+
break;
139+
case 4:
140+
text = const Text('F', style: style);
141+
break;
142+
case 5:
143+
text = const Text('S', style: style);
144+
break;
145+
case 6:
146+
text = const Text('S', style: style);
147+
break;
148+
default:
149+
text = const Text('', style: style);
150+
break;
151+
}
152+
return SideTitleWidget(
153+
meta: meta,
154+
space: 16,
155+
child: text,
156+
);
157+
}
158+
159+
BarChartGroupData _makeGroupData(
160+
int x,
161+
double y1,
162+
double y2,
163+
) {
164+
return BarChartGroupData(
165+
x: x,
166+
barRods: [
167+
BarChartRodData(
168+
toY: y1 + y2,
169+
color: Colors.transparent, // Make the main rod transparent
170+
width: 22,
171+
borderRadius: BorderRadius.circular(4),
172+
rodStackItems: [
173+
// Regular solid color stack item
174+
BarChartRodStackItem(
175+
0,
176+
y1,
177+
const Color(0xff0293ee),
178+
),
179+
// Hatched stack item with diagonal lines
180+
BarChartRodStackItem(
181+
y1,
182+
y1 + y2,
183+
null, // No solid color
184+
isHatched: true,
185+
hatchPattern: const HatchPattern(
186+
hatchColor: Color(0xff38f3f3),
187+
backgroundColor: Color(0xff0293ee),
188+
spacing: 6.0,
189+
angle: -45.0,
190+
strokeWidth: 2.5,
191+
),
192+
),
193+
],
194+
),
195+
],
196+
);
197+
}
198+
}

example/lib/presentation/samples/chart_samples.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'bar/bar_chart_sample5.dart';
99
import 'bar/bar_chart_sample6.dart';
1010
import 'bar/bar_chart_sample7.dart';
1111
import 'bar/bar_chart_sample8.dart';
12+
import 'bar/bar_chart_sample9.dart';
1213
import 'chart_sample.dart';
1314
import 'line/line_chart_sample1.dart';
1415
import 'line/line_chart_sample10.dart';
@@ -56,6 +57,7 @@ class ChartSamples {
5657
BarChartSample(6, (context) => const BarChartSample6()),
5758
BarChartSample(7, (context) => BarChartSample7()),
5859
BarChartSample(8, (context) => BarChartSample8()),
60+
BarChartSample(9, (context) => const BarChartSample9()),
5961
],
6062
ChartType.pie: [
6163
PieChartSample(1, (context) => const PieChartSample1()),

lib/src/chart/bar_chart/bar_chart_data.dart

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -493,9 +493,11 @@ class BarChartRodStackItem with EquatableMixin {
493493
this.label,
494494
this.labelStyle,
495495
this.borderSide = Utils.defaultBorderSide,
496+
this.isHatched = false,
497+
this.hatchPattern,
496498
}) : assert(
497-
color != null || gradient != null,
498-
'You must provide either a color or gradient',
499+
color != null || gradient != null || (isHatched && hatchPattern != null),
500+
'You must provide either a color, gradient, or hatch pattern',
499501
);
500502
final String? label;
501503
final TextStyle? labelStyle;
@@ -515,6 +517,12 @@ class BarChartRodStackItem with EquatableMixin {
515517
/// Renders border stroke for a Stacked Chart section
516518
final BorderSide borderSide;
517519

520+
/// Whether this stack item should use hatching instead of solid color/gradient
521+
final bool isHatched;
522+
523+
/// Hatching pattern configuration (required if isHatched is true)
524+
final HatchPattern? hatchPattern;
525+
518526
/// Copies current [BarChartRodStackItem] to a new [BarChartRodStackItem],
519527
/// and replaces provided values.
520528
BarChartRodStackItem copyWith({
@@ -525,6 +533,8 @@ class BarChartRodStackItem with EquatableMixin {
525533
String? label,
526534
TextStyle? labelStyle,
527535
BorderSide? borderSide,
536+
bool? isHatched,
537+
HatchPattern? hatchPattern,
528538
}) =>
529539
BarChartRodStackItem(
530540
fromY ?? this.fromY,
@@ -534,6 +544,8 @@ class BarChartRodStackItem with EquatableMixin {
534544
label: label ?? this.label,
535545
labelStyle: labelStyle ?? this.labelStyle,
536546
borderSide: borderSide ?? this.borderSide,
547+
isHatched: isHatched ?? this.isHatched,
548+
hatchPattern: hatchPattern ?? this.hatchPattern,
537549
);
538550

539551
/// Lerps a [BarChartRodStackItem] based on [t] value, check [Tween.lerp].
@@ -550,12 +562,79 @@ class BarChartRodStackItem with EquatableMixin {
550562
label: b.label,
551563
labelStyle: b.labelStyle,
552564
borderSide: BorderSide.lerp(a.borderSide, b.borderSide, t),
565+
isHatched: b.isHatched,
566+
hatchPattern: b.hatchPattern != null && a.hatchPattern != null
567+
? HatchPattern.lerp(a.hatchPattern!, b.hatchPattern!, t)
568+
: b.hatchPattern,
553569
);
554570

555571
/// Used for equality check, see [EquatableMixin].
556572
@override
557573
List<Object?> get props =>
558-
[fromY, toY, color, gradient, label, labelStyle, borderSide];
574+
[fromY, toY, color, gradient, label, labelStyle, borderSide, isHatched, hatchPattern];
575+
}
576+
577+
/// Configuration for hatching pattern in bar chart stack items
578+
class HatchPattern with EquatableMixin {
579+
/// Creates a hatch pattern configuration
580+
///
581+
/// [spacing] - Distance between parallel hatch lines in logical pixels (default: 6.0)
582+
/// [angle] - Angle of hatch lines in degrees (default: -45.0 for diagonal)
583+
/// [strokeWidth] - Thickness of each hatch line (default: 1.0)
584+
/// [hatchColor] - Color of the hatch lines
585+
/// [backgroundColor] - Optional background color behind hatching
586+
const HatchPattern({
587+
this.spacing = 6.0,
588+
this.angle = -45.0,
589+
this.strokeWidth = 1.0,
590+
required this.hatchColor,
591+
this.backgroundColor,
592+
}) : assert(spacing > 0.0, 'Spacing must be greater than 0'),
593+
assert(strokeWidth >= 0.0, 'Stroke width must be non-negative');
594+
595+
/// Distance between parallel hatch lines in logical pixels
596+
final double spacing;
597+
598+
/// Angle of hatch lines in degrees
599+
final double angle;
600+
601+
/// Thickness of each hatch line
602+
final double strokeWidth;
603+
604+
/// Color of the hatch lines
605+
final Color hatchColor;
606+
607+
/// Optional background color behind hatching
608+
final Color? backgroundColor;
609+
610+
/// Creates a copy with modified properties
611+
HatchPattern copyWith({
612+
double? spacing,
613+
double? angle,
614+
double? strokeWidth,
615+
Color? hatchColor,
616+
Color? backgroundColor,
617+
}) =>
618+
HatchPattern(
619+
spacing: spacing ?? this.spacing,
620+
angle: angle ?? this.angle,
621+
strokeWidth: strokeWidth ?? this.strokeWidth,
622+
hatchColor: hatchColor ?? this.hatchColor,
623+
backgroundColor: backgroundColor ?? this.backgroundColor,
624+
);
625+
626+
/// Lerps between two HatchPattern instances
627+
static HatchPattern lerp(HatchPattern a, HatchPattern b, double t) =>
628+
HatchPattern(
629+
spacing: lerpDouble(a.spacing, b.spacing, t)!,
630+
angle: lerpDouble(a.angle, b.angle, t)!,
631+
strokeWidth: lerpDouble(a.strokeWidth, b.strokeWidth, t)!,
632+
hatchColor: Color.lerp(a.hatchColor, b.hatchColor, t)!,
633+
backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
634+
);
635+
636+
@override
637+
List<Object?> get props => [spacing, angle, strokeWidth, hatchColor, backgroundColor];
559638
}
560639

561640
/// Holds values to draw a rod in rear of the main rod.

0 commit comments

Comments
 (0)