Forked from cirediew/single_bundle_symbol_renderer.dart
Created
January 30, 2021 18:41
Revisions
-
cirediew revised this gist
Aug 28, 2020 . 3 changed files with 235 additions and 0 deletions.There are no files selected for viewing
File renamed without changes.This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,78 @@ import 'package:my_app/database/database.dart'; import 'package:my_app/util/extensions/iterable.ext.dart'; //used for firstOrNull import 'package:my_app/util/number_formatter.dart'; import 'package:charts_flutter/flutter.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/cupertino.dart'; import 'package:intl/intl.dart'; class TooltipData with EquatableMixin { num value = 0; String formattedValue = ''; String formattedIndex = ''; int index = 0; int size = 0; int fractionDigits = 2; bool dateIsInFuture = false; int textSize = 14; double textScaleFactor = 1.0; NumberFormat format = NumberFormat.decimalPattern('en'); TooltipData({ this.value, this.formattedValue, this.formattedIndex, this.index, this.size, this.fractionDigits, }); @override List get props => [ value, formattedValue, formattedIndex, index, size, fractionDigits, dateIsInFuture, textSize, textScaleFactor, ]; void updateFromModel( BuildContext context, { SelectionModel<DateTime> selectionModel, EnergyAssetCategory category, DateFormat dateFormat, double textScaleFactor, }) { value = selectionModel.selectedSeries.firstOrNull?.measureFn( selectionModel.selectedDatum.firstOrNull?.index, ); format = decimalFormat( decimalDigits: fractionDigits ?? category.fractionDigits, ); formattedValue = '${format.format(value)} ${category.suffix}'; final date = selectionModel.selectedSeries.firstOrNull?.domainFn( selectionModel.selectedDatum.firstOrNull?.index, ); dateIsInFuture = date?.isAfter(DateTime.now()) ?? false; formattedIndex = dateFormat.format(date); index = selectionModel.selectedDatum.firstOrNull?.index; size = selectionModel.selectedSeries.firstOrNull?.data?.length; this.textScaleFactor = textScaleFactor; } @override String toString() => 'TooltipData{' 'hash: $hashCode, ' 'value: $value, ' 'formattedValue: $formattedValue, ' 'formattedIndex: $formattedIndex, ' 'index: $index, ' 'size: $size, ' 'dateIsInFuture: $dateIsInFuture}'; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,157 @@ import 'package:my_app/database/data_classes/bundle_chart_data.dart'; import 'package:my_app/database/database.dart'; import 'package:my_app/util/chart/chart_util.dart'; import 'package:my_app/util/chart/custom_date_time_factory.dart'; import 'package:my_app/util/chart/single_bundle_symbol_renderer.dart'; import 'package:my_app/util/chart/tooltip_data.dart'; import 'package:charts_common/common.dart' show SymbolRenderer; import 'package:charts_flutter/flutter.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; class YearChart extends StatefulWidget { final EnergyAssetCategory category; final List<Series<ChartDataPoint, DateTime>> seriesList; const YearChart(this.seriesList, this.category); @override _YearChartState createState() => _YearChartStateState(); } class _YearChartState extends State<YearChart> { SelectionModel<DateTime> _selectionModel; TooltipData _tooltipData; MarginSpec _marginSpec; @override void initState() { super.initState(); _tooltipData = TooltipData(fractionDigits: 0); _marginSpec = MarginSpec.fromPercent( minPercent: 5, maxPercent: 50, ); } @override Widget build(BuildContext context) { final Color textColor = ColorUtil.fromDartColor(Theme.of(context).colorScheme.onSurface); final textScaleFactor = MediaQuery.textScaleFactorOf(context); if (_tooltipData.textScaleFactor != textScaleFactor) { _tooltipData.textScaleFactor = textScaleFactor; } return TimeSeriesChart( widget.seriesList, layoutConfig: LayoutConfig( leftMarginSpec: _marginSpec, topMarginSpec: MarginSpec.fromPercent( minPercent: 10, maxPercent: 50, ), rightMarginSpec: MarginSpec.defaultSpec, bottomMarginSpec: _marginSpec, ), defaultRenderer: BarRendererConfig<DateTime>(groupingType: BarGroupingType.stacked), dateTimeFactory: CustomDateTimeFactory(Localizations.localeOf(context).languageCode), primaryMeasureAxis: NumericAxisSpec( showAxisLine: false, renderSpec: SmallTickRendererSpec<num>( labelStyle: TextStyleSpec( color: textColor, fontSize: (ChartUtil.defaultLabelSize * textScaleFactor).round()), ), ), domainAxis: DateTimeAxisSpec( renderSpec: SmallTickRendererSpec<DateTime>( labelStyle: TextStyleSpec( color: textColor, fontSize: (ChartUtil.defaultLabelSize * textScaleFactor).round()), ), tickFormatterSpec: const AutoDateTimeTickFormatterSpec( minute: ChartUtil.emptyFormatterSpec, hour: ChartUtil.emptyFormatterSpec, day: ChartUtil.emptyFormatterSpec, month: ChartUtil.monthFormatterSpec, year: ChartUtil.yearFormatterSpec, ), ), behaviors: [ SelectNearest(), DomainHighlighter(), CustomLinePointHighlighter( symbolRenderer: SingleBundleSymbolRenderer( model: _selectionModel, tooltipData: _tooltipData, textColor: textColor, textScaleFactor: textScaleFactor, backgroundColor: ColorUtil.fromDartColor( Theme.of(context).canvasColor, ), strokeColor: ColorUtil.fromDartColor( widget.category.getColor(), ), ), drawFollowLinesAcrossChart: true, showVerticalFollowLine: LinePointHighlighterFollowLineType.none, showHorizontalFollowLine: LinePointHighlighterFollowLineType.all, defaultRadiusPx: 0, ), ], selectionModels: [ SelectionModelConfig( changedListener: (SelectionModel<DateTime> model) { if (model != null && model.hasAnySelection) { _tooltipData.updateFromModel(context, selectionModel: model, category: widget.category, dateFormat: DateFormat.MMMM(), textScaleFactor: textScaleFactor); } }, ), ], ); } } ///Overridden class because [symbolRenderer] is missing from equals and hashcode in [LinePointHighlighter] class CustomLinePointHighlighter extends LinePointHighlighter with EquatableMixin { CustomLinePointHighlighter({ SelectionModelType selectionModelType, double defaultRadiusPx, double radiusPaddingPx, LinePointHighlighterFollowLineType showHorizontalFollowLine, LinePointHighlighterFollowLineType showVerticalFollowLine, List<int> dashPattern, bool drawFollowLinesAcrossChart, SymbolRenderer symbolRenderer, }) : super( selectionModelType: selectionModelType, defaultRadiusPx: defaultRadiusPx, radiusPaddingPx: radiusPaddingPx, showHorizontalFollowLine: showHorizontalFollowLine, showVerticalFollowLine: showVerticalFollowLine, dashPattern: dashPattern, drawFollowLinesAcrossChart: drawFollowLinesAcrossChart, symbolRenderer: symbolRenderer, ); @override List<Object> get props => [ selectionModelType, defaultRadiusPx, radiusPaddingPx, showHorizontalFollowLine, showVerticalFollowLine, dashPattern, drawFollowLinesAcrossChart, symbolRenderer, ]; } -
cirediew created this gist
Aug 28, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,132 @@ import 'dart:math' show Rectangle; import 'package:my_app/database/database.dart'; import 'package:my_app/util/chart/tooltip_data.dart'; import 'package:charts_flutter/flutter.dart' show CircleSymbolRenderer, Color, SelectionModel, ChartCanvas, FillPatternType; import 'package:charts_flutter/src/text_element.dart'; // ignore: implementation_imports import 'package:charts_flutter/src/text_style.dart'; // ignore: implementation_imports import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; class SingleBundleSymbolRenderer extends CircleSymbolRenderer with EquatableMixin { final Color textColor; final Color backgroundColor; final Color strokeColor; final EnergyAssetCategory category; final TooltipData tooltipData; final SelectionModel<DateTime> model; final int horizontalMargin; final int verticalMargin; final int bottomPadding; final int textSize; final double textScaleFactor; final TextStyle textStyle; SingleBundleSymbolRenderer({ @required this.model, @required this.tooltipData, @required this.textColor, @required this.backgroundColor, @required this.strokeColor, this.category, this.horizontalMargin = 4, this.verticalMargin = 7, this.bottomPadding = 4, this.textSize = 14, this.textScaleFactor = 1.0, bool isSolid = true, }) : textStyle = TextStyle() ..color = textColor ..fontSize = textSize, super(isSolid: isSolid); @override bool shouldRepaint(SingleBundleSymbolRenderer oldRenderer) => this != oldRenderer; @override List<Object> get props => [ model, tooltipData, textColor, backgroundColor, strokeColor, category, horizontalMargin, verticalMargin, bottomPadding, textSize, textScaleFactor, isSolid, textStyle, ]; @override void paint( ChartCanvas canvas, Rectangle<num> bounds, { List<int> dashPattern, Color fillColor, FillPatternType fillPattern, Color strokeColor, double strokeWidthPx, }) { super.paint( canvas, bounds, dashPattern: dashPattern, fillColor: fillColor, strokeColor: strokeColor, strokeWidthPx: strokeWidthPx, ); if (tooltipData.dateIsInFuture) { return; } TextElement textElement = category != null ? TextElement( '${tooltipData.formattedIndex}\n' '${category.title}: ${tooltipData.formattedValue}', style: textStyle, textScaleFactor: tooltipData.textScaleFactor, ) : TextElement( '${tooltipData.formattedIndex}\n' '${tooltipData.formattedValue}', style: textStyle, textScaleFactor: tooltipData.textScaleFactor, ); final offsetX = bounds.left - (textElement.measurement.horizontalSliceWidth * (tooltipData.index / ((tooltipData.size - 1 == 0 ? 1 : tooltipData.size - 1)))); final offsetY = bounds.top - textElement.measurement.verticalSliceWidth - verticalMargin; canvas.drawRect( Rectangle( offsetX - horizontalMargin, offsetY - verticalMargin - bottomPadding, textElement.measurement.horizontalSliceWidth + horizontalMargin * 2, textElement.measurement.verticalSliceWidth + verticalMargin * 2, ), fill: backgroundColor, stroke: strokeColor, strokeWidthPx: 1, ); canvas.drawText( textElement, offsetX.toInt(), offsetY.toInt() - bottomPadding, ); } }