Skip to content

Commit 04e3551

Browse files
feat: add gradientArea property for LineChartBarData (#1925)
* feat: add `gradientArea` property for `LineChartBarData` * test: add test for `gradientArea` * docs: add documents for `gradientArea` * Set the default value for `gradientArea` in `LineChartBarData`'s constructor and add docs for `LineChartGradientArea` * updated CHANGELOG.md
1 parent 3a3484c commit 04e3551

File tree

6 files changed

+195
-4
lines changed

6 files changed

+195
-4
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 1.1.0
2+
* **FEATURE** (by @huanghui1998hhh) Add `gradientArea` property to `LineChartBarData` to allow you to control the scope of gradient effects, #1925
3+
14
## 1.0.0
25
<img width="600" alt="Image" src="https://github.com/user-attachments/assets/3d8b58f4-4ce7-489f-ba45-27ece063f57c" />
36

lib/src/chart/line_chart/line_chart_data.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,15 @@ class LineChartData extends AxisChartData with EquatableMixin {
184184
];
185185
}
186186

187+
enum LineChartGradientArea {
188+
/// The gradient area will be around the line only, meaning
189+
/// the gradient will exactly wrap around the curve.
190+
rectAroundTheLine,
191+
192+
/// The entire chart area will be used as the gradient area for the curve.
193+
wholeChart;
194+
}
195+
187196
/// Holds data for drawing each individual line in the [LineChart]
188197
class LineChartBarData with EquatableMixin {
189198
/// [BarChart] draws some lines and overlaps them in the chart's view,
@@ -232,6 +241,7 @@ class LineChartBarData with EquatableMixin {
232241
this.show = true,
233242
Color? color,
234243
this.gradient,
244+
this.gradientArea = LineChartGradientArea.rectAroundTheLine,
235245
this.barWidth = 2.0,
236246
this.isCurved = false,
237247
this.curveSmoothness = 0.35,
@@ -324,6 +334,11 @@ class LineChartBarData with EquatableMixin {
324334
/// It throws an exception if you provide both [color] and [gradient]
325335
final Gradient? gradient;
326336

337+
/// Only effective if [gradient] is provided.
338+
///
339+
/// It will be used to determine the area of the gradient.
340+
final LineChartGradientArea gradientArea;
341+
327342
/// Determines thickness of drawing line.
328343
final double barWidth;
329344

@@ -405,6 +420,7 @@ class LineChartBarData with EquatableMixin {
405420
dashArray: lerpIntList(a.dashArray, b.dashArray, t),
406421
color: Color.lerp(a.color, b.color, t),
407422
gradient: Gradient.lerp(a.gradient, b.gradient, t),
423+
gradientArea: b.gradientArea,
408424
spots: lerpFlSpotList(a.spots, b.spots, t)!,
409425
showingIndicators: b.showingIndicators,
410426
shadow: Shadow.lerp(a.shadow, b.shadow, t)!,
@@ -420,6 +436,7 @@ class LineChartBarData with EquatableMixin {
420436
bool? show,
421437
Color? color,
422438
Gradient? gradient,
439+
LineChartGradientArea? gradientArea,
423440
double? barWidth,
424441
bool? isCurved,
425442
double? curveSmoothness,
@@ -443,6 +460,7 @@ class LineChartBarData with EquatableMixin {
443460
show: show ?? this.show,
444461
color: color ?? this.color,
445462
gradient: gradient ?? this.gradient,
463+
gradientArea: gradientArea ?? this.gradientArea,
446464
barWidth: barWidth ?? this.barWidth,
447465
isCurved: isCurved ?? this.isCurved,
448466
curveSmoothness: curveSmoothness ?? this.curveSmoothness,
@@ -470,6 +488,7 @@ class LineChartBarData with EquatableMixin {
470488
show,
471489
color,
472490
gradient,
491+
gradientArea,
473492
barWidth,
474493
isCurved,
475494
curveSmoothness,

lib/src/chart/line_chart/line_chart_painter.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,9 @@ class LineChartPainter extends AxisChartPainter<LineChartData> {
10561056
..setColorOrGradient(
10571057
barData.color,
10581058
barData.gradient,
1059-
rectAroundTheLine,
1059+
barData.gradientArea == LineChartGradientArea.wholeChart
1060+
? Offset.zero & viewSize
1061+
: rectAroundTheLine,
10601062
)
10611063
..maskFilter = null
10621064
..strokeWidth = barData.barWidth

repo_files/documentations/line_chart.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ When you change the chart's state, it animates to the new state internally (usin
4545
|spots| list of [FlSpot](base_chart.md#FlSpot)'s x and y coordinates that the line go through it| []
4646
|color|color of the line|[Colors.redAccent]|
4747
|gradient| You can use any [Gradient](https://api.flutter.dev/flutter/dart-ui/Gradient-class.html) here. such as [LinearGradient](https://api.flutter.dev/flutter/painting/LinearGradient-class.html) or [RadialGradient](https://api.flutter.dev/flutter/painting/RadialGradient-class.html)|null|
48+
|gradientArea| determines the area where the gradient is applied |null|
4849
|barWidth| gets the stroke width of the line bar|2.0|
4950
|isCurved| curves the corners of the line on the spot's positions| false|
5051
|curveSmoothness| smoothness radius of the curve corners (works when isCurved is true) | 0.35|

test/chart/line_chart/line_chart_painter_test.dart

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import '../data_pool.dart';
1818
import 'line_chart_painter_test.mocks.dart';
1919

2020
@GenerateMocks([Canvas, CanvasWrapper, BuildContext, Utils, LineChartPainter])
21+
@GenerateNiceMocks([MockSpec<LinearGradient>()])
2122
void main() {
2223
group('paint()', () {
2324
test('test 1', () {
@@ -2172,14 +2173,13 @@ void main() {
21722173
FlSpot(8, 9),
21732174
];
21742175

2176+
final mockLinearGradient = MockLinearGradient();
21752177
final lineChartBarData1 = LineChartBarData(
21762178
spots: barSpots1,
21772179
barWidth: 80,
21782180
isStrokeCapRound: true,
21792181
isStepLineChart: true,
2180-
gradient: const LinearGradient(
2181-
colors: [Color(0xF0F0F0F0), Color(0x0100FF00)],
2182-
),
2182+
gradient: mockLinearGradient,
21832183
dashArray: [1, 2, 3],
21842184
);
21852185

@@ -2199,6 +2199,11 @@ void main() {
21992199
final mockCanvasWrapper = MockCanvasWrapper();
22002200
when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize);
22012201
when(mockCanvasWrapper.canvas).thenReturn(MockCanvas());
2202+
// just to provide a dummy Shader, don't care about its value
2203+
provideDummy<Shader>(
2204+
const LinearGradient(colors: [Color(0xF0F0F0F0), Color(0x0100FF00)])
2205+
.createShader(Rect.zero),
2206+
);
22022207

22032208
final barPath = Path()
22042209
..moveTo(10, 10)
@@ -2222,6 +2227,29 @@ void main() {
22222227
expect(paint.shader != null, true);
22232228
expect(paint.maskFilter, null);
22242229
expect(paint.strokeWidth, 80);
2230+
2231+
final createShaderWithRectAroundTheLine =
2232+
verify(mockLinearGradient.createShader(captureAny))..called(1);
2233+
expect(
2234+
createShaderWithRectAroundTheLine.captured.single,
2235+
const Rect.fromLTRB(10, 10, 80, 10),
2236+
);
2237+
2238+
lineChartPainter.drawBar(
2239+
mockCanvasWrapper,
2240+
barPath,
2241+
lineChartBarData1.copyWith(
2242+
gradientArea: LineChartGradientArea.wholeChart,
2243+
),
2244+
holder,
2245+
);
2246+
2247+
final createShaderWithRectWholeChart =
2248+
verify(mockLinearGradient.createShader(captureAny))..called(1);
2249+
expect(
2250+
createShaderWithRectWholeChart.captured.single,
2251+
Offset.zero & viewSize,
2252+
);
22252253
});
22262254
});
22272255

test/chart/line_chart/line_chart_painter_test.mocks.dart

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,18 @@ class _FakePath_9 extends _i1.SmartFake implements _i2.Path {
105105
: super(parent, parentInvocation);
106106
}
107107

108+
class _FakeAlignmentGeometry_10 extends _i1.SmartFake
109+
implements _i3.AlignmentGeometry {
110+
_FakeAlignmentGeometry_10(Object parent, Invocation parentInvocation)
111+
: super(parent, parentInvocation);
112+
}
113+
114+
class _FakeLinearGradient_11 extends _i1.SmartFake
115+
implements _i3.LinearGradient {
116+
_FakeLinearGradient_11(Object parent, Invocation parentInvocation)
117+
: super(parent, parentInvocation);
118+
}
119+
108120
/// A class which mocks [Canvas].
109121
///
110122
/// See the documentation for Mockito's code generation for more information.
@@ -1448,3 +1460,129 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter {
14481460
)
14491461
as double);
14501462
}
1463+
1464+
/// A class which mocks [LinearGradient].
1465+
///
1466+
/// See the documentation for Mockito's code generation for more information.
1467+
class MockLinearGradient extends _i1.Mock implements _i3.LinearGradient {
1468+
@override
1469+
_i3.AlignmentGeometry get begin =>
1470+
(super.noSuchMethod(
1471+
Invocation.getter(#begin),
1472+
returnValue: _FakeAlignmentGeometry_10(
1473+
this,
1474+
Invocation.getter(#begin),
1475+
),
1476+
returnValueForMissingStub: _FakeAlignmentGeometry_10(
1477+
this,
1478+
Invocation.getter(#begin),
1479+
),
1480+
)
1481+
as _i3.AlignmentGeometry);
1482+
1483+
@override
1484+
_i3.AlignmentGeometry get end =>
1485+
(super.noSuchMethod(
1486+
Invocation.getter(#end),
1487+
returnValue: _FakeAlignmentGeometry_10(
1488+
this,
1489+
Invocation.getter(#end),
1490+
),
1491+
returnValueForMissingStub: _FakeAlignmentGeometry_10(
1492+
this,
1493+
Invocation.getter(#end),
1494+
),
1495+
)
1496+
as _i3.AlignmentGeometry);
1497+
1498+
@override
1499+
_i2.TileMode get tileMode =>
1500+
(super.noSuchMethod(
1501+
Invocation.getter(#tileMode),
1502+
returnValue: _i2.TileMode.clamp,
1503+
returnValueForMissingStub: _i2.TileMode.clamp,
1504+
)
1505+
as _i2.TileMode);
1506+
1507+
@override
1508+
List<_i2.Color> get colors =>
1509+
(super.noSuchMethod(
1510+
Invocation.getter(#colors),
1511+
returnValue: <_i2.Color>[],
1512+
returnValueForMissingStub: <_i2.Color>[],
1513+
)
1514+
as List<_i2.Color>);
1515+
1516+
@override
1517+
_i2.Shader createShader(_i2.Rect? rect, {_i2.TextDirection? textDirection}) =>
1518+
(super.noSuchMethod(
1519+
Invocation.method(
1520+
#createShader,
1521+
[rect],
1522+
{#textDirection: textDirection},
1523+
),
1524+
returnValue: _i9.dummyValue<_i2.Shader>(
1525+
this,
1526+
Invocation.method(
1527+
#createShader,
1528+
[rect],
1529+
{#textDirection: textDirection},
1530+
),
1531+
),
1532+
returnValueForMissingStub: _i9.dummyValue<_i2.Shader>(
1533+
this,
1534+
Invocation.method(
1535+
#createShader,
1536+
[rect],
1537+
{#textDirection: textDirection},
1538+
),
1539+
),
1540+
)
1541+
as _i2.Shader);
1542+
1543+
@override
1544+
_i3.LinearGradient scale(double? factor) =>
1545+
(super.noSuchMethod(
1546+
Invocation.method(#scale, [factor]),
1547+
returnValue: _FakeLinearGradient_11(
1548+
this,
1549+
Invocation.method(#scale, [factor]),
1550+
),
1551+
returnValueForMissingStub: _FakeLinearGradient_11(
1552+
this,
1553+
Invocation.method(#scale, [factor]),
1554+
),
1555+
)
1556+
as _i3.LinearGradient);
1557+
1558+
@override
1559+
_i3.Gradient? lerpFrom(_i3.Gradient? a, double? t) =>
1560+
(super.noSuchMethod(
1561+
Invocation.method(#lerpFrom, [a, t]),
1562+
returnValueForMissingStub: null,
1563+
)
1564+
as _i3.Gradient?);
1565+
1566+
@override
1567+
_i3.Gradient? lerpTo(_i3.Gradient? b, double? t) =>
1568+
(super.noSuchMethod(
1569+
Invocation.method(#lerpTo, [b, t]),
1570+
returnValueForMissingStub: null,
1571+
)
1572+
as _i3.Gradient?);
1573+
1574+
@override
1575+
_i3.LinearGradient withOpacity(double? opacity) =>
1576+
(super.noSuchMethod(
1577+
Invocation.method(#withOpacity, [opacity]),
1578+
returnValue: _FakeLinearGradient_11(
1579+
this,
1580+
Invocation.method(#withOpacity, [opacity]),
1581+
),
1582+
returnValueForMissingStub: _FakeLinearGradient_11(
1583+
this,
1584+
Invocation.method(#withOpacity, [opacity]),
1585+
),
1586+
)
1587+
as _i3.LinearGradient);
1588+
}

0 commit comments

Comments
 (0)