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/lib/src/chart.dart b/lib/src/chart.dart index 967c058..8dbdbec 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,36 +27,38 @@ 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) { + kLineColor3 = chartStyle?.verticalGridColor ?? kLineColor3; + kLineColor1 = chartStyle?.horizontalGridColor ?? kLineColor1; + 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(); @@ -155,8 +158,7 @@ class ChartState extends State DateTime _getFirstItemDate({Duration addition = Duration.zero}) { return widget.data.isEmpty ? DateTime.now() - : widget.data.first.end.dateWithoutTime().add(addition); - } + : widget.data.first.end.dateWithoutTime().add(addition); } void _addScrollNotifier() { WidgetsBinding.instance.addPostFrameCallback((_) { @@ -253,7 +255,6 @@ class ChartState extends State 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; // 툴팁을 바의 오른쪽에 배치해야 하는 경우 @@ -261,7 +262,6 @@ class ChartState extends State direction = Direction.right; tooltipLeft = localLeft + barWidth + _tooltipPadding; } - return Positioned( // 바 옆에 [tooltip]을 띄운다. top: tooltipTop, @@ -272,7 +272,7 @@ class ChartState extends State curve: Curves.fastOutSlowIn, ), child: TooltipOverlay( - backgroundColor: widget.tooltipBackgroundColor, + backgroundColor: widget.chartStyle?.tooltipBackgroundColor, chartType: chartType, bottomHour: bottomHour, timeRange: range, @@ -285,7 +285,6 @@ class ChartState extends State ), ); } - /// 현재 존재하는 툴팁을 제거한다. void _removeEntry() { _tooltipHideTimer?.cancel(); @@ -293,11 +292,9 @@ class ChartState extends State _overlayEntry?.remove(); _overlayEntry = null; } - void _cancelTimer() { _pivotHourUpdatingTimer?.cancel(); } - double _getRightMargin(BuildContext context) { final translations = Translations(context); final TextPainter tp = TextPainter( @@ -313,14 +310,11 @@ class ChartState extends State 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) { @@ -329,7 +323,6 @@ class ChartState extends State } return true; } - void _timerCallback() { final beforeIsFirstDataMovedNextDay = isFirstDataMovedNextDay; final beforeTopHour = topHour; @@ -342,23 +335,17 @@ class ChartState extends State 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) { @@ -383,18 +370,14 @@ class ChartState extends State }); _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, @@ -492,7 +475,6 @@ class ChartState extends State ), ); } - Widget _buildHorizontalScrollView({ required Widget child, required Key key, @@ -515,7 +497,6 @@ class ChartState extends State ), ); } - Widget _buildAnimatedBox({ Widget? child, required double width, @@ -534,7 +515,6 @@ class ChartState extends State begin: 0, end: _heightForAlignTop, ).animate(_sizeAnimation); - return AnimatedBuilder( animation: _sizeAnimation, builder: (context, child) { @@ -559,7 +539,6 @@ class ChartState extends State child: child, ); } - CustomPainter _buildYLabelPainter(BuildContext context, double topPosition) { switch (widget.chartType) { case ChartType.time: @@ -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..efa6a1c --- /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; + + const 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..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; -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; @@ -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; 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/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, + ) ); }