From d965e731ddc8f05cab1bb6f09c759ec454a77097 Mon Sep 17 00:00:00 2001 From: meminb Date: Sat, 3 Feb 2024 01:28:43 +0300 Subject: [PATCH 1/4] Improve styling options of time chart --- example/pubspec.lock | 56 +------------- lib/src/chart.dart | 81 ++++++++------------ lib/src/components/chart_style.dart | 17 ++++ lib/src/components/painter/chart_engine.dart | 8 +- lib/src/time_chart.dart | 62 ++++++++------- lib/time_chart.dart | 1 + pubspec.lock | 56 +------------- test/time_data_processor_test.dart | 6 +- 8 files changed, 99 insertions(+), 188 deletions(-) create mode 100644 lib/src/components/chart_style.dart diff --git a/example/pubspec.lock b/example/pubspec.lock index 0e13ecc..2e212c6 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -41,14 +41,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" - crypto: - dependency: transitive - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" cupertino_icons: dependency: "direct main" description: @@ -96,22 +88,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.18.1" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "7e108028e3d258667d079986da8c0bc32da4cb57431c2af03b1dc1038621a9dc" - url: "https://pub.dev" - source: hosted - version: "9.0.13" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff - url: "https://pub.dev" - source: hosted - version: "1.0.5" linked_scroll_controller: dependency: transitive description: @@ -140,18 +116,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.10.0" path: dependency: transitive description: @@ -244,14 +220,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" vector_math: dependency: transitive description: @@ -260,14 +228,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 - url: "https://pub.dev" - source: hosted - version: "13.0.0" web: dependency: transitive description: @@ -276,14 +236,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.0" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b - url: "https://pub.dev" - source: hosted - version: "2.4.0" sdks: dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=1.24.0-7.0" diff --git a/lib/src/chart.dart b/lib/src/chart.dart index 967c058..84c862a 100644 --- a/lib/src/chart.dart +++ b/lib/src/chart.dart @@ -8,6 +8,7 @@ import 'package:linked_scroll_controller/linked_scroll_controller.dart'; import 'package:touchable/touchable.dart'; import '../time_chart.dart'; +import 'components/chart_style.dart'; import 'components/painter/amount_chart/amount_x_label_painter.dart'; import 'components/painter/amount_chart/amount_y_label_painter.dart'; import 'components/painter/time_chart/time_x_label_painter.dart'; @@ -26,43 +27,44 @@ import 'components/translations/translations.dart'; import 'components/utils/context_utils.dart'; class Chart extends StatefulWidget { - const Chart({ + Chart({ Key? key, required this.chartType, required this.width, required this.height, - required this.barColor, required this.data, required this.timeChartSizeAnimationDuration, required this.tooltipDuration, - required this.tooltipBackgroundColor, required this.tooltipStart, required this.tooltipEnd, required this.activeTooltip, required this.viewMode, required this.defaultPivotHour, - }) : super(key: key); + this.chartStyle, + }) : super(key: key) { + kLineColor1 = chartStyle?.horizontalGridColor ?? kLineColor1; + kLineColor2 = chartStyle?.verticalGridColor ?? kLineColor2; + kTextColor = chartStyle?.labelColor ?? kLineColor3; + } final ChartType chartType; final double width; final double height; - final Color? barColor; final List data; final Duration timeChartSizeAnimationDuration; final Duration tooltipDuration; - final Color? tooltipBackgroundColor; final String tooltipStart; final String tooltipEnd; final bool activeTooltip; final ViewMode viewMode; final int defaultPivotHour; + final ChartStyle? chartStyle; @override ChartState createState() => ChartState(); } -class ChartState extends State - with TickerProviderStateMixin, TimeDataProcessor { +class ChartState extends State with TickerProviderStateMixin, TimeDataProcessor { static const Duration _tooltipFadeInDuration = Duration(milliseconds: 150); static const Duration _tooltipFadeOutDuration = Duration(milliseconds: 75); @@ -147,15 +149,12 @@ class ChartState extends State _sizeController.dispose(); _tooltipController.dispose(); _cancelTimer(); - GestureBinding.instance.pointerRouter - .removeGlobalRoute(_handlePointerEvent); + GestureBinding.instance.pointerRouter.removeGlobalRoute(_handlePointerEvent); super.dispose(); } DateTime _getFirstItemDate({Duration addition = Duration.zero}) { - return widget.data.isEmpty - ? DateTime.now() - : widget.data.first.end.dateWithoutTime().add(addition); + return widget.data.isEmpty ? DateTime.now() : widget.data.first.end.dateWithoutTime().add(addition); } void _addScrollNotifier() { @@ -163,8 +162,7 @@ class ChartState extends State final minDifference = _blockWidth!; _scrollControllerGroup.addOffsetChangedListener(() { - final difference = - (_scrollControllerGroup.offset - _previousScrollOffset).abs(); + final difference = (_scrollControllerGroup.offset - _previousScrollOffset).abs(); if (difference >= minDifference) { _scrollOffsetNotifier.value = _scrollControllerGroup.offset; @@ -204,8 +202,7 @@ class ChartState extends State } // 현재 보이는 툴팁이 다시 호출되면 무시한다. - if ((_tooltipHideTimer?.isActive ?? false) && - _currentVisibleTooltipRect == rect) return; + if ((_tooltipHideTimer?.isActive ?? false) && _currentVisibleTooltipRect == rect) return; _currentVisibleTooltipRect = rect; HapticFeedback.vibrate(); @@ -239,16 +236,13 @@ class ChartState extends State final chartType = amount == null ? ChartType.time : ChartType.amount; // 현재 위젯의 위치를 얻는다. final widgetOffset = context.getRenderBoxOffset()!; - final tooltipSize = - chartType == ChartType.time ? kTimeTooltipSize : kAmountTooltipSize; + final tooltipSize = chartType == ChartType.time ? kTimeTooltipSize : kAmountTooltipSize; final candidateTop = rect.top + widgetOffset.dy - tooltipSize.height / 2 + kTimeChartTopPadding + - (chartType == ChartType.time - ? (rect.bottom - rect.top) / 2 - : kTooltipArrowHeight / 2); + (chartType == ChartType.time ? (rect.bottom - rect.top) / 2 : kTooltipArrowHeight / 2); final scrollPixels = position.maxScrollExtent - position.pixels; final localLeft = rect.left + widgetOffset.dx - scrollPixels; @@ -272,7 +266,7 @@ class ChartState extends State curve: Curves.fastOutSlowIn, ), child: TooltipOverlay( - backgroundColor: widget.tooltipBackgroundColor, + backgroundColor: widget.chartStyle?.tooltipBackgroundColor, chartType: chartType, bottomHour: bottomHour, timeRange: range, @@ -303,10 +297,7 @@ class ChartState extends State final TextPainter tp = TextPainter( text: TextSpan( text: translations.formatHourOnly(12), - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Colors.white38), + style: Theme.of(context).textTheme.bodyMedium!.copyWith(color: Colors.white38), ), textDirection: TextDirection.ltr, ); @@ -324,8 +315,7 @@ class ChartState extends State if (notification is ScrollStartNotification) { _cancelTimer(); } else if (notification is ScrollEndNotification) { - _pivotHourUpdatingTimer = - Timer(const Duration(milliseconds: 800), _timerCallback); + _pivotHourUpdatingTimer = Timer(const Duration(milliseconds: 800), _timerCallback); } return true; } @@ -335,10 +325,8 @@ class ChartState extends State final beforeTopHour = topHour; final beforeBottomHour = bottomHour; - final blockIndex = - getCurrentBlockIndex(_barController.position, _blockWidth!).toInt(); - final needsToAdaptScrollPosition = - blockIndex > 0 && isFirstDataMovedNextDay; + final blockIndex = getCurrentBlockIndex(_barController.position, _blockWidth!).toInt(); + final needsToAdaptScrollPosition = blockIndex > 0 && isFirstDataMovedNextDay; final scrollPositionDuration = Duration( days: -blockIndex + (needsToAdaptScrollPosition ? 1 : 0), ); @@ -362,8 +350,7 @@ class ChartState extends State double get heightWithoutLabel => widget.height - kXLabelHeight; void _runHeightAnimation(int beforeTopHour, int beforeBottomHour) { - final beforeDiff = - hourDiffBetween(beforeTopHour, beforeBottomHour).toDouble(); + final beforeDiff = hourDiffBetween(beforeTopHour, beforeBottomHour).toDouble(); final currentDiff = hourDiffBetween(topHour, bottomHour).toDouble(); final candidateUpward = diffBetween(beforeTopHour, topHour!); @@ -371,15 +358,11 @@ class ChartState extends State // (candidate)중에서 current top-bottom hour 범위에 들어오는 것을 선택한다. final topDiff = - isDirUpward(beforeTopHour, beforeBottomHour, topHour!, bottomHour!) - ? candidateUpward - : candidateDownWard; + isDirUpward(beforeTopHour, beforeBottomHour, topHour!, bottomHour!) ? candidateUpward : candidateDownWard; setState(() { - _animationBeginHeight = - (currentDiff / beforeDiff) * heightWithoutLabel + kXLabelHeight; - _heightForAlignTop = (_animationBeginHeight - widget.height) / 2 + - (topDiff / beforeDiff) * heightWithoutLabel; + _animationBeginHeight = (currentDiff / beforeDiff) * heightWithoutLabel + kXLabelHeight; + _heightForAlignTop = (_animationBeginHeight - widget.height) / 2 + (topDiff / beforeDiff) * heightWithoutLabel; }); _sizeController.reverse(from: 1.0); } @@ -523,8 +506,7 @@ class ChartState extends State double bottomPadding = 0.0, Function(BuildContext, double)? builder, }) { - assert( - (child != null && builder == null) || child == null && builder != null); + assert((child != null && builder == null) || child == null && builder != null); final heightAnimation = Tween( begin: widget.height, @@ -538,9 +520,7 @@ class ChartState extends State return AnimatedBuilder( animation: _sizeAnimation, builder: (context, child) { - final topPosition = (widget.height - heightAnimation.value) / 2 + - heightForAlignTopAnimation.value + - topPadding; + final topPosition = (widget.height - heightAnimation.value) / 2 + heightForAlignTopAnimation.value + topPadding; return Positioned( right: 0, top: topPosition, @@ -582,8 +562,7 @@ class ChartState extends State } CustomPainter _buildXLabelPainter(BuildContext context) { - final firstValueDateTime = - processedData.isEmpty ? DateTime.now() : processedData.first.end; + final firstValueDateTime = processedData.isEmpty ? DateTime.now() : processedData.first.end; switch (widget.chartType) { case ChartType.time: return TimeXLabelPainter( @@ -616,7 +595,7 @@ class ChartState extends State context: context, tooltipCallback: _tooltipCallback, dataList: processedData, - barColor: widget.barColor, + barColor: widget.chartStyle?.barColor, topHour: topHour!, bottomHour: bottomHour!, dayCount: dayCount, @@ -628,7 +607,7 @@ class ChartState extends State repaint: _scrollOffsetNotifier, context: context, dataList: processedData, - barColor: widget.barColor, + barColor: widget.chartStyle?.barColor, topHour: topHour!, bottomHour: bottomHour!, tooltipCallback: _tooltipCallback, diff --git a/lib/src/components/chart_style.dart b/lib/src/components/chart_style.dart new file mode 100644 index 0000000..b7348ab --- /dev/null +++ b/lib/src/components/chart_style.dart @@ -0,0 +1,17 @@ +import 'dart:ui'; + +class ChartStyle { + final Color? barColor; + final Color? tooltipBackgroundColor; + final Color? labelColor; + final Color? verticalGridColor; + final Color? horizontalGridColor; + + ChartStyle({ + this.barColor, + this.tooltipBackgroundColor, + this.labelColor, + this.verticalGridColor, + this.horizontalGridColor, + }); +} diff --git a/lib/src/components/painter/chart_engine.dart b/lib/src/components/painter/chart_engine.dart index 0291045..2388a32 100644 --- a/lib/src/components/painter/chart_engine.dart +++ b/lib/src/components/painter/chart_engine.dart @@ -14,10 +14,10 @@ const double kLineStrokeWidth = 0.8; const double kBarWidthRatio = 0.7; const double kBarPaddingWidthRatio = (1 - kBarWidthRatio) / 2; -const Color kLineColor1 = Color(0x44757575); -const Color kLineColor2 = Color(0x77757575); -const Color kLineColor3 = Color(0xAA757575); -const Color kTextColor = Color(0xFF757575); + Color kLineColor1 = Color(0x44757575); + Color kLineColor2 = Color(0x77757575); + Color kLineColor3 = Color(0xAA757575); + Color kTextColor = Color(0xFF757575); abstract class ChartEngine extends CustomPainter { static const int toleranceDay = 1; diff --git a/lib/src/time_chart.dart b/lib/src/time_chart.dart index c9afd63..b11a491 100644 --- a/lib/src/time_chart.dart +++ b/lib/src/time_chart.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'chart.dart'; +import 'components/chart_style.dart'; import 'components/chart_type.dart'; import 'components/view_mode.dart'; @@ -12,16 +13,15 @@ class TimeChart extends StatelessWidget { this.chartType = ChartType.time, this.width, this.height = 280.0, - this.barColor, required this.data, this.timeChartSizeAnimationDuration = const Duration(milliseconds: 300), this.tooltipDuration = const Duration(seconds: 7), - this.tooltipBackgroundColor, this.tooltipStart = "START", this.tooltipEnd = "END", this.activeTooltip = true, this.viewMode = ViewMode.weekly, this.defaultPivotHour = 0, + this.chartStyle, }) : assert(0 <= defaultPivotHour && defaultPivotHour < 24), super(key: key); @@ -40,11 +40,6 @@ class TimeChart extends StatelessWidget { /// Default is `280.0`. Actual height is [height] + 4.0([kTimeChartTopPadding]). final double height; - /// The color of the bar in the chart. - /// - /// Default is the `Theme.of(context).colorScheme.secondary`. - final Color? barColor; - /// The list of [DateTimeRange]. /// /// The first index is the latest data, The end data is the oldest data. @@ -65,11 +60,6 @@ class TimeChart extends StatelessWidget { /// Default is `Duration(seconds: 7)`. final Duration tooltipDuration; - /// The color of the tooltip background. - /// - /// [Theme.of(context).dialogBackgroundColor] is default color. - final Color? tooltipBackgroundColor; - /// The label of [ChartType.time] tooltip. /// /// Default is "start" @@ -107,6 +97,26 @@ class TimeChart extends StatelessWidget { /// It must be in the range of 0 to 23. final int defaultPivotHour; + /// The style of the chart. + /// + /// tooltipBackgroundColor + /// [Theme.of(context).dialogBackgroundColor] is default color. + /// + /// barColor + /// Default is the `Theme.of(context).colorScheme.secondary`. + /// + /// labelColor + /// Default is the `Theme.of(context).colorScheme.onSurface`. + /// + /// verticalGridColor + /// Default is the `Theme.of(context).colorScheme.onSurface`. + /// + /// horizontalGridColor + /// Default is the `Theme.of(context).colorScheme.onSurface`. + /// + + final ChartStyle? chartStyle; + @override Widget build(BuildContext context) { return LayoutBuilder(builder: (_, box) { @@ -116,21 +126,19 @@ class TimeChart extends StatelessWidget { height: height + kTimeChartTopPadding, width: actualWidth, child: Chart( - key: ValueKey(viewMode), - chartType: chartType, - width: actualWidth, - height: height, - barColor: barColor, - data: data, - timeChartSizeAnimationDuration: timeChartSizeAnimationDuration, - tooltipDuration: tooltipDuration, - tooltipBackgroundColor: tooltipBackgroundColor, - tooltipStart: tooltipStart, - tooltipEnd: tooltipEnd, - activeTooltip: activeTooltip, - viewMode: viewMode, - defaultPivotHour: defaultPivotHour, - ), + key: ValueKey(viewMode), + chartType: chartType, + width: actualWidth, + height: height, + data: data, + timeChartSizeAnimationDuration: timeChartSizeAnimationDuration, + tooltipDuration: tooltipDuration, + tooltipStart: tooltipStart, + tooltipEnd: tooltipEnd, + activeTooltip: activeTooltip, + viewMode: viewMode, + defaultPivotHour: defaultPivotHour, + chartStyle: chartStyle), ); }); } diff --git a/lib/time_chart.dart b/lib/time_chart.dart index a09e175..6d1a042 100644 --- a/lib/time_chart.dart +++ b/lib/time_chart.dart @@ -3,3 +3,4 @@ library time_chart; export 'src/time_chart.dart'; export 'src/components/view_mode.dart'; export 'src/components/chart_type.dart'; +export 'src/components/chart_style.dart'; diff --git a/pubspec.lock b/pubspec.lock index f00399a..85fc616 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -41,14 +41,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" - crypto: - dependency: transitive - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" fake_async: dependency: transitive description: @@ -88,22 +80,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.18.1" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "7e108028e3d258667d079986da8c0bc32da4cb57431c2af03b1dc1038621a9dc" - url: "https://pub.dev" - source: hosted - version: "9.0.13" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff - url: "https://pub.dev" - source: hosted - version: "1.0.5" linked_scroll_controller: dependency: "direct main" description: @@ -132,18 +108,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.10.0" path: dependency: transitive description: @@ -229,14 +205,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" vector_math: dependency: transitive description: @@ -245,14 +213,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 - url: "https://pub.dev" - source: hosted - version: "13.0.0" web: dependency: transitive description: @@ -261,14 +221,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.0" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b - url: "https://pub.dev" - source: hosted - version: "2.4.0" sdks: dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=1.24.0-7.0" diff --git a/test/time_data_processor_test.dart b/test/time_data_processor_test.dart index 3063c6a..1d9c8a9 100644 --- a/test/time_data_processor_test.dart +++ b/test/time_data_processor_test.dart @@ -37,13 +37,15 @@ Chart _getChart( data: data, timeChartSizeAnimationDuration: const Duration(milliseconds: 300), tooltipDuration: const Duration(milliseconds: 500), - tooltipBackgroundColor: Colors.black, tooltipStart: "START", tooltipEnd: "END", activeTooltip: true, viewMode: ViewMode.weekly, defaultPivotHour: defaultPivotHour, - barColor: Colors.red, + chartStyle: ChartStyle( + barColor: Colors.red, + tooltipBackgroundColor: Colors.black, + ) ); } From 427949a19e94fb84d982573d7e9033a11750c642 Mon Sep 17 00:00:00 2001 From: meminb Date: Sat, 3 Feb 2024 01:37:35 +0300 Subject: [PATCH 2/4] Revert .lock changes --- example/lib/main.dart | 8 +++++-- example/pubspec.lock | 56 +++++++++++++++++++++++++++++++++++++++---- pubspec.lock | 56 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 110 insertions(+), 10 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 44a754d..9cd619f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -89,7 +89,9 @@ class MyApp extends StatelessWidget { data: smallDataList, chartType: ChartType.amount, viewMode: ViewMode.weekly, - barColor: Colors.deepPurple, + chartStyle: ChartStyle( + barColor: Colors.deepPurple + ), ), sizedBox, const Text('Monthly amount chart'), @@ -97,7 +99,9 @@ class MyApp extends StatelessWidget { data: smallDataList, chartType: ChartType.amount, viewMode: ViewMode.monthly, - barColor: Colors.deepPurple, + chartStyle: ChartStyle( + barColor: Colors.deepPurple + ) ), ], ), diff --git a/example/pubspec.lock b/example/pubspec.lock index 2e212c6..0e13ecc 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" cupertino_icons: dependency: "direct main" description: @@ -88,6 +96,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.18.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7e108028e3d258667d079986da8c0bc32da4cb57431c2af03b1dc1038621a9dc" + url: "https://pub.dev" + source: hosted + version: "9.0.13" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff + url: "https://pub.dev" + source: hosted + version: "1.0.5" linked_scroll_controller: dependency: transitive description: @@ -116,18 +140,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" path: dependency: transitive description: @@ -220,6 +244,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" vector_math: dependency: transitive description: @@ -228,6 +260,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + url: "https://pub.dev" + source: hosted + version: "13.0.0" web: dependency: transitive description: @@ -236,6 +276,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" sdks: dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=1.24.0-7.0" diff --git a/pubspec.lock b/pubspec.lock index 85fc616..f00399a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" fake_async: dependency: transitive description: @@ -80,6 +88,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.18.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7e108028e3d258667d079986da8c0bc32da4cb57431c2af03b1dc1038621a9dc" + url: "https://pub.dev" + source: hosted + version: "9.0.13" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff + url: "https://pub.dev" + source: hosted + version: "1.0.5" linked_scroll_controller: dependency: "direct main" description: @@ -108,18 +132,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" path: dependency: transitive description: @@ -205,6 +229,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" vector_math: dependency: transitive description: @@ -213,6 +245,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + url: "https://pub.dev" + source: hosted + version: "13.0.0" web: dependency: transitive description: @@ -221,6 +261,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" sdks: dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=1.24.0-7.0" From f487ceb1e45d45b86bc2a2715b5de2fa18298526 Mon Sep 17 00:00:00 2001 From: meminb Date: Sat, 3 Feb 2024 01:47:05 +0300 Subject: [PATCH 3/4] Fix variable binding --- lib/src/chart.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/chart.dart b/lib/src/chart.dart index 84c862a..35e8ce0 100644 --- a/lib/src/chart.dart +++ b/lib/src/chart.dart @@ -42,8 +42,8 @@ class Chart extends StatefulWidget { required this.defaultPivotHour, this.chartStyle, }) : super(key: key) { + kLineColor3 = chartStyle?.verticalGridColor ?? kLineColor3; kLineColor1 = chartStyle?.horizontalGridColor ?? kLineColor1; - kLineColor2 = chartStyle?.verticalGridColor ?? kLineColor2; kTextColor = chartStyle?.labelColor ?? kLineColor3; } From b3c45644fdcf9a0a3034bf5166cdd8a857a1d086 Mon Sep 17 00:00:00 2001 From: meminb Date: Sun, 4 Feb 2024 10:20:01 +0300 Subject: [PATCH 4/4] Revert code formatting --- lib/src/chart.dart | 84 ++++++++++---------- lib/src/components/chart_style.dart | 2 +- lib/src/components/painter/chart_engine.dart | 12 +-- 3 files changed, 50 insertions(+), 48 deletions(-) diff --git a/lib/src/chart.dart b/lib/src/chart.dart index 35e8ce0..8dbdbec 100644 --- a/lib/src/chart.dart +++ b/lib/src/chart.dart @@ -64,7 +64,8 @@ class Chart extends StatefulWidget { ChartState createState() => ChartState(); } -class ChartState extends State with TickerProviderStateMixin, TimeDataProcessor { +class ChartState extends State + with TickerProviderStateMixin, TimeDataProcessor { static const Duration _tooltipFadeInDuration = Duration(milliseconds: 150); static const Duration _tooltipFadeOutDuration = Duration(milliseconds: 75); @@ -149,20 +150,23 @@ class ChartState extends State with TickerProviderStateMixin, TimeDataPro _sizeController.dispose(); _tooltipController.dispose(); _cancelTimer(); - GestureBinding.instance.pointerRouter.removeGlobalRoute(_handlePointerEvent); + GestureBinding.instance.pointerRouter + .removeGlobalRoute(_handlePointerEvent); super.dispose(); } DateTime _getFirstItemDate({Duration addition = Duration.zero}) { - return widget.data.isEmpty ? DateTime.now() : widget.data.first.end.dateWithoutTime().add(addition); - } + return widget.data.isEmpty + ? DateTime.now() + : widget.data.first.end.dateWithoutTime().add(addition); } void _addScrollNotifier() { WidgetsBinding.instance.addPostFrameCallback((_) { final minDifference = _blockWidth!; _scrollControllerGroup.addOffsetChangedListener(() { - final difference = (_scrollControllerGroup.offset - _previousScrollOffset).abs(); + final difference = + (_scrollControllerGroup.offset - _previousScrollOffset).abs(); if (difference >= minDifference) { _scrollOffsetNotifier.value = _scrollControllerGroup.offset; @@ -202,7 +206,8 @@ class ChartState extends State with TickerProviderStateMixin, TimeDataPro } // 현재 보이는 툴팁이 다시 호출되면 무시한다. - if ((_tooltipHideTimer?.isActive ?? false) && _currentVisibleTooltipRect == rect) return; + if ((_tooltipHideTimer?.isActive ?? false) && + _currentVisibleTooltipRect == rect) return; _currentVisibleTooltipRect = rect; HapticFeedback.vibrate(); @@ -236,18 +241,20 @@ class ChartState extends State with TickerProviderStateMixin, TimeDataPro final chartType = amount == null ? ChartType.time : ChartType.amount; // 현재 위젯의 위치를 얻는다. final widgetOffset = context.getRenderBoxOffset()!; - final tooltipSize = chartType == ChartType.time ? kTimeTooltipSize : kAmountTooltipSize; + final tooltipSize = + chartType == ChartType.time ? kTimeTooltipSize : kAmountTooltipSize; final candidateTop = rect.top + widgetOffset.dy - tooltipSize.height / 2 + kTimeChartTopPadding + - (chartType == ChartType.time ? (rect.bottom - rect.top) / 2 : kTooltipArrowHeight / 2); + (chartType == ChartType.time + ? (rect.bottom - rect.top) / 2 + : kTooltipArrowHeight / 2); final scrollPixels = position.maxScrollExtent - position.pixels; final localLeft = rect.left + widgetOffset.dx - scrollPixels; final tooltipTop = max(candidateTop, 0.0); - Direction direction = Direction.left; double tooltipLeft = localLeft - tooltipSize.width - _tooltipPadding; // 툴팁을 바의 오른쪽에 배치해야 하는 경우 @@ -255,7 +262,6 @@ class ChartState extends State with TickerProviderStateMixin, TimeDataPro direction = Direction.right; tooltipLeft = localLeft + barWidth + _tooltipPadding; } - return Positioned( // 바 옆에 [tooltip]을 띄운다. top: tooltipTop, @@ -279,7 +285,6 @@ class ChartState extends State with TickerProviderStateMixin, TimeDataPro ), ); } - /// 현재 존재하는 툴팁을 제거한다. void _removeEntry() { _tooltipHideTimer?.cancel(); @@ -287,70 +292,65 @@ class ChartState extends State with TickerProviderStateMixin, TimeDataPro _overlayEntry?.remove(); _overlayEntry = null; } - void _cancelTimer() { _pivotHourUpdatingTimer?.cancel(); } - double _getRightMargin(BuildContext context) { final translations = Translations(context); final TextPainter tp = TextPainter( text: TextSpan( text: translations.formatHourOnly(12), - style: Theme.of(context).textTheme.bodyMedium!.copyWith(color: Colors.white38), + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.white38), ), textDirection: TextDirection.ltr, ); tp.layout(); return tp.width + kYLabelMargin; } - void _handlePanDown(_) { _scrollPhysics!.setPanDownPixels(_barController.position.pixels); } - bool _handleScrollNotification(ScrollNotification notification) { if (widget.chartType == ChartType.amount) return false; - if (notification is ScrollStartNotification) { _cancelTimer(); } else if (notification is ScrollEndNotification) { - _pivotHourUpdatingTimer = Timer(const Duration(milliseconds: 800), _timerCallback); + _pivotHourUpdatingTimer = + Timer(const Duration(milliseconds: 800), _timerCallback); } return true; } - void _timerCallback() { final beforeIsFirstDataMovedNextDay = isFirstDataMovedNextDay; final beforeTopHour = topHour; final beforeBottomHour = bottomHour; - final blockIndex = getCurrentBlockIndex(_barController.position, _blockWidth!).toInt(); - final needsToAdaptScrollPosition = blockIndex > 0 && isFirstDataMovedNextDay; + final blockIndex = + getCurrentBlockIndex(_barController.position, _blockWidth!).toInt(); + final needsToAdaptScrollPosition = + blockIndex > 0 && isFirstDataMovedNextDay; final scrollPositionDuration = Duration( days: -blockIndex + (needsToAdaptScrollPosition ? 1 : 0), ); - processData(widget, _getFirstItemDate(addition: scrollPositionDuration)); - if (topHour == beforeTopHour && bottomHour == beforeBottomHour) return; - if (beforeIsFirstDataMovedNextDay != isFirstDataMovedNextDay) { // 하루가 추가 혹은 삭제되는 경우 x축 방향으로 발생하는 차이를 해결할 값이다. final add = isFirstDataMovedNextDay ? _blockWidth! : -_blockWidth!; - _barController.jumpTo(_barController.position.pixels + add); _scrollPhysics!.addPanDownPixels(add); _scrollPhysics!.setDayCount(dayCount!); } - _runHeightAnimation(beforeTopHour!, beforeBottomHour!); } - double get heightWithoutLabel => widget.height - kXLabelHeight; void _runHeightAnimation(int beforeTopHour, int beforeBottomHour) { - final beforeDiff = hourDiffBetween(beforeTopHour, beforeBottomHour).toDouble(); + final beforeDiff = + hourDiffBetween(beforeTopHour, beforeBottomHour).toDouble(); final currentDiff = hourDiffBetween(topHour, bottomHour).toDouble(); final candidateUpward = diffBetween(beforeTopHour, topHour!); @@ -358,26 +358,26 @@ class ChartState extends State with TickerProviderStateMixin, TimeDataPro // (candidate)중에서 current top-bottom hour 범위에 들어오는 것을 선택한다. final topDiff = - isDirUpward(beforeTopHour, beforeBottomHour, topHour!, bottomHour!) ? candidateUpward : candidateDownWard; + isDirUpward(beforeTopHour, beforeBottomHour, topHour!, bottomHour!) + ? candidateUpward + : candidateDownWard; setState(() { - _animationBeginHeight = (currentDiff / beforeDiff) * heightWithoutLabel + kXLabelHeight; - _heightForAlignTop = (_animationBeginHeight - widget.height) / 2 + (topDiff / beforeDiff) * heightWithoutLabel; + _animationBeginHeight = + (currentDiff / beforeDiff) * heightWithoutLabel + kXLabelHeight; + _heightForAlignTop = (_animationBeginHeight - widget.height) / 2 + + (topDiff / beforeDiff) * heightWithoutLabel; }); _sizeController.reverse(from: 1.0); } - @override Widget build(BuildContext context) { final int viewModeLimitDay = widget.viewMode.dayCount; final key = ValueKey((topHour ?? 0) + (bottomHour ?? 1) * 100); - final double outerHeight = kTimeChartTopPadding + widget.height; final double yLabelWidth = _getRightMargin(context); final double totalWidth = widget.width; - _blockWidth ??= (totalWidth - yLabelWidth) / viewModeLimitDay; - final innerSize = Size( _blockWidth! * max(dayCount!, viewModeLimitDay), double.infinity, @@ -475,7 +475,6 @@ class ChartState extends State with TickerProviderStateMixin, TimeDataPro ), ); } - Widget _buildHorizontalScrollView({ required Widget child, required Key key, @@ -498,7 +497,6 @@ class ChartState extends State with TickerProviderStateMixin, TimeDataPro ), ); } - Widget _buildAnimatedBox({ Widget? child, required double width, @@ -506,7 +504,8 @@ class ChartState extends State with TickerProviderStateMixin, TimeDataPro double bottomPadding = 0.0, Function(BuildContext, double)? builder, }) { - assert((child != null && builder == null) || child == null && builder != null); + assert( + (child != null && builder == null) || child == null && builder != null); final heightAnimation = Tween( begin: widget.height, @@ -516,11 +515,12 @@ class ChartState extends State with TickerProviderStateMixin, TimeDataPro begin: 0, end: _heightForAlignTop, ).animate(_sizeAnimation); - return AnimatedBuilder( animation: _sizeAnimation, builder: (context, child) { - final topPosition = (widget.height - heightAnimation.value) / 2 + heightForAlignTopAnimation.value + topPadding; + final topPosition = (widget.height - heightAnimation.value) / 2 + + heightForAlignTopAnimation.value + + topPadding; return Positioned( right: 0, top: topPosition, @@ -539,7 +539,6 @@ class ChartState extends State with TickerProviderStateMixin, TimeDataPro child: child, ); } - CustomPainter _buildYLabelPainter(BuildContext context, double topPosition) { switch (widget.chartType) { case ChartType.time: @@ -562,7 +561,8 @@ class ChartState extends State with TickerProviderStateMixin, TimeDataPro } CustomPainter _buildXLabelPainter(BuildContext context) { - final firstValueDateTime = processedData.isEmpty ? DateTime.now() : processedData.first.end; + final firstValueDateTime = + processedData.isEmpty ? DateTime.now() : processedData.first.end; switch (widget.chartType) { case ChartType.time: return TimeXLabelPainter( diff --git a/lib/src/components/chart_style.dart b/lib/src/components/chart_style.dart index b7348ab..efa6a1c 100644 --- a/lib/src/components/chart_style.dart +++ b/lib/src/components/chart_style.dart @@ -7,7 +7,7 @@ class ChartStyle { final Color? verticalGridColor; final Color? horizontalGridColor; - ChartStyle({ + const ChartStyle({ this.barColor, this.tooltipBackgroundColor, this.labelColor, diff --git a/lib/src/components/painter/chart_engine.dart b/lib/src/components/painter/chart_engine.dart index 2388a32..7707382 100644 --- a/lib/src/components/painter/chart_engine.dart +++ b/lib/src/components/painter/chart_engine.dart @@ -1,7 +1,7 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; -import '../view_mode.dart'; +import 'package:time_chart/time_chart.dart'; import '../translations/translations.dart'; const double kYLabelMargin = 12.0; @@ -14,10 +14,10 @@ const double kLineStrokeWidth = 0.8; const double kBarWidthRatio = 0.7; const double kBarPaddingWidthRatio = (1 - kBarWidthRatio) / 2; - Color kLineColor1 = Color(0x44757575); - Color kLineColor2 = Color(0x77757575); - Color kLineColor3 = Color(0xAA757575); - Color kTextColor = Color(0xFF757575); +Color kLineColor1 = Color(0x44757575); +Color kLineColor2 = Color(0x77757575); +Color kLineColor3 = Color(0xAA757575); +Color kTextColor = Color(0xFF757575); abstract class ChartEngine extends CustomPainter { static const int toleranceDay = 1; @@ -28,6 +28,7 @@ abstract class ChartEngine extends CustomPainter { required this.viewMode, this.firstValueDateTime, required this.context, + this.chartStyle, super.repaint, }) : dayCount = math.max(dayCount ?? -1, viewMode.dayCount), translations = Translations(context); @@ -38,6 +39,7 @@ abstract class ChartEngine extends CustomPainter { final DateTime? firstValueDateTime; final BuildContext context; final Translations translations; + final ChartStyle? chartStyle; int get currentDayFromScrollOffset { if (!scrollController!.hasClients) return 0;