Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions example/lib/presentation/samples/bar/bar_chart_sample1.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ import 'package:fl_chart_app/util/extensions/color_extensions.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';

enum BarPattern {
stripes,
squarePois,
circlePois,
none,
}

class BarChartSample1 extends StatefulWidget {
BarChartSample1({super.key});

Expand Down Expand Up @@ -33,6 +40,39 @@ class BarChartSample1State extends State<BarChartSample1> {
int touchedIndex = -1;

bool isPlaying = false;
BarPattern pattern = BarPattern.none;
final stripesPainter = StripesPatternPainter(
stripesShader: StripesShader(),
width: 2,
gap: 8,
angle: 45,
);
final squarePoisPainter = SquarePoisPatternPainter(
poisShader: SquarePoisShader(),
color: AppColors.contentColorBlack,
squaresPerRow: 4,
gap: 2.0,
verticalGap: 2.0,
margin: 2.0,
);
final circlePoisPainter = CirclePoisPatternPainter(
poisShader: CirclePoisShader(),
color: AppColors.contentColorBlack,
gap: 2.0,
);

@override
void initState() {
super.initState();

initializeShaders();
}

Future<void> initializeShaders() async {
await stripesPainter.initialize();
await squarePoisPainter.initialize();
await circlePoisPainter.initialize();
}

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -79,6 +119,32 @@ class BarChartSample1State extends State<BarChartSample1> {
const SizedBox(
height: 12,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Bar pattern',
style: TextStyle(
color: AppColors.contentColorGreen.darken(),
fontSize: 16,
),
),
DropdownButton<BarPattern>(
value: pattern,
items: BarPattern.values
.map((e) => DropdownMenuItem(
value: e,
child: Text(e.name),
))
.toList(),
onChanged: (BarPattern? value) {
setState(() {
pattern = value ?? BarPattern.none;
});
},
)
],
),
],
),
),
Expand Down Expand Up @@ -107,6 +173,15 @@ class BarChartSample1State extends State<BarChartSample1> {
);
}

FlSurfacePainter? _getPainter() {
return switch (pattern) {
BarPattern.stripes => stripesPainter,
BarPattern.squarePois => squarePoisPainter,
BarPattern.circlePois => circlePoisPainter,
BarPattern.none => null,
};
}

