Skip to content

Commit

Permalink
Migrate radar_chart to use render object for drawing
Browse files Browse the repository at this point in the history
  • Loading branch information
imaNNeo committed Mar 17, 2021
1 parent 0cf0064 commit 0b515a3
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 125 deletions.
113 changes: 5 additions & 108 deletions lib/src/chart/radar_chart/radar_chart.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import 'package:fl_chart/src/chart/base/base_chart/base_chart_painter.dart';
import 'package:fl_chart/src/chart/base/base_chart/touch_input.dart';
import 'package:fl_chart/src/chart/radar_chart/radar_chart_data.dart';
import 'package:fl_chart/src/chart/radar_chart/radar_chart_painter.dart';
import 'package:fl_chart/src/utils/utils.dart';
import 'package:fl_chart/src/chart/radar_chart/radar_chart_renderer.dart';
import 'package:flutter/material.dart';

/// Renders a radar chart as a widget, using provided [RadarChartData].
Expand All @@ -28,121 +25,21 @@ class _RadarChartState extends AnimatedWidgetBaseState<RadarChart> {
/// it lerps between the old [RadarChartData] to the new one.
RadarChartDataTween? _radarChartDataTween;

/// this is used to map the touch events to [RadarChartResponse]
TouchHandler<RadarTouchResponse>? _touchHandler;

/// this is used to retrieve the chart size to handle the touches
final GlobalKey _chartKey = GlobalKey();

@override
Widget build(BuildContext context) {
final showingData = _getDate();
final touchData = showingData.radarTouchData;

return GestureDetector(
onLongPressStart: (d) {
final chartSize = _getChartSize();
if (chartSize == null || _touchHandler == null) {
return;
}

final response = _touchHandler!.handleTouch(FlLongPressStart(d.localPosition), chartSize);
touchData.touchCallback?.call(response);
},
onLongPressEnd: (d) {
final chartSize = _getChartSize();
if (chartSize == null || _touchHandler == null) {
return;
}

final response = _touchHandler!.handleTouch(FlLongPressEnd(d.localPosition), chartSize);
touchData.touchCallback?.call(response);
},
onLongPressMoveUpdate: (d) {
final chartSize = _getChartSize();
if (chartSize == null || _touchHandler == null) {
return;
}

final response = _touchHandler!.handleTouch(
FlLongPressMoveUpdate(d.localPosition),
chartSize,
);
touchData.touchCallback?.call(response);
},
onPanCancel: () {
final chartSize = _getChartSize();
if (chartSize == null || _touchHandler == null) {
return;
}

final response = _touchHandler!.handleTouch(
FlPanEnd(Offset.zero, const Velocity(pixelsPerSecond: Offset.zero)),
chartSize,
);
touchData.touchCallback?.call(response);
},
onPanEnd: (DragEndDetails details) {
final chartSize = _getChartSize();
if (chartSize == null || _touchHandler == null) {
return;
}

final response =
_touchHandler!.handleTouch(FlPanEnd(Offset.zero, details.velocity), chartSize);
touchData.touchCallback?.call(response);
},
onPanDown: (DragDownDetails details) {
final chartSize = _getChartSize();
if (chartSize == null || _touchHandler == null) {
return;
}

final response = _touchHandler!.handleTouch(FlPanStart(details.localPosition), chartSize);
touchData.touchCallback?.call(response);
},
onPanUpdate: (DragUpdateDetails details) {
final chartSize = _getChartSize();
if (chartSize == null || _touchHandler == null) {
return;
}

final response =
_touchHandler!.handleTouch(FlPanMoveUpdate(details.localPosition), chartSize);
touchData.touchCallback?.call(response);
},
child: CustomPaint(
key: _chartKey,
size: getDefaultSize(MediaQuery.of(context).size),
painter: RadarChartPainter(
_radarChartDataTween!.evaluate(animation),
showingData,
(touchHandler) {
setState(() {
_touchHandler = touchHandler;
});
},
textScale: MediaQuery.of(context).textScaleFactor,
),
),
return RadarChartLeaf(
data: _radarChartDataTween!.evaluate(animation),
targetData: showingData,
touchCallback: showingData.radarTouchData.touchCallback,
);
}

RadarChartData _getDate() {
return widget.data;
}

Size? _getChartSize() {
final containerRenderBox = _chartKey.currentContext?.findRenderObject();
if (containerRenderBox == null || containerRenderBox is! RenderBox) {
return null;
}
if (containerRenderBox.hasSize) {
return containerRenderBox.size;
}
return null;
}

@override
void forEachTween(visitor) {
_radarChartDataTween = visitor(
Expand Down
5 changes: 4 additions & 1 deletion lib/src/chart/radar_chart/radar_chart_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,9 @@ class RadarTouchData extends FlTouchData with EquatableMixin {
];
}

/// [RadarChart]'s touch callback.
typedef RadarTouchCallback = void Function(RadarTouchResponse);

/// Holds information about touch response in the [RadarTouchResponse].
///
/// You can override [RadarTouchData.touchCallback] to handle touch events,
Expand All @@ -373,7 +376,7 @@ class RadarTouchResponse extends BaseTouchResponse with EquatableMixin {
/// [touchInput] is the type of happened touch.
RadarTouchResponse(
RadarTouchedSpot? touchedSpot,
FlTouchInput touchInput,
PointerEvent touchInput,
) : touchedSpot = touchedSpot,
super(touchInput);

Expand Down
21 changes: 5 additions & 16 deletions lib/src/chart/radar_chart/radar_chart_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import 'package:flutter/material.dart';
import '../../../fl_chart.dart';

/// Paints [RadarChartData] in the canvas, it can be used in a [CustomPainter]
class RadarChartPainter extends BaseChartPainter<RadarChartData>
with TouchHandler<RadarTouchResponse> {
class RadarChartPainter extends BaseChartPainter<RadarChartData> {
final Paint _borderPaint, _backgroundPaint, _gridPaint, _tickPaint;
final Paint _graphPaint, _graphBorderPaint, _graphPointPaint;
final TextPainter _ticksTextPaint, _titleTextPaint;
Expand All @@ -22,16 +21,12 @@ class RadarChartPainter extends BaseChartPainter<RadarChartData>
/// during animation, then we should use it when we need to show
/// tooltips or something like that, because [data] is changing constantly.
///
/// [touchHandler] passes a [TouchHandler] to the parent,
/// parent will use it for touch handling flow.
///
/// [textScale] used for scaling texts inside the chart,
/// parent can use [MediaQuery.textScaleFactor] to respect
/// the system's font size.
RadarChartPainter(
RadarChartData data,
RadarChartData targetData,
Function(TouchHandler<RadarTouchResponse>) touchHandler, {
RadarChartData targetData, {
double textScale = 1,
}) : _backgroundPaint = Paint()
..color = data.radarBackgroundColor
Expand All @@ -54,9 +49,7 @@ class RadarChartPainter extends BaseChartPainter<RadarChartData>
_graphPointPaint = Paint(),
_ticksTextPaint = TextPainter(),
_titleTextPaint = TextPainter(),
super(data, targetData, textScale: textScale) {
touchHandler(this);
}
super(data, targetData, textScale: textScale);

/// Paints [RadarChartData] into the provided canvas.
@override
Expand Down Expand Up @@ -242,9 +235,8 @@ class RadarChartPainter extends BaseChartPainter<RadarChartData>
});
}

@override
RadarTouchResponse handleTouch(FlTouchInput touchInput, Size size) {
final touchedSpot = _getNearestTouchSpot(size, touchInput.getOffset(), dataSetsPosition);
RadarTouchResponse handleTouch(PointerEvent touchInput, Size size) {
final touchedSpot = _getNearestTouchSpot(size, touchInput.localPosition, dataSetsPosition);
return RadarTouchResponse(touchedSpot, touchInput);
}

Expand Down Expand Up @@ -318,9 +310,6 @@ class RadarChartPainter extends BaseChartPainter<RadarChartData>

return dataSetsPosition;
}

@override
bool shouldRepaint(RadarChartPainter oldDelegate) => oldDelegate.data != data;
}

class RadarDataSetsPosition {
Expand Down
99 changes: 99 additions & 0 deletions lib/src/chart/radar_chart/radar_chart_renderer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';

import 'radar_chart_painter.dart';

class RadarChartLeaf extends LeafRenderObjectWidget {
const RadarChartLeaf({Key? key, required this.data, required this.targetData, this.touchCallback})
: super(key: key);

final RadarChartData data, targetData;

final RadarTouchCallback? touchCallback;

@override
RenderRadarChart createRenderObject(BuildContext context) =>
RenderRadarChart(data, targetData, MediaQuery.of(context).textScaleFactor, touchCallback);

@override
void updateRenderObject(BuildContext context, RenderRadarChart renderObject) {
renderObject
..data = data
..targetData = targetData
..textScale = MediaQuery.of(context).textScaleFactor
..touchCallback = touchCallback;
}
}

class RenderRadarChart extends RenderBox {
RenderRadarChart(RadarChartData data, RadarChartData targetData, double textScale,
RadarTouchCallback? touchCallback)
: _data = data,
_targetData = targetData,
_textScale = textScale,
_touchCallback = touchCallback;

RadarChartData get data => _data;
RadarChartData _data;
set data(RadarChartData value) {
if (_data == value) return;
_data = value;
markNeedsPaint();
}

RadarChartData get targetData => _targetData;
RadarChartData _targetData;
set targetData(RadarChartData value) {
if (_targetData == value) return;
_targetData = value;
markNeedsPaint();
}

double get textScale => _textScale;
double _textScale;
set textScale(double value) {
if (_textScale == value) return;
_textScale = value;
markNeedsPaint();
}

RadarTouchCallback? _touchCallback;
set touchCallback(RadarTouchCallback? value) {
_touchCallback = value;
}

late RadarChartPainter _painter;

@override
void performLayout() {
size = computeDryLayout(constraints);
}

@override
Size computeDryLayout(BoxConstraints constraints) {
return Size(constraints.maxWidth, constraints.maxHeight);
}

@override
void paint(PaintingContext context, Offset offset) {
final canvas = context.canvas;
canvas.save();
canvas.translate(offset.dx, offset.dy);

_painter = RadarChartPainter(data, targetData, textScale: textScale);
_painter.paint(canvas, size);

canvas.restore();
}

@override
bool hitTestSelf(Offset position) => true;

@override
void handleEvent(PointerEvent event, covariant BoxHitTestEntry entry) {
_touchCallback?.call(_painter.handleTouch(event, size));
}
}

0 comments on commit 0b515a3

Please sign in to comment.