diff --git a/.idea/misc.xml b/.idea/misc.xml index f8c5fc9..d002923 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,8 @@ - + + \ No newline at end of file diff --git a/example/android/build.gradle b/example/android/build.gradle index 58a8c74..713d7f6 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/example/lib/main.dart b/example/lib/main.dart index 8eeecab..9f51303 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -28,7 +28,7 @@ class MyApp extends StatelessWidget { primaryColor: const Color(0xFF0C00C5), elevatedButtonTheme: ElevatedButtonThemeData( style: ButtonStyle( - backgroundColor: MaterialStateProperty.all( + backgroundColor: WidgetStateProperty.all( const Color(0xFF0C00C5), ), ), diff --git a/example/lib/sample1.dart b/example/lib/sample1.dart index 3b6a494..8646823 100644 --- a/example/lib/sample1.dart +++ b/example/lib/sample1.dart @@ -7,6 +7,34 @@ import 'package:intl/intl.dart'; class Sample1 extends StatelessWidget { const Sample1({Key? key}) : super(key: key); + List> get spotsList => [ + [ + const LineChartSpot(0.0, 3.7806739187454177), + const LineChartSpot(1.0, 3.5991452613605475), + const LineChartSpot(2.0, 2.6534102643618773), + const LineChartSpot(3.0, 3.495803225491705), + const LineChartSpot(4.0, 3.2687418283157106), + ], [ + const LineChartSpot(0.0, 7.748415631185418), + const LineChartSpot(1.0, 6.876026011448352), + const LineChartSpot(2.0, 7.62484251992211), + const LineChartSpot(3.0, 8.320701383214557), + const LineChartSpot(4.0, 8.34446381632569), + ], [ + const LineChartSpot(0.0, 1.8119931114678556), + const LineChartSpot(1.0, 2.119198907823737), + const LineChartSpot(2.0, 2.907415228279169), + const LineChartSpot(3.0, 2.0087045168815028), + const LineChartSpot(4.0, 2.675884068457985), + ],[ + const LineChartSpot(0.0, 3.4873266523791857), + const LineChartSpot(1.0, 4.446586791105186), + const LineChartSpot(2.0, 4.223622629086266), + const LineChartSpot(3.0, 4.191570249574648), + const LineChartSpot(4.0, 4.7799742234986695), + ], + ]; + @override Widget build(BuildContext context) { final today = DateTime.now(); @@ -20,7 +48,6 @@ class Sample1 extends StatelessWidget { mainAxisSpacing: 16, ), itemBuilder: (context, index) { - final spotsList = createSpotsList(spotsNum: 1, length: 5); return Container( decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade300), @@ -32,16 +59,17 @@ class Sample1 extends StatelessWidget { AspectRatio( aspectRatio: 16 / 9, child: LineChart( + margin: EdgeInsets.symmetric(vertical: 10, horizontal: 5), data: LineChartData( lineBarsData: spotsList - .map((spots) => LineChartBarData( - spots: spots, - color: - index % 2 == 0 ? Colors.orange : Colors.cyan, - point: const LineChartPoint( - type: LineChartPointType.circle, - ), - )) + .map((spots) => + LineChartBarData( + spots: spots, + color: index % 2 == 0 ? Colors.orange : Colors.cyan, + point: const LineChartPoint( + type: LineChartPointType.circle, + ), + )) .toList(), area: const LineChartArea( borderRadius: BorderRadius.vertical( diff --git a/lib/src/line_chart/line_chart.dart b/lib/src/line_chart/line_chart.dart index 04f58bc..d142605 100644 --- a/lib/src/line_chart/line_chart.dart +++ b/lib/src/line_chart/line_chart.dart @@ -22,6 +22,7 @@ class LineChart extends ImplicitlyAnimatedWidget { this.tooltip, Duration swapAnimationDuration = const Duration(milliseconds: 250), Curve swapAnimationCurve = Curves.linear, + this.margin = EdgeInsets.zero, }) : super( key: key, duration: swapAnimationDuration, @@ -34,6 +35,8 @@ class LineChart extends ImplicitlyAnimatedWidget { /// The tooltip displayed when tapping the line chart. /// When null, the tooltip is disabled. final LineChartTooltip? tooltip; + /// + final EdgeInsets margin; @override AnimatedWidgetBaseState createState() => _LineChartState(); @@ -56,6 +59,7 @@ class _LineChartState extends AnimatedWidgetBaseState { tooltip: widget.tooltip, width: constraints.maxWidth, height: constraints.maxHeight, + margin: widget.margin, ); }, ), @@ -82,21 +86,23 @@ class _Chart extends StatelessWidget { this.tooltip, required this.width, required this.height, + this.margin = EdgeInsets.zero, }) : super(key: key); final NormalizedLineChartData data; final LineChartTooltip? tooltip; final double width; final double height; + final EdgeInsets margin; @override Widget build(BuildContext context) { final xLabel = data.xAxis.label; final yLabel = data.yAxis.label; - final textScaleFactor = MediaQuery.of(context).textScaleFactor; + final textScale = MediaQuery.of(context).textScaler; final xAxisHeight = data.xAxis.label?.height ?? - _calculateLabelMaxOffset(xLabel, textScaleFactor).dy; + _calculateLabelMaxOffset(xLabel, textScale).dy; final yAxisWidth = data.yAxis.label?.width ?? - _calculateLabelMaxOffset(yLabel, textScaleFactor).dx; + _calculateLabelMaxOffset(yLabel, textScale).dx; return Stack( clipBehavior: Clip.none, @@ -114,7 +120,7 @@ class _Chart extends StatelessWidget { range: data.xAxis.range, yAxisWidth: yAxisWidth, padding: data.area?.padding, - textScaleFactor: textScaleFactor, + textScale: textScale, ), child: const SizedBox.expand(), ), @@ -131,7 +137,7 @@ class _Chart extends StatelessWidget { painter: LineChartYLabelPainter( label: yLabel, range: data.yAxis.range, - textScaleFactor: textScaleFactor, + textScale: textScale, ), child: const SizedBox.expand(), ), @@ -146,6 +152,7 @@ class _Chart extends StatelessWidget { child: CustomPaint( painter: LineChartPainter( data: data, + margin: margin, ), child: const SizedBox.expand(), ), @@ -161,14 +168,14 @@ class _Chart extends StatelessWidget { yAxisWidth: yAxisWidth, positionX: data.xAxis.range.position, positionY: data.yAxis.range.position, - ) + ), ], ); } static Offset _calculateLabelMaxOffset( LineChartLabel? label, - double textScaleFactor, + TextScaler textScale, ) { if (label == null || label.texts == null || label.texts!.isEmpty) { return Offset.zero; @@ -177,7 +184,7 @@ class _Chart extends StatelessWidget { (labelText) => createTextPainter( text: labelText.text, style: label.style, - textScaleFactor: textScaleFactor, + textScale: textScale, ), ); final maxWidth = diff --git a/lib/src/line_chart/line_chart_label_painter.dart b/lib/src/line_chart/line_chart_label_painter.dart index 8ace14c..5a3362f 100644 --- a/lib/src/line_chart/line_chart_label_painter.dart +++ b/lib/src/line_chart/line_chart_label_painter.dart @@ -14,7 +14,7 @@ class LineChartXLabelPainter extends CustomPainter { required this.range, required this.yAxisWidth, required this.padding, - required this.textScaleFactor, + required this.textScale, }); /// The label to draw. @@ -30,7 +30,7 @@ class LineChartXLabelPainter extends CustomPainter { final EdgeInsets? padding; /// The text scale factor. - final double textScaleFactor; + final TextScaler textScale; @override void paint(Canvas canvas, Size size) { @@ -42,7 +42,7 @@ class LineChartXLabelPainter extends CustomPainter { final textPainter = createTextPainter( text: labelText.text, style: label.style, - textScaleFactor: textScaleFactor, + textScale: textScale, ); final pixelX = getPixelX(rect, range.ratio(labelText.position)); if (label.hideOverflowedLabels) { @@ -81,7 +81,7 @@ class LineChartXLabelPainter extends CustomPainter { oldDelegate.range != range || oldDelegate.yAxisWidth != yAxisWidth || oldDelegate.padding != padding || - oldDelegate.textScaleFactor != textScaleFactor; + oldDelegate.textScale != textScale; } } @@ -91,7 +91,7 @@ class LineChartYLabelPainter extends CustomPainter { LineChartYLabelPainter({ required this.label, required this.range, - required this.textScaleFactor, + required this.textScale, }); /// The label to draw. @@ -101,7 +101,7 @@ class LineChartYLabelPainter extends CustomPainter { final LineChartYRangeInternal range; /// The text scale factor. - final double textScaleFactor; + final TextScaler textScale; @override void paint(Canvas canvas, Size size) { @@ -112,7 +112,7 @@ class LineChartYLabelPainter extends CustomPainter { final textPainter = createTextPainter( text: labelText.text, style: label.style, - textScaleFactor: textScaleFactor, + textScale: textScale, ); canvas ..save() @@ -134,6 +134,6 @@ class LineChartYLabelPainter extends CustomPainter { bool shouldRepaint(covariant LineChartYLabelPainter oldDelegate) { return oldDelegate.label != label || oldDelegate.range != range || - oldDelegate.textScaleFactor != textScaleFactor; + oldDelegate.textScale != textScale; } } diff --git a/lib/src/line_chart/line_chart_painter.dart b/lib/src/line_chart/line_chart_painter.dart index 5e5ae3d..6b49144 100644 --- a/lib/src/line_chart/line_chart_painter.dart +++ b/lib/src/line_chart/line_chart_painter.dart @@ -12,11 +12,15 @@ class LineChartPainter extends CustomPainter { /// Creates [LineChartPainter]. LineChartPainter({ required this.data, + this.margin = EdgeInsets.zero, }); /// The normalized data for painting the line chart. final NormalizedLineChartData data; + /// To Apply margin around the chart plot points + final EdgeInsets margin; + static final Map> _cachedOffsets = {}; @override @@ -54,7 +58,7 @@ class LineChartPainter extends CustomPainter { } for (final barData in data.lineBarsData) { - _drawChart(canvas, rect, barData); + _drawChart(canvas, rect, barData, margin); } for (final barData in data.lineBarsData) { @@ -64,6 +68,7 @@ class LineChartPainter extends CustomPainter { rect, barData, barData.point?.fillColor ?? data.area?.color, + margin, ); } } @@ -87,19 +92,30 @@ class LineChartPainter extends CustomPainter { /// Creates the points for the line chart. static List createPlotPoints( - Rect rect, - LineChartBarData lineBarData, - ) { + Rect rect, + LineChartBarData lineBarData, + EdgeInsets margin, + ) { if (_cachedOffsets.containsKey(lineBarData)) { return _cachedOffsets[lineBarData]!; } - final points = []; - for (final spot in lineBarData.spots) { - final x = getPixelX(rect, spot.x); - final y = getPixelY(rect, spot.y); + var padding = lineBarData.point?.size ?? 0.0; + final adjustedTop = rect.top + margin.top; + final adjustedBottom = rect.bottom - margin.bottom; + for (var i = 0; i < lineBarData.spots.length; i++) { + final spot = lineBarData.spots[i]; + final x = (i == 0) + ? getPixelX(rect, spot.x) + padding + margin.left + : (i == lineBarData.spots.length - 1) + ? getPixelX(rect, spot.x) - padding - margin.right + : getPixelX(rect, spot.x); + final y = adjustedTop + + ((getPixelY(rect, spot.y) - rect.top) / rect.height) * + (adjustedBottom - adjustedTop); points.add(Offset(x, y)); } + _cachedOffsets[lineBarData] = points; return points; } @@ -143,14 +159,14 @@ class LineChartPainter extends CustomPainter { } } - static void _drawChart(Canvas canvas, Rect rect, LineChartBarData barData) { + static void _drawChart(Canvas canvas, Rect rect, LineChartBarData barData, EdgeInsets margin) { final paintStroke = Paint() ..color = barData.color ..strokeWidth = barData.strokeWidth ..style = PaintingStyle.stroke ..strokeCap = barData.strokeCap; - final points = createPlotPoints(rect, barData); + final points = createPlotPoints(rect, barData, margin); if (points.length == 1) { canvas.drawCircle( @@ -178,6 +194,7 @@ class LineChartPainter extends CustomPainter { Rect rect, LineChartBarData barData, Color? color, + EdgeInsets margin, ) { final paintCircle = Paint() ..color = color ?? Colors.transparent @@ -189,7 +206,7 @@ class LineChartPainter extends CustomPainter { ..style = PaintingStyle.stroke ..strokeCap = barData.strokeCap; - final points = createPlotPoints(rect, barData); + final points = createPlotPoints(rect, barData, margin); for (final point in points) { canvas.drawCircle(point, barData.point!.size, paintCircle); canvas.drawCircle(point, barData.point!.size, paintBorder); diff --git a/lib/src/utils/paint.dart b/lib/src/utils/paint.dart index fe15908..8d75b3f 100644 --- a/lib/src/utils/paint.dart +++ b/lib/src/utils/paint.dart @@ -4,12 +4,12 @@ import 'package:flutter/material.dart'; TextPainter createTextPainter({ required String text, required TextStyle? style, - required double textScaleFactor, + required TextScaler textScale, }) => TextPainter( text: TextSpan(text: text, style: style), textDirection: TextDirection.ltr, - textScaleFactor: textScaleFactor, + textScaler: textScale, maxLines: 1, )..layout();