BarChartGroupData makeGroupData(
int x,
double y, {
Expand All @@ -123,6 +198,7 @@ class BarChartSample1State extends State<BarChartSample1> {
toY: isTouched ? y + 1 : y,
color: isTouched ? widget.touchedBarColor : barColor,
width: width,
surfacePainter: _getPainter(),
borderSide: isTouched
? BorderSide(color: widget.touchedBarColor.darken(80))
: const BorderSide(color: Colors.white, width: 0),
Expand Down
8 changes: 8 additions & 0 deletions lib/fl_chart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,11 @@ export 'src/chart/radar_chart/radar_chart.dart';
export 'src/chart/radar_chart/radar_chart_data.dart';
export 'src/chart/scatter_chart/scatter_chart.dart';
export 'src/chart/scatter_chart/scatter_chart_data.dart';
export 'src/pattern_painters/circle_pois_painter.dart';
export 'src/pattern_painters/square_pois_painter.dart';
export 'src/pattern_painters/stripes_painter.dart';
export 'src/shaders/circle_pois_shader.dart';
export 'src/shaders/fl_shader.dart';
export 'src/shaders/fl_surface_painter.dart';
export 'src/shaders/square_pois_shader.dart';
export 'src/shaders/stripes_shader.dart';
10 changes: 10 additions & 0 deletions lib/src/chart/bar_chart/bar_chart_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ class BarChartRodData with EquatableMixin {
BorderSide? borderSide,
BackgroundBarChartRodData? backDrawRodData,
List<BarChartRodStackItem>? rodStackItems,
this.surfacePainter,
}) : fromY = fromY ?? 0,
color =
color ?? ((color == null && gradient == null) ? Colors.cyan : null),
Expand All @@ -350,6 +351,10 @@ class BarChartRodData with EquatableMixin {
backDrawRodData = backDrawRodData ?? BackgroundBarChartRodData(),
rodStackItems = rodStackItems ?? const [];

/// If provided, this [BarChartRodData] will be drawn using this CustomPainter.
/// If null, the default rendering is used.
final FlSurfacePainter? surfacePainter;

/// [BarChart] renders rods vertically from [fromY].
final double fromY;

Expand Down Expand Up @@ -413,6 +418,7 @@ class BarChartRodData with EquatableMixin {
BorderSide? borderSide,
BackgroundBarChartRodData? backDrawRodData,
List<BarChartRodStackItem>? rodStackItems,
FlSurfacePainter? surfacePainter,
}) =>
BarChartRodData(
fromY: fromY ?? this.fromY,
Expand All @@ -426,6 +432,7 @@ class BarChartRodData with EquatableMixin {
borderSide: borderSide ?? this.borderSide,
backDrawRodData: backDrawRodData ?? this.backDrawRodData,
rodStackItems: rodStackItems ?? this.rodStackItems,
surfacePainter: surfacePainter ?? this.surfacePainter,
);

/// Lerps a [BarChartRodData] based on [t] value, check [Tween.lerp].
Expand All @@ -447,6 +454,8 @@ class BarChartRodData with EquatableMixin {
),
rodStackItems:
lerpBarChartRodStackList(a.rodStackItems, b.rodStackItems, t),
surfacePainter:
lerpSurfacePainter(a.surfacePainter, b.surfacePainter, t),
);

/// Used for equality check, see [EquatableMixin].
Expand All @@ -463,6 +472,7 @@ class BarChartRodData with EquatableMixin {
rodStackItems,
color,
gradient,
surfacePainter,
];
}

Expand Down
27 changes: 27 additions & 0 deletions lib/src/chart/bar_chart/bar_chart_painter.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:core';
import 'dart:math';
import 'dart:ui';

import 'package:fl_chart/fl_chart.dart';
import 'package:fl_chart/src/chart/base/axis_chart/axis_chart_painter.dart';
Expand Down Expand Up @@ -350,6 +351,32 @@ class BarChartPainter extends AxisChartPainter<BarChartData> {
_barStrokePaint,
);
}

// draw pattern painter overlay (on top of everything)
if (barRod.surfacePainter != null &&
(barRod.surfacePainter?.isInitialized ?? false)) {
final barRect = barRRect.getRect();
final recorder = PictureRecorder();
final customCanvas = Canvas(
recorder,
Rect.fromLTWH(0, 0, barRect.width, barRect.height),
)..clipRRect(
RRect.fromRectAndCorners(
Rect.fromLTWH(0, 0, barRect.width, barRect.height),
topLeft: barRRect.tlRadius,
topRight: barRRect.trRadius,
bottomLeft: barRRect.blRadius,
bottomRight: barRRect.brRadius,
),
);
barRod.surfacePainter!
.paint(customCanvas, Size(barRect.width, barRect.height));
final picture = recorder.endRecording();
canvasWrapper.canvas.save();
canvasWrapper.canvas.translate(barRect.left, barRect.top);
canvasWrapper.canvas.drawPicture(picture);
canvasWrapper.canvas.restore();
}
}
}
}
Expand Down
83 changes: 83 additions & 0 deletions lib/src/pattern_painters/circle_pois_painter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';

