From fd9c4191c66d59a9ceeb38267dd1189a36600af8 Mon Sep 17 00:00:00 2001 From: Bwolf Date: Mon, 21 Feb 2022 22:22:16 -0300 Subject: [PATCH] Add Touchable Circular Chart --- lib/flutter_circular_chart_two.dart | 4 +- lib/src/animated_circular_chart.dart | 33 +--- lib/src/circular_chart.dart | 5 +- lib/src/consts.dart | 18 ++ lib/src/entry.dart | 10 +- lib/src/painter.dart | 6 +- lib/src/segment.dart | 12 +- lib/src/stack.dart | 20 ++- lib/src/touchable_circular_chart.dart | 249 ++++++++++++++++++++++++++ lib/src/touchable_painter.dart | 102 +++++++++++ pubspec.lock | 52 ++++-- pubspec.yaml | 1 + 12 files changed, 444 insertions(+), 68 deletions(-) create mode 100644 lib/src/consts.dart create mode 100644 lib/src/touchable_circular_chart.dart create mode 100644 lib/src/touchable_painter.dart diff --git a/lib/flutter_circular_chart_two.dart b/lib/flutter_circular_chart_two.dart index abc0542..a83ccc9 100644 --- a/lib/flutter_circular_chart_two.dart +++ b/lib/flutter_circular_chart_two.dart @@ -1,5 +1,7 @@ library flutter_circular_chart_two; -export 'src/circular_chart.dart'; export 'src/animated_circular_chart.dart'; +export 'src/circular_chart.dart'; +export 'src/consts.dart'; export 'src/entry.dart'; +export 'src/touchable_circular_chart.dart'; diff --git a/lib/src/animated_circular_chart.dart b/lib/src/animated_circular_chart.dart index 16475f7..66ce58c 100644 --- a/lib/src/animated_circular_chart.dart +++ b/lib/src/animated_circular_chart.dart @@ -1,27 +1,9 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_circular_chart_two/src/circular_chart.dart'; import 'package:flutter_circular_chart_two/src/entry.dart'; import 'package:flutter_circular_chart_two/src/painter.dart'; -// The default chart tween animation duration. -const Duration _kDuration = const Duration(milliseconds: 300); -// The default angle the chart is oriented at. -const double _kStartAngle = -90.0; - -enum CircularChartType { - Pie, - Radial, -} - -/// Determines how the ends of a chart's segments should be drawn. -enum SegmentEdgeStyle { - /// Segments begin and end with a flat edge. - flat, - - /// Segments begin and end with a semi-circle. - round, -} +import 'consts.dart'; class AnimatedCircularChart extends StatefulWidget { AnimatedCircularChart({ @@ -29,10 +11,10 @@ class AnimatedCircularChart extends StatefulWidget { @required this.size, @required this.initialChartData, this.chartType = CircularChartType.Radial, - this.duration = _kDuration, + this.duration = kDuration, this.percentageValues = false, this.holeRadius, - this.startAngle = _kStartAngle, + this.startAngle = kStartAngle, this.holeLabel, this.labelStyle, this.edgeStyle = SegmentEdgeStyle.flat, @@ -117,13 +99,11 @@ class AnimatedCircularChart extends StatefulWidget { assert(context != null); assert(nullOk != null); - final AnimatedCircularChartState result = - context.findAncestorStateOfType(); + final AnimatedCircularChartState result = context.findAncestorStateOfType(); if (nullOk || result != null) return result; - throw FlutterError( - 'AnimatedCircularChart.of() called with a context that does not contain a AnimatedCircularChart.\n' + throw FlutterError('AnimatedCircularChart.of() called with a context that does not contain a AnimatedCircularChart.\n' 'No AnimatedCircularChart ancestor could be found starting from the context that was passed to AnimatedCircularChart.of(). ' 'This can happen when the context provided is from the same StatefulWidget that ' 'built the AnimatedCircularChart.\n' @@ -149,8 +129,7 @@ class AnimatedCircularChart extends StatefulWidget { /// ... /// chartKey.currentState.updateData(newData); /// ``` -class AnimatedCircularChartState extends State - with TickerProviderStateMixin { +class AnimatedCircularChartState extends State with TickerProviderStateMixin { CircularChartTween _tween; AnimationController _animation; final Map _stackRanks = {}; diff --git a/lib/src/circular_chart.dart b/lib/src/circular_chart.dart index 5383a37..5a3a24c 100644 --- a/lib/src/circular_chart.dart +++ b/lib/src/circular_chart.dart @@ -1,11 +1,10 @@ -import 'package:flutter/animation.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_circular_chart_two/src/animated_circular_chart.dart'; import 'package:flutter_circular_chart_two/src/entry.dart'; import 'package:flutter_circular_chart_two/src/stack.dart'; import 'package:flutter_circular_chart_two/src/tween.dart'; +import 'consts.dart'; + class CircularChart { static const double _kStackWidthFraction = 0.75; diff --git a/lib/src/consts.dart b/lib/src/consts.dart new file mode 100644 index 0000000..827513b --- /dev/null +++ b/lib/src/consts.dart @@ -0,0 +1,18 @@ +// The default chart tween animation duration. +const Duration kDuration = const Duration(milliseconds: 300); +// The default angle the chart is oriented at. +const double kStartAngle = -90.0; + +enum CircularChartType { + Pie, + Radial, +} + +/// Determines how the ends of a chart's segments should be drawn. +enum SegmentEdgeStyle { + /// Segments begin and end with a flat edge. + flat, + + /// Segments begin and end with a semi-circle. + round, +} diff --git a/lib/src/entry.dart b/lib/src/entry.dart index 04ab966..6b83a1d 100644 --- a/lib/src/entry.dart +++ b/lib/src/entry.dart @@ -9,7 +9,12 @@ import 'dart:ui'; /// [value], what proportion of a stack this corresponds to depends on the [percentageValues] /// property of the chart. class CircularSegmentEntry { - const CircularSegmentEntry(this.value, this.color, {this.rankKey}); + const CircularSegmentEntry( + this.value, + this.color, { + this.rankKey, + this.id, + }); /// The value of this data point, defines the sweep angle of the arc /// that is drawn. If the chart being drawn has [percentageValues] set to false @@ -25,6 +30,9 @@ class CircularSegmentEntry { /// transitioning between data points. final String rankKey; + ///Segment Identifier + final String id; + String toString() { return '$rankKey: $value $color'; } diff --git a/lib/src/painter.dart b/lib/src/painter.dart index b4ee61a..878b174 100644 --- a/lib/src/painter.dart +++ b/lib/src/painter.dart @@ -1,10 +1,11 @@ import 'dart:math' as Math; import 'package:flutter/material.dart'; -import 'package:flutter_circular_chart_two/src/animated_circular_chart.dart'; import 'package:flutter_circular_chart_two/src/circular_chart.dart'; import 'package:flutter_circular_chart_two/src/stack.dart'; +import 'consts.dart'; + class AnimatedCircularChartPainter extends CustomPainter { AnimatedCircularChartPainter(this.animation, this.labelPainter) : super(repaint: animation); @@ -53,8 +54,7 @@ void _paintLabel(Canvas canvas, Size size, TextPainter labelPainter) { void _paintChart(Canvas canvas, Size size, CircularChart chart) { final Paint segmentPaint = Paint() - ..style = - chart.chartType == CircularChartType.Radial ? PaintingStyle.stroke : PaintingStyle.fill + ..style = chart.chartType == CircularChartType.Radial ? PaintingStyle.stroke : PaintingStyle.fill ..strokeCap = chart.edgeStyle == SegmentEdgeStyle.round ? StrokeCap.round : StrokeCap.butt; for (final CircularChartStack stack in chart.stacks) { diff --git a/lib/src/segment.dart b/lib/src/segment.dart index 24ff8cc..cedf718 100644 --- a/lib/src/segment.dart +++ b/lib/src/segment.dart @@ -4,11 +4,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_circular_chart_two/src/tween.dart'; class CircularChartSegment extends MergeTweenable { - CircularChartSegment(this.rank, this.sweepAngle, this.color); + CircularChartSegment(this.rank, this.sweepAngle, this.color, {this.id}); final int rank; final double sweepAngle; final Color color; + final String id; @override CircularChartSegment get empty => CircularChartSegment(rank, 0.0, color); @@ -17,23 +18,22 @@ class CircularChartSegment extends MergeTweenable { bool operator <(CircularChartSegment other) => rank < other.rank; @override - Tween tweenTo(CircularChartSegment other) => - CircularChartSegmentTween(this, other); + Tween tweenTo(CircularChartSegment other) => CircularChartSegmentTween(this, other); - static CircularChartSegment lerp(CircularChartSegment begin, CircularChartSegment end, double t) { + static CircularChartSegment lerp(CircularChartSegment begin, CircularChartSegment end, double t, {String id}) { assert(begin.rank == end.rank); return CircularChartSegment( begin.rank, lerpDouble(begin.sweepAngle, end.sweepAngle, t), Color.lerp(begin.color, end.color, t), + id: id, ); } } class CircularChartSegmentTween extends Tween { - CircularChartSegmentTween(CircularChartSegment begin, CircularChartSegment end) - : super(begin: begin, end: end) { + CircularChartSegmentTween(CircularChartSegment begin, CircularChartSegment end) : super(begin: begin, end: end) { assert(begin.rank == end.rank); } diff --git a/lib/src/stack.dart b/lib/src/stack.dart index bfdcd07..3f0985c 100644 --- a/lib/src/stack.dart +++ b/lib/src/stack.dart @@ -43,7 +43,12 @@ class CircularChartStack implements MergeTweenable { double sweepAngle = (entries[i].value / valueSum * _kMaxAngle) + previousSweepAngle; previousSweepAngle = sweepAngle; int rank = entryRanks[entries[i].rankKey] ?? i; - return CircularChartSegment(rank, sweepAngle, entries[i].color); + return CircularChartSegment( + rank, + sweepAngle, + entries[i].color, + id: entries[i].id, + ); }); return CircularChartStack( @@ -56,24 +61,23 @@ class CircularChartStack implements MergeTweenable { } @override - CircularChartStack get empty => - CircularChartStack(rank, radius, 0.0, startAngle, []); + CircularChartStack get empty => CircularChartStack(rank, radius, 0.0, startAngle, []); @override bool operator <(CircularChartStack other) => rank < other.rank; @override - Tween tweenTo(CircularChartStack other) => - CircularChartStackTween(this, other); + Tween tweenTo(CircularChartStack other) => CircularChartStackTween(this, other); } class CircularChartStackTween extends Tween { - CircularChartStackTween(CircularChartStack begin, CircularChartStack end) - : _circularSegmentsTween = MergeTween(begin.segments, end.segments), + CircularChartStackTween( + CircularChartStack begin, + CircularChartStack end, + ) : _circularSegmentsTween = MergeTween(begin.segments, end.segments), super(begin: begin, end: end) { assert(begin.rank == end.rank); } - final MergeTween _circularSegmentsTween; @override diff --git a/lib/src/touchable_circular_chart.dart b/lib/src/touchable_circular_chart.dart new file mode 100644 index 0000000..abf653f --- /dev/null +++ b/lib/src/touchable_circular_chart.dart @@ -0,0 +1,249 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_circular_chart_two/src/circular_chart.dart'; +import 'package:flutter_circular_chart_two/src/consts.dart'; +import 'package:flutter_circular_chart_two/src/entry.dart'; +import 'package:flutter_circular_chart_two/src/touchable_painter.dart'; +import 'package:touchable/touchable.dart'; + +class TouchableCircularChart extends StatefulWidget { + TouchableCircularChart({ + Key key, + @required this.size, + @required this.initialChartData, + this.chartType = CircularChartType.Radial, + this.duration = kDuration, + this.percentageValues = false, + this.holeRadius, + this.startAngle = kStartAngle, + this.holeLabel, + this.labelStyle, + this.edgeStyle = SegmentEdgeStyle.flat, + this.onSelectSegment, + }) : assert(size != null), + super(key: key); + + final Function(String id) onSelectSegment; + + /// The size of the bounding box this chart will be constrained to. + final Size size; + + /// The data used to build the chart displayed when the widget is first placed. + /// Each [CircularStackEntry] in the list defines an individual stack of data: + /// For a Pie chart that corresponds to individual slices in the chart. + /// For a Radial chart it corresponds to individual segments on the same arc. + /// + /// If length > 1 and [chartType] is [CircularChartType.Radial] then the stacks + /// will be grouped together as concentric circles. + /// + /// If [chartType] is [CircularChartType.Pie] then length cannot be > 1. + final List initialChartData; + + /// The type of chart to be rendered. + /// Use [CircularChartType.Pie] for a circle divided into slices for each entry. + /// Use [CircularChartType.Radial] for one or more arcs with a hole in the center. + final CircularChartType chartType; + + /// The duration of the chart animation when [TouchableCircularChartState.updateData] + /// is called. + final Duration duration; + + /// If true then the data values provided will determine what percentage of the circle + /// this segment occupies [i.e: a value of 100 is the full circle]. + /// + /// Otherwise the data is normalized such that the sum of all values in each stack + /// is considered to encompass 100% of the circle. + /// + /// defaults to false. + final bool percentageValues; + + /// For [CircularChartType.Radial] charts this defines the circle in the center + /// of the canvas, around which the chart is drawn. If not provided then it will + /// be automatically calculated to accommodate all the data. + /// + /// Has no effect in [CircularChartType.Pie] charts. + final double holeRadius; + + /// The chart gets drawn and animates clockwise from [startAngle], defaulting to the + /// top/center point or -90.0. In terms of a clock face these would be: + /// - -90.0: 12 o'clock + /// - 0.0: 3 o'clock + /// - 90.0: 6 o'clock + /// - 180.0: 9 o'clock + final double startAngle; + + /// A label to show in the hole of a radial chart. + /// + /// It is used to display the value of a radial slider, and it is displayed + /// in the center of the chart's hole. + /// + /// See also [labelStyle] which is used to render the label. + final String holeLabel; + + /// The style used when rendering the [holeLabel]. + /// + /// Defaults to the active [ThemeData]'s + /// [ThemeData.textTheme.body2] text style. + final TextStyle labelStyle; + + /// The type of segment edges to be drawn. + /// + /// Defaults to [SegmentEdgeStyle.flat]. + final SegmentEdgeStyle edgeStyle; + + /// The state from the closest instance of this class that encloses the given context. + /// + /// This method is typically used by [TouchableCircularChart] item widgets that insert or + /// remove items in response to user input. + /// + /// ```dart + /// TouchableCircularChartState TouchableCircularChart = TouchableCircularChart.of(context); + /// ``` + static TouchableCircularChartState of(BuildContext context, {bool nullOk: false}) { + assert(context != null); + assert(nullOk != null); + + final TouchableCircularChartState result = context.findAncestorStateOfType(); + + if (nullOk || result != null) return result; + + throw FlutterError('TouchableCircularChart.of() called with a context that does not contain a TouchableCircularChart.\n' + 'No TouchableCircularChart ancestor could be found starting from the context that was passed to TouchableCircularChart.of(). ' + 'This can happen when the context provided is from the same StatefulWidget that ' + 'built the TouchableCircularChart.\n' + 'The context used was:\n' + ' $context'); + } + + @override + TouchableCircularChartState createState() => TouchableCircularChartState(); +} + +/// The state for a circular chart that animates when its data is updated. +/// +/// When the chart data changes with [updateData] an animation begins running. +/// +/// An app that needs to update its data in response to an event +/// can refer to the [TouchableCircularChart]'s state with a global key: +/// +/// ```dart +/// GlobalKey chartKey = GlobalKey(); +/// ... +/// TouchableCircularChart(key: chartKey, ...); +/// ... +/// chartKey.currentState.updateData(newData); +/// ``` +class TouchableCircularChartState extends State with TickerProviderStateMixin { + CircularChartTween _tween; + AnimationController _animation; + final Map _stackRanks = {}; + final Map _entryRanks = {}; + final TextPainter _labelPainter = TextPainter(); + + @override + void initState() { + super.initState(); + _animation = AnimationController( + duration: widget.duration, + vsync: this, + ); + + _assignRanks(widget.initialChartData); + + _tween = CircularChartTween( + CircularChart.empty(chartType: widget.chartType), + CircularChart.fromData( + size: widget.size, + data: widget.initialChartData, + chartType: widget.chartType, + stackRanks: _stackRanks, + entryRanks: _entryRanks, + percentageValues: widget.percentageValues, + holeRadius: widget.holeRadius, + startAngle: widget.startAngle, + edgeStyle: widget.edgeStyle, + ), + ); + _animation.forward(); + } + + @override + void didUpdateWidget(TouchableCircularChart oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.holeLabel != widget.holeLabel || oldWidget.labelStyle != widget.labelStyle) { + _updateLabelPainter(); + } + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _updateLabelPainter(); + } + + @override + void dispose() { + _animation.dispose(); + super.dispose(); + } + + void _assignRanks(List data) { + for (CircularStackEntry stackEntry in data) { + _stackRanks.putIfAbsent(stackEntry.rankKey, () => _stackRanks.length); + for (CircularSegmentEntry entry in stackEntry.entries) { + _entryRanks.putIfAbsent(entry.rankKey, () => _entryRanks.length); + } + } + } + + void _updateLabelPainter() { + if (widget.holeLabel != null) { + TextStyle _labelStyle = widget.labelStyle ?? Theme.of(context).textTheme.bodyText1; + _labelPainter + ..text = TextSpan(style: _labelStyle, text: widget.holeLabel) + ..textDirection = Directionality.of(context) + ..textScaleFactor = MediaQuery.of(context).textScaleFactor + ..layout(); + } else { + _labelPainter.text = null; + } + } + + /// Update the data this chart represents and start an animation that will tween + /// between the old data and this one. + void updateData(List data) { + _assignRanks(data); + + setState(() { + _tween = CircularChartTween( + _tween.evaluate(_animation), + CircularChart.fromData( + size: widget.size, + data: data, + chartType: widget.chartType, + stackRanks: _stackRanks, + entryRanks: _entryRanks, + percentageValues: widget.percentageValues, + holeRadius: widget.holeRadius, + startAngle: widget.startAngle, + edgeStyle: widget.edgeStyle, + ), + ); + _animation.forward(from: 0.0); + }); + } + + @override + Widget build(BuildContext context) { + return CanvasTouchDetector( + builder: (context) => CustomPaint( + size: widget.size, + painter: TouchableCircularChartPainter( + _tween.animate(_animation), + widget.holeLabel != null ? _labelPainter : null, + context, + widget.onSelectSegment, + ), + ), + ); + } +} diff --git a/lib/src/touchable_painter.dart b/lib/src/touchable_painter.dart new file mode 100644 index 0000000..84c8f2e --- /dev/null +++ b/lib/src/touchable_painter.dart @@ -0,0 +1,102 @@ +import 'dart:math' as Math; + +import 'package:flutter/material.dart'; +import 'package:flutter_circular_chart_two/src/circular_chart.dart'; +import 'package:flutter_circular_chart_two/src/stack.dart'; +import 'package:touchable/touchable.dart'; + +import 'consts.dart'; + +class TouchableCircularChartPainter extends CustomPainter { + TouchableCircularChartPainter( + this.animation, + this.labelPainter, + this.context, + this.onSelectSegment, + ) : super(repaint: animation); + + final Animation animation; + final TextPainter labelPainter; + final BuildContext context; + final Function(String id) onSelectSegment; + @override + void paint(Canvas canvas, Size size) { + _paintLabel(canvas, size, labelPainter); + _paintChart(canvas, size, animation.value, context, onSelectSegment); + } + + @override + bool shouldRepaint(TouchableCircularChartPainter old) => false; +} + +class CircularChartPainter extends CustomPainter { + CircularChartPainter(this.chart, this.labelPainter, this.context, this.onSelectSegment); + + final CircularChart chart; + final TextPainter labelPainter; + final BuildContext context; + final Function(String id) onSelectSegment; + @override + void paint(Canvas canvas, Size size) { + _paintLabel(canvas, size, labelPainter); + _paintChart( + canvas, + size, + chart, + context, + this.onSelectSegment, + ); + } + + @override + bool shouldRepaint(CircularChartPainter old) => false; +} + +const double _kRadiansPerDegree = Math.pi / 180; + +void _paintLabel(Canvas canvas, Size size, TextPainter labelPainter) { + if (labelPainter != null) { + labelPainter.paint( + canvas, + Offset( + size.width / 2 - labelPainter.width / 2, + size.height / 2 - labelPainter.height / 2, + ), + ); + } +} + +void _paintChart( + Canvas canvas, + Size size, + CircularChart chart, + BuildContext context, + Function(String id) onSelectSegment, +) { + final Paint segmentPaint = Paint() + ..style = chart.chartType == CircularChartType.Radial ? PaintingStyle.stroke : PaintingStyle.fill + ..strokeCap = chart.edgeStyle == SegmentEdgeStyle.round ? StrokeCap.round : StrokeCap.butt; + + var myCanvas = TouchyCanvas(context, canvas); + + for (final CircularChartStack stack in chart.stacks) { + for (final segment in stack.segments) { + segmentPaint.color = segment.color; + segmentPaint.strokeWidth = stack.width; + + myCanvas.drawArc( + Rect.fromCircle( + center: Offset(size.width / 2, size.height / 2), + radius: stack.radius, + ), + stack.startAngle * _kRadiansPerDegree, + segment.sweepAngle * _kRadiansPerDegree, + chart.chartType == CircularChartType.Pie, + segmentPaint, + onTapDown: (_) { + onSelectSegment(segment.id); + }, + ); + } + } +} diff --git a/pubspec.lock b/pubspec.lock index b308ac3..7b7dc63 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,49 +7,49 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.1" + version: "2.8.2" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.2.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.3.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.3" + version: "1.15.0" fake_async: dependency: transitive description: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" flutter: dependency: "direct main" description: flutter @@ -66,21 +66,28 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.1" + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.7.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.1" + version: "1.8.0" sky_engine: dependency: transitive description: flutter @@ -92,55 +99,62 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.2" + version: "1.8.1" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.2" + version: "0.4.8" + touchable: + dependency: "direct main" + description: + name: touchable + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.1" sdks: - dart: ">=2.10.0-110 <2.11.0" + dart: ">=2.14.0 <3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 0c4f40f..4d5f78a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,6 +4,7 @@ version: "0.1.2" homepage: https://github.com/GusRodrigues86/flutter_circular_chart dependencies: + touchable: ^0.2.1 flutter: sdk: flutter