diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 0833ecf..d76e7d8 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion flutter.compileSdkVersion + compileSdkVersion 34 ndkVersion flutter.ndkVersion compileOptions { @@ -47,8 +47,8 @@ android { applicationId "com.example.example" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. - minSdkVersion flutter.minSdkVersion - targetSdkVersion flutter.targetSdkVersion + minSdkVersion 28 + targetSdkVersion 34 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/example/android/build.gradle b/example/android/build.gradle index 83ae220..7c7276d 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.8.21' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' + classpath 'com.android.tools.build:gradle:7.1.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 94adc3a..a673820 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +android.enableR8=true diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 8d4492f..9625e10 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 6edd238..549a3d1 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -127,7 +127,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -171,10 +171,12 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -185,6 +187,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -272,7 +275,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -349,7 +352,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -398,7 +401,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c87d15a..a6b826d 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ CADisableMinimumFrameDurationOnPhone + UIApplicationSupportsIndirectInputEvents + diff --git a/example/lib/main.dart b/example/lib/main.dart index 5292882..a4a92fa 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -154,7 +154,7 @@ class HomePageState extends State { ListTile( title: Text( 'Pie Chart Options'.toUpperCase(), - style: Theme.of(context).textTheme.overline!.copyWith( + style: Theme.of(context).textTheme.labelSmall!.copyWith( fontSize: 12, fontWeight: FontWeight.bold, ), @@ -269,7 +269,7 @@ class HomePageState extends State { ListTile( title: Text( 'Legend Options'.toUpperCase(), - style: Theme.of(context).textTheme.overline!.copyWith( + style: Theme.of(context).textTheme.labelSmall!.copyWith( fontSize: 12, fontWeight: FontWeight.bold, ), @@ -361,7 +361,7 @@ class HomePageState extends State { ListTile( title: Text( 'Chart values Options'.toUpperCase(), - style: Theme.of(context).textTheme.overline!.copyWith( + style: Theme.of(context).textTheme.labelSmall!.copyWith( fontSize: 12, fontWeight: FontWeight.bold, ), diff --git a/lib/src/chart_painter.dart b/lib/src/chart_painter.dart index 6f93ebf..124d708 100644 --- a/lib/src/chart_painter.dart +++ b/lib/src/chart_painter.dart @@ -1,6 +1,7 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart' as intl; import 'package:pie_chart/pie_chart.dart'; const doublePi = math.pi * 2; @@ -29,6 +30,8 @@ class PieChartPainter extends CustomPainter { final DegreeOptions degreeOptions; final Color baseChartColor; final double? totalValue; + final String? prefix; + final String? suffix; late double _prevAngle; @@ -57,6 +60,8 @@ class PieChartPainter extends CustomPainter { this.degreeOptions = const DegreeOptions(), required this.baseChartColor, this.totalValue, + this.prefix, + this.suffix, }) { // set total value if (totalValue == null) { @@ -98,7 +103,10 @@ class PieChartPainter extends CustomPainter { // this is not a precise calculation, should be more generalized later // e.g. (-90, 91) should start from the left - final left = degreeOptions.initialAngle >= -90 && degreeOptions.totalDegrees <= 180 ? -size.width / 2 : 0.0; + final left = + degreeOptions.initialAngle >= -90 && degreeOptions.totalDegrees <= 180 + ? -size.width / 2 + : 0.0; const top = 0.0; @@ -131,7 +139,8 @@ class PieChartPainter extends CustomPainter { ); } else { final isGradientPresent = gradientList?.isNotEmpty ?? false; - final isNonGradientElementPresent = (_subParts.length - (gradientList?.length ?? 0)) > 0; + final isNonGradientElementPresent = + (_subParts.length - (gradientList?.length ?? 0)) > 0; for (int i = 0; i < _subParts.length; i++) { if (isGradientPresent) { @@ -144,7 +153,8 @@ class PieChartPainter extends CustomPainter { transform: GradientRotation(normalizedPrevAngle), endAngle: normalizedEndAngle, colors: getGradient(gradientList!, i, - isNonGradientElementPresent: isNonGradientElementPresent, emptyColorGradient: emptyColorGradient!), + isNonGradientElementPresent: isNonGradientElementPresent, + emptyColorGradient: emptyColorGradient!), ); paint.shader = gradient.createShader(boundingSquare); if (chartType == ChartType.ring) { @@ -170,7 +180,8 @@ class PieChartPainter extends CustomPainter { } final radius = showChartValuesOutside ? (side / 2) + 16 : side / 3; - final radians = _prevAngle + (((_totalAngle / _total) * _subParts[i]) / 2); + final radians = + _prevAngle + (((_totalAngle / _total) * _subParts[i]) / 2); final x = (radius) * math.cos(radians); final y = (radius) * math.sin(radians); @@ -209,9 +220,11 @@ class PieChartPainter extends CustomPainter { double side, { TextStyle? style, }) { + final value = intl.NumberFormat("#,##0.00", "en_US"); final span = TextSpan( style: style ?? chartValueStyle, - text: name, + text: + '${prefix ?? ''} ${value.format(double.parse(name ?? '0'))} ${suffix ?? ''}', ); TextPainter tp = TextPainter( text: span, @@ -244,5 +257,6 @@ class PieChartPainter extends CustomPainter { } @override - bool shouldRepaint(PieChartPainter oldDelegate) => oldDelegate._totalAngle != _totalAngle; + bool shouldRepaint(PieChartPainter oldDelegate) => + oldDelegate._totalAngle != _totalAngle; } diff --git a/lib/src/chart_values_options.dart b/lib/src/chart_values_options.dart index c7e8437..10f126b 100644 --- a/lib/src/chart_values_options.dart +++ b/lib/src/chart_values_options.dart @@ -10,6 +10,8 @@ class ChartValuesOptions { final bool showChartValuesOutside; final Color? chartValueBackgroundColor; final TextStyle chartValueStyle; + final String? prefix; + final String? suffix; const ChartValuesOptions({ this.showChartValueBackground = true, @@ -19,5 +21,7 @@ class ChartValuesOptions { this.chartValueStyle = defaultChartValueStyle, this.showChartValues = true, this.showChartValuesOutside = false, + this.prefix, + this.suffix, }); } diff --git a/lib/src/legend.dart b/lib/src/legend.dart index fddcfa8..3629cf0 100644 --- a/lib/src/legend.dart +++ b/lib/src/legend.dart @@ -1,11 +1,15 @@ import 'package:flutter/material.dart'; +import '../pie_chart.dart'; + class Legend extends StatelessWidget { const Legend({ required this.title, required this.color, required this.style, required this.legendShape, + required this.position, + this.width, Key? key, }) : super(key: key); @@ -13,37 +17,48 @@ class Legend extends StatelessWidget { final Color color; final TextStyle style; final BoxShape legendShape; + final LegendPosition position; + final double? width; @override Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Container( - margin: const EdgeInsets.symmetric(vertical: 2.0), - height: 20.0, - width: 18.0, - decoration: BoxDecoration( - shape: legendShape, - color: color, + double sw = MediaQuery.of(context).size.width; + return SizedBox( + width: (width != null) + ? width + : ((position == LegendPosition.right || + position == LegendPosition.left) + ? sw * 0.5 + : sw), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: const EdgeInsets.symmetric(vertical: 2.0), + height: 20.0, + width: 18.0, + decoration: BoxDecoration( + shape: legendShape, + color: color, + ), + ), + const SizedBox( + width: 8.0, + ), + Flexible( + fit: FlexFit.loose, + child: Text( + title, + style: style, + softWrap: true, + ), ), - ), - const SizedBox( - width: 8.0, - ), - Flexible( - fit: FlexFit.loose, - child: Text( - title, - style: style, - softWrap: true, + const SizedBox( + width: 8.0, ), - ), - const SizedBox( - width: 8.0, - ), - ], + ], + ), ); } } diff --git a/lib/src/legend_options.dart b/lib/src/legend_options.dart index fa177fa..a333d13 100644 --- a/lib/src/legend_options.dart +++ b/lib/src/legend_options.dart @@ -7,7 +7,8 @@ class LegendOptions { final TextStyle legendTextStyle; final BoxShape legendShape; final LegendPosition legendPosition; - final Map legendLabels; + final Map legendLabels; + final double? legendWidth; const LegendOptions({ this.showLegends = true, @@ -16,5 +17,6 @@ class LegendOptions { this.legendShape = BoxShape.circle, this.legendPosition = LegendPosition.right, this.legendLabels = const {}, + this.legendWidth, }); } diff --git a/lib/src/pie_chart.dart b/lib/src/pie_chart.dart index 6b7b3af..08359b4 100644 --- a/lib/src/pie_chart.dart +++ b/lib/src/pie_chart.dart @@ -130,36 +130,39 @@ class _PieChartState extends State children: [ CustomPaint( painter: PieChartPainter( - _animFraction, - widget.chartValuesOptions.showChartValues, - widget.chartValuesOptions.showChartValuesOutside, - widget.colorList, - chartValueStyle: widget.chartValuesOptions.chartValueStyle, - chartValueBackgroundColor: - widget.chartValuesOptions.chartValueBackgroundColor, - values: legendValues, - titles: legendTitles, - showValuesInPercentage: - widget.chartValuesOptions.showChartValuesInPercentage, - decimalPlaces: widget.chartValuesOptions.decimalPlaces, - showChartValueLabel: - widget.chartValuesOptions.showChartValueBackground, - chartType: widget.chartType, - centerText: widget.centerText, - centerTextStyle: widget.centerTextStyle, - formatChartValues: widget.formatChartValues, - strokeWidth: widget.ringStrokeWidth, - emptyColor: widget.emptyColor, - gradientList: widget.gradientList, - emptyColorGradient: widget.emptyColorGradient, - degreeOptions: widget.degreeOptions.copyWith( - // because we've deprecated initialAngleInDegree, - // we want the old value to be used if it's not null - // ignore: deprecated_member_use_from_same_package - initialAngle: widget.initialAngleInDegree, - ), - baseChartColor: widget.baseChartColor, - totalValue: widget.totalValue), + _animFraction, + widget.chartValuesOptions.showChartValues, + widget.chartValuesOptions.showChartValuesOutside, + widget.colorList, + chartValueStyle: widget.chartValuesOptions.chartValueStyle, + chartValueBackgroundColor: + widget.chartValuesOptions.chartValueBackgroundColor, + values: legendValues, + titles: legendTitles, + showValuesInPercentage: + widget.chartValuesOptions.showChartValuesInPercentage, + decimalPlaces: widget.chartValuesOptions.decimalPlaces, + showChartValueLabel: + widget.chartValuesOptions.showChartValueBackground, + chartType: widget.chartType, + centerText: widget.centerText, + centerTextStyle: widget.centerTextStyle, + formatChartValues: widget.formatChartValues, + strokeWidth: widget.ringStrokeWidth, + emptyColor: widget.emptyColor, + gradientList: widget.gradientList, + emptyColorGradient: widget.emptyColorGradient, + degreeOptions: widget.degreeOptions.copyWith( + // because we've deprecated initialAngleInDegree, + // we want the old value to be used if it's not null + // ignore: deprecated_member_use_from_same_package + initialAngle: widget.initialAngleInDegree, + ), + baseChartColor: widget.baseChartColor, + totalValue: widget.totalValue, + prefix: widget.chartValuesOptions.prefix, + suffix: widget.chartValuesOptions.suffix, + ), child: const AspectRatio(aspectRatio: 1), ), if (widget.centerWidget != null) widget.centerWidget! @@ -201,9 +204,11 @@ class _PieChartState extends State return Row( mainAxisSize: MainAxisSize.min, children: [ - _getLegend( - padding: EdgeInsets.only( - right: widget.chartLegendSpacing, + Expanded( + child: _getLegend( + padding: EdgeInsets.only( + right: widget.chartLegendSpacing, + ), ), ), _getChart(), @@ -214,9 +219,11 @@ class _PieChartState extends State mainAxisSize: MainAxisSize.min, children: [ _getChart(), - _getLegend( - padding: EdgeInsets.only( - left: widget.chartLegendSpacing, + Expanded( + child: _getLegend( + padding: EdgeInsets.only( + left: widget.chartLegendSpacing, + ), ), ), ], @@ -226,9 +233,11 @@ class _PieChartState extends State mainAxisSize: MainAxisSize.min, children: [ _getChart(), - _getLegend( - padding: EdgeInsets.only( - left: widget.chartLegendSpacing, + Expanded( + child: _getLegend( + padding: EdgeInsets.only( + left: widget.chartLegendSpacing, + ), ), ), ], @@ -252,6 +261,8 @@ class _PieChartState extends State children: legendTitles! .map( (item) => Legend( + width: widget.legendOptions.legendWidth, + position: widget.legendOptions.legendPosition, title: item, color: isGradientPresent ? getGradient( diff --git a/pubspec.yaml b/pubspec.yaml index 6fc39ec..2a5c6ab 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,6 +10,9 @@ dependencies: flutter: sdk: flutter + intl: ^0.18.1 + + dev_dependencies: flutter_test: sdk: flutter