/// {@template circle_pois_pattern_painter}
/// A [CustomPainter] that fills the area with a polka dot pattern.
///
/// The pattern consists of evenly spaced circles (dots) arranged in rows and columns.
/// You can configure the dot color, number of dots per row, horizontal and vertical gaps, and margin.
///
/// The pattern is rendered using a custom [CirclePoisShader], which allows for efficient and flexible drawing.
///
/// ### Constructor parameters:
/// - [poisShader]: The shader responsible for rendering the polka dot pattern.
/// - [color]: The color of the dots (default: black).
/// - [dotsPerRow]: Number of dots per row (default: 2).
/// - [gap]: Horizontal gap between dots (default: 2.0).
/// - [verticalGap]: Vertical gap between dots (default: 2.0).
/// - [margin]: Margin around the pattern (default: 2.0).
///
/// ### Example usage:
/// ```dart
/// CustomPaint(
/// painter: CirclePoisPatternPainter(
/// poisShader: myShader,
/// color: Colors.red,
/// dotsPerRow: 4,
/// gap: 4.0,
/// verticalGap: 4.0,
/// margin: 2.0,
/// ),
/// )
/// ```
/// {@endtemplate}
class CirclePoisPatternPainter extends FlSurfacePainter {
CirclePoisPatternPainter({
required this.poisShader,
this.color = Colors.black,
this.dotsPerRow = 2,
this.gap = 2.0,
this.verticalGap = 2.0,
this.margin = 2.0,
}) : super(flShader: poisShader);

/// The color of the dots.
final Color color;

/// Number of dots per row.
final int dotsPerRow;

/// The gap between dots on X axis.
final double gap;

/// The gap between dots on Y axis.
final double verticalGap;

/// The margin around the pattern from the borders.
final double margin;

/// The shader used to render the polka dot pattern.
final CirclePoisShader poisShader;

@override
void paint(Canvas canvas, Size size) {
poisShader
..setFloat(0, size.width)
..setFloat(1, size.height)
..setFloat(2, color.r)
..setFloat(3, color.g)
..setFloat(4, color.b)
..setFloat(5, dotsPerRow.toDouble())
..setFloat(6, gap)
..setFloat(7, verticalGap)
..setFloat(8, margin);

canvas.drawRect(
Rect.fromLTWH(0, 0, size.width, size.height),
Paint()..shader = poisShader.shader,
);
}

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
83 changes: 83 additions & 0 deletions lib/src/pattern_painters/square_pois_painter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';

/// {@template square_pois_pattern_painter}
/// A [CustomPainter] that fills the area with a square polka dot pattern.
///
/// The pattern consists of evenly spaced squares arranged in rows and columns.
/// You can configure the color of the squares, the number of squares per row, the horizontal and vertical gaps between squares, and the margin around the pattern.
///
/// The pattern is rendered using a custom [SquarePoisShader], which allows for efficient and flexible drawing.
///
/// ### Constructor parameters:
/// - [poisShader]: The shader responsible for rendering the square polka dot pattern.
/// - [color]: The color of the squares (default: black).
/// - [squaresPerRow]: Number of squares per row (default: 3).
/// - [gap]: Horizontal gap between squares (default: 2.0).
/// - [verticalGap]: Vertical gap between squares (default: 2.0).
/// - [margin]: Margin around the pattern (default: 2.0).
///
/// ### Example usage:
/// ```dart
/// CustomPaint(
/// painter: SquarePoisPatternPainter(
/// poisShader: myShader,
/// color: Colors.green,
/// squaresPerRow: 4,
/// gap: 2.0,
/// verticalGap: 2.0,
/// margin: 2.0,
/// ),
/// )
/// ```
/// {@endtemplate}
class SquarePoisPatternPainter extends FlSurfacePainter {
SquarePoisPatternPainter({
required this.poisShader,
this.color = Colors.black,
this.squaresPerRow = 3,
this.gap = 2.0,
this.verticalGap = 2.0,
this.margin = 2.0,
}) : super(flShader: poisShader);

/// The color of the squares.
final Color color;

/// Number of squares per row.
final int squaresPerRow;

/// The gap between dots on X axis;
final double gap;

/// The gap between dots on Y axis;
final double verticalGap;

/// The margin around the pattern from the borders.
final double margin;

/// The shader used to render the polka dot pattern.
final SquarePoisShader poisShader;

@override
void paint(Canvas canvas, Size size) {
poisShader
..setFloat(0, size.width)
..setFloat(1, size.height)
..setFloat(2, color.r)
..setFloat(3, color.g)
..setFloat(4, color.b)
..setFloat(5, squaresPerRow.toDouble())
..setFloat(6, gap)
..setFloat(7, verticalGap)
..setFloat(8, margin);

canvas.drawRect(
Rect.fromLTWH(0, 0, size.width, size.height),
Paint()..shader = poisShader.shader,
);
}

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Loading