Line data Source code
1 : // Copyright 2023 The terCAD team. All rights reserved.
2 : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file.
3 :
4 : import 'dart:collection';
5 :
6 : import 'package:app_finance/_classes/herald/app_locale.dart';
7 : import 'package:app_finance/_classes/structure/transaction_log_data.dart';
8 : import 'package:app_finance/_ext/build_context_ext.dart';
9 : import 'package:app_finance/charts/interface/chart_data.dart';
10 : import 'package:app_finance/charts/interface/data_scope.dart';
11 : import 'package:app_finance/charts/painter/foreground_chart_painter.dart';
12 : import 'package:app_finance/charts/painter/line_chart_painter.dart';
13 : import 'package:flutter/material.dart';
14 : import 'package:intl/intl.dart';
15 :
16 : class TradeChart extends StatelessWidget {
17 : final double width;
18 : final double height;
19 : final double indent;
20 : final String tooltip;
21 : final List<TransactionLogData> data;
22 :
23 0 : const TradeChart({
24 : super.key,
25 : required this.data,
26 : required this.width,
27 : required this.height,
28 : this.indent = 0.0,
29 : this.tooltip = '',
30 : });
31 :
32 0 : DataScope _prepareData() {
33 0 : final result = SplayTreeMap<double, Offset>();
34 0 : final xMax = DateTime.now().millisecondsSinceEpoch.toDouble();
35 0 : double yMin = data.firstOrNull?.changedTo ?? 0.0;
36 : double yMax = 0.0;
37 0 : for (int i = 0; i < data.length; i++) {
38 0 : final time = data[i].timestamp;
39 0 : final x = DateTime(time.year, time.month, time.day).millisecondsSinceEpoch.toDouble();
40 0 : if (!result.containsKey(x) || result[x]!.dy < data[i].changedTo) {
41 0 : result[x] = Offset(x, data[i].changedTo ?? 0.0);
42 : }
43 0 : if (data[i].changedTo > yMax) {
44 0 : yMax = data[i].changedTo;
45 : }
46 0 : if (data[i].changedTo < yMin) {
47 0 : yMin = data[i].changedTo;
48 : }
49 : }
50 0 : final scope = result.values.toList();
51 0 : return DataScope(
52 : xMax: xMax,
53 0 : xMin: scope.firstOrNull?.dx ?? xMax,
54 0 : yMin: yMin * 0.8,
55 0 : yMax: yMax * 1.2,
56 0 : data: [
57 0 : ChartData(
58 : scope,
59 0 : color: scope.isNotEmpty && scope.first.dy > scope.last.dy ? Colors.orange : Colors.blue,
60 : ),
61 0 : if (scope.isNotEmpty)
62 0 : ChartData([
63 0 : scope.first,
64 0 : Offset(DateTime.now().millisecondsSinceEpoch.toDouble(), scope.first.dy),
65 : ], color: Colors.grey, strokeWidth: 1)
66 : ],
67 : );
68 : }
69 :
70 0 : @override
71 : Widget build(BuildContext context) {
72 0 : final size = Size(width, height);
73 0 : final bgColor = context.colorScheme.onSurface;
74 0 : final scope = _prepareData();
75 :
76 0 : final bg = ForegroundChartPainter(
77 : size: size,
78 : color: bgColor,
79 : lineColor: bgColor,
80 0 : background: Colors.white.withOpacity(0.0),
81 0 : yMin: scope.yMin,
82 0 : yMax: scope.yMax,
83 : xType: DateTime,
84 0 : xMin: scope.xMin,
85 0 : xMax: scope.xMax,
86 : xDivider: 16,
87 0 : xTpl: DateFormat.Md(AppLocale.code),
88 : );
89 :
90 0 : return SizedBox(
91 0 : height: size.height,
92 0 : width: size.width,
93 0 : child: CustomPaint(
94 : size: size,
95 0 : painter: LineChartPainter(
96 0 : indent: bg.shift,
97 : size: size,
98 0 : data: scope.data,
99 0 : yMin: scope.yMin,
100 0 : yMax: scope.yMax,
101 0 : xMin: bg.xMin,
102 0 : xMax: bg.xMax,
103 : ),
104 : foregroundPainter: bg,
105 : willChange: false,
106 0 : child: Padding(
107 0 : padding: EdgeInsets.only(top: indent / 4),
108 0 : child: Text(tooltip),
109 : ),
110 : ),
111 : );
112 : }
113 : }
|