mirror of
https://github.com/flutter/samples.git
synced 2025-11-09 14:28:51 +00:00
Drop github_dataviz (#1508)
* Drop `github_dataviz` * Drop from `samples_index`
This commit is contained in:
@@ -1,27 +0,0 @@
|
|||||||
Copyright 2019 The Chromium Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
A visualization for
|
|
||||||
[Flutter GitHub repository](https://github.com/flutter/flutter/) metadata.
|
|
||||||
|
|
||||||
Created for Google by [Larva Labs](http://larvalabs.com/), a team of creative
|
|
||||||
technologists who make things for Android, iPhone, and the Web.
|
|
||||||
|
|
||||||
## Data Notes
|
|
||||||
|
|
||||||
The data starts the week of Oct 19, 2014 which is the first commit. This is week
|
|
||||||
0 in our data arrays, but it is week 43 in 2014. Year boundaries are then offset
|
|
||||||
by 9 weeks.
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
include: package:flutter_lints/flutter.yaml
|
|
||||||
|
|
||||||
linter:
|
|
||||||
rules:
|
|
||||||
avoid_print: false
|
|
||||||
prefer_single_quotes: true
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
@@ -1,178 +0,0 @@
|
|||||||
54 30
|
|
||||||
55 448
|
|
||||||
56 286
|
|
||||||
57 156
|
|
||||||
58 293
|
|
||||||
59 289
|
|
||||||
60 230
|
|
||||||
61 96
|
|
||||||
62 83
|
|
||||||
63 254
|
|
||||||
64 254
|
|
||||||
65 227
|
|
||||||
66 344
|
|
||||||
67 337
|
|
||||||
68 428
|
|
||||||
69 487
|
|
||||||
70 547
|
|
||||||
71 446
|
|
||||||
72 425
|
|
||||||
73 331
|
|
||||||
74 324
|
|
||||||
75 399
|
|
||||||
76 399
|
|
||||||
77 301
|
|
||||||
78 318
|
|
||||||
79 283
|
|
||||||
80 351
|
|
||||||
81 428
|
|
||||||
82 452
|
|
||||||
83 447
|
|
||||||
84 310
|
|
||||||
85 310
|
|
||||||
86 270
|
|
||||||
87 331
|
|
||||||
88 144
|
|
||||||
89 150
|
|
||||||
90 176
|
|
||||||
91 218
|
|
||||||
92 253
|
|
||||||
93 302
|
|
||||||
94 276
|
|
||||||
95 452
|
|
||||||
96 347
|
|
||||||
97 340
|
|
||||||
98 246
|
|
||||||
99 392
|
|
||||||
100 430
|
|
||||||
101 298
|
|
||||||
102 267
|
|
||||||
103 233
|
|
||||||
104 316
|
|
||||||
105 307
|
|
||||||
106 416
|
|
||||||
107 268
|
|
||||||
108 335
|
|
||||||
109 213
|
|
||||||
110 342
|
|
||||||
111 227
|
|
||||||
112 166
|
|
||||||
113 56
|
|
||||||
114 63
|
|
||||||
115 230
|
|
||||||
116 362
|
|
||||||
117 248
|
|
||||||
118 550
|
|
||||||
119 809
|
|
||||||
120 677
|
|
||||||
121 667
|
|
||||||
122 425
|
|
||||||
123 544
|
|
||||||
124 420
|
|
||||||
125 457
|
|
||||||
126 415
|
|
||||||
127 480
|
|
||||||
128 569
|
|
||||||
129 402
|
|
||||||
130 459
|
|
||||||
131 466
|
|
||||||
132 523
|
|
||||||
133 634
|
|
||||||
134 479
|
|
||||||
135 564
|
|
||||||
136 436
|
|
||||||
137 519
|
|
||||||
138 808
|
|
||||||
139 589
|
|
||||||
140 367
|
|
||||||
141 169
|
|
||||||
142 316
|
|
||||||
143 370
|
|
||||||
144 334
|
|
||||||
145 281
|
|
||||||
146 256
|
|
||||||
147 357
|
|
||||||
148 293
|
|
||||||
149 349
|
|
||||||
150 300
|
|
||||||
151 342
|
|
||||||
152 291
|
|
||||||
153 327
|
|
||||||
154 329
|
|
||||||
155 346
|
|
||||||
156 386
|
|
||||||
157 318
|
|
||||||
158 310
|
|
||||||
159 361
|
|
||||||
160 360
|
|
||||||
161 352
|
|
||||||
162 474
|
|
||||||
163 495
|
|
||||||
164 569
|
|
||||||
165 426
|
|
||||||
166 89
|
|
||||||
167 17
|
|
||||||
171 208
|
|
||||||
172 502
|
|
||||||
173 565
|
|
||||||
174 273
|
|
||||||
175 1002
|
|
||||||
176 920
|
|
||||||
177 808
|
|
||||||
178 870
|
|
||||||
179 808
|
|
||||||
180 537
|
|
||||||
181 745
|
|
||||||
182 717
|
|
||||||
183 778
|
|
||||||
184 676
|
|
||||||
185 562
|
|
||||||
186 708
|
|
||||||
187 957
|
|
||||||
188 853
|
|
||||||
189 703
|
|
||||||
190 1013
|
|
||||||
191 1040
|
|
||||||
192 759
|
|
||||||
193 811
|
|
||||||
194 1086
|
|
||||||
195 1031
|
|
||||||
196 1080
|
|
||||||
197 1159
|
|
||||||
198 622
|
|
||||||
199 892
|
|
||||||
200 1188
|
|
||||||
201 1273
|
|
||||||
202 1214
|
|
||||||
203 1103
|
|
||||||
204 876
|
|
||||||
205 1248
|
|
||||||
206 956
|
|
||||||
207 1119
|
|
||||||
208 1218
|
|
||||||
209 1126
|
|
||||||
210 1058
|
|
||||||
211 872
|
|
||||||
212 1005
|
|
||||||
213 805
|
|
||||||
214 1146
|
|
||||||
215 1132
|
|
||||||
216 1404
|
|
||||||
217 1436
|
|
||||||
218 819
|
|
||||||
219 2152
|
|
||||||
220 1651
|
|
||||||
221 1418
|
|
||||||
222 1387
|
|
||||||
223 1496
|
|
||||||
224 1262
|
|
||||||
225 1784
|
|
||||||
226 1681
|
|
||||||
227 1401
|
|
||||||
228 1150
|
|
||||||
229 1269
|
|
||||||
230 1162
|
|
||||||
231 1136
|
|
||||||
232 1045
|
|
||||||
233 1370
|
|
||||||
234 41
|
|
||||||
|
@@ -1,177 +0,0 @@
|
|||||||
48 1
|
|
||||||
53 1
|
|
||||||
54 16
|
|
||||||
55 70
|
|
||||||
56 78
|
|
||||||
57 46
|
|
||||||
58 74
|
|
||||||
59 67
|
|
||||||
60 50
|
|
||||||
61 10
|
|
||||||
62 9
|
|
||||||
63 47
|
|
||||||
64 53
|
|
||||||
65 55
|
|
||||||
66 49
|
|
||||||
67 59
|
|
||||||
68 96
|
|
||||||
69 67
|
|
||||||
70 92
|
|
||||||
71 75
|
|
||||||
72 88
|
|
||||||
73 69
|
|
||||||
74 69
|
|
||||||
75 84
|
|
||||||
76 82
|
|
||||||
77 59
|
|
||||||
78 63
|
|
||||||
79 67
|
|
||||||
80 62
|
|
||||||
81 60
|
|
||||||
82 76
|
|
||||||
83 63
|
|
||||||
84 58
|
|
||||||
85 55
|
|
||||||
86 44
|
|
||||||
87 64
|
|
||||||
88 27
|
|
||||||
89 24
|
|
||||||
90 32
|
|
||||||
91 46
|
|
||||||
92 58
|
|
||||||
93 53
|
|
||||||
94 56
|
|
||||||
95 57
|
|
||||||
96 37
|
|
||||||
97 41
|
|
||||||
98 27
|
|
||||||
99 52
|
|
||||||
100 63
|
|
||||||
101 51
|
|
||||||
102 49
|
|
||||||
103 44
|
|
||||||
104 65
|
|
||||||
105 55
|
|
||||||
106 65
|
|
||||||
107 53
|
|
||||||
108 47
|
|
||||||
109 34
|
|
||||||
110 43
|
|
||||||
111 34
|
|
||||||
112 24
|
|
||||||
113 5
|
|
||||||
115 32
|
|
||||||
116 62
|
|
||||||
117 39
|
|
||||||
118 88
|
|
||||||
119 85
|
|
||||||
120 100
|
|
||||||
121 97
|
|
||||||
122 65
|
|
||||||
123 77
|
|
||||||
124 71
|
|
||||||
125 77
|
|
||||||
126 63
|
|
||||||
127 57
|
|
||||||
128 84
|
|
||||||
129 44
|
|
||||||
130 68
|
|
||||||
131 58
|
|
||||||
132 86
|
|
||||||
133 96
|
|
||||||
134 56
|
|
||||||
135 61
|
|
||||||
136 42
|
|
||||||
137 45
|
|
||||||
138 66
|
|
||||||
139 59
|
|
||||||
140 44
|
|
||||||
141 15
|
|
||||||
142 47
|
|
||||||
143 50
|
|
||||||
144 17
|
|
||||||
145 36
|
|
||||||
146 27
|
|
||||||
147 18
|
|
||||||
148 57
|
|
||||||
149 77
|
|
||||||
150 38
|
|
||||||
151 50
|
|
||||||
152 40
|
|
||||||
153 48
|
|
||||||
154 45
|
|
||||||
155 35
|
|
||||||
156 55
|
|
||||||
157 33
|
|
||||||
158 48
|
|
||||||
159 53
|
|
||||||
160 32
|
|
||||||
161 46
|
|
||||||
162 67
|
|
||||||
163 70
|
|
||||||
164 70
|
|
||||||
165 48
|
|
||||||
166 3
|
|
||||||
171 20
|
|
||||||
172 71
|
|
||||||
173 79
|
|
||||||
174 38
|
|
||||||
175 58
|
|
||||||
176 64
|
|
||||||
177 63
|
|
||||||
178 70
|
|
||||||
179 43
|
|
||||||
180 25
|
|
||||||
181 48
|
|
||||||
182 60
|
|
||||||
183 62
|
|
||||||
184 69
|
|
||||||
185 65
|
|
||||||
186 44
|
|
||||||
187 41
|
|
||||||
188 56
|
|
||||||
189 76
|
|
||||||
190 61
|
|
||||||
191 46
|
|
||||||
192 49
|
|
||||||
193 30
|
|
||||||
194 55
|
|
||||||
195 51
|
|
||||||
196 50
|
|
||||||
197 68
|
|
||||||
198 51
|
|
||||||
199 52
|
|
||||||
200 52
|
|
||||||
201 70
|
|
||||||
202 78
|
|
||||||
203 51
|
|
||||||
204 46
|
|
||||||
205 58
|
|
||||||
206 75
|
|
||||||
207 60
|
|
||||||
208 69
|
|
||||||
209 62
|
|
||||||
210 75
|
|
||||||
211 86
|
|
||||||
212 75
|
|
||||||
213 31
|
|
||||||
214 31
|
|
||||||
215 2
|
|
||||||
216 60
|
|
||||||
217 60
|
|
||||||
218 16
|
|
||||||
219 60
|
|
||||||
220 90
|
|
||||||
221 77
|
|
||||||
222 50
|
|
||||||
223 49
|
|
||||||
224 72
|
|
||||||
225 86
|
|
||||||
226 64
|
|
||||||
227 101
|
|
||||||
228 69
|
|
||||||
229 87
|
|
||||||
230 80
|
|
||||||
231 82
|
|
||||||
232 81
|
|
||||||
233 74
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,175 +0,0 @@
|
|||||||
51 1
|
|
||||||
54 14
|
|
||||||
55 19
|
|
||||||
56 11
|
|
||||||
57 7
|
|
||||||
58 2
|
|
||||||
59 2
|
|
||||||
60 4
|
|
||||||
61 2
|
|
||||||
62 3
|
|
||||||
63 6
|
|
||||||
64 2
|
|
||||||
65 6
|
|
||||||
66 2
|
|
||||||
67 4
|
|
||||||
68 5
|
|
||||||
69 3
|
|
||||||
70 2
|
|
||||||
71 3
|
|
||||||
72 3
|
|
||||||
73 6
|
|
||||||
75 3
|
|
||||||
76 2
|
|
||||||
77 6
|
|
||||||
78 1
|
|
||||||
79 2
|
|
||||||
80 5
|
|
||||||
81 4
|
|
||||||
82 7
|
|
||||||
83 6
|
|
||||||
85 2
|
|
||||||
86 1
|
|
||||||
87 4
|
|
||||||
88 2
|
|
||||||
89 3
|
|
||||||
90 1
|
|
||||||
91 3
|
|
||||||
92 1
|
|
||||||
93 3
|
|
||||||
94 4
|
|
||||||
95 15
|
|
||||||
96 11
|
|
||||||
97 6
|
|
||||||
98 1
|
|
||||||
99 2
|
|
||||||
100 2
|
|
||||||
101 3
|
|
||||||
102 2
|
|
||||||
103 2
|
|
||||||
104 1
|
|
||||||
105 5
|
|
||||||
106 6
|
|
||||||
107 8
|
|
||||||
108 7
|
|
||||||
109 7
|
|
||||||
110 4
|
|
||||||
111 5
|
|
||||||
112 3
|
|
||||||
113 5
|
|
||||||
114 2
|
|
||||||
115 8
|
|
||||||
116 7
|
|
||||||
117 5
|
|
||||||
118 5
|
|
||||||
119 8
|
|
||||||
120 6
|
|
||||||
121 6
|
|
||||||
122 5
|
|
||||||
123 6
|
|
||||||
124 6
|
|
||||||
125 4
|
|
||||||
126 5
|
|
||||||
127 7
|
|
||||||
128 6
|
|
||||||
129 4
|
|
||||||
130 4
|
|
||||||
131 3
|
|
||||||
132 5
|
|
||||||
133 18
|
|
||||||
134 9
|
|
||||||
135 16
|
|
||||||
136 14
|
|
||||||
137 5
|
|
||||||
138 6
|
|
||||||
139 12
|
|
||||||
140 8
|
|
||||||
141 9
|
|
||||||
142 9
|
|
||||||
143 2
|
|
||||||
144 9
|
|
||||||
145 11
|
|
||||||
146 7
|
|
||||||
147 7
|
|
||||||
148 2
|
|
||||||
149 10
|
|
||||||
150 13
|
|
||||||
151 12
|
|
||||||
152 61
|
|
||||||
153 35
|
|
||||||
154 15
|
|
||||||
155 20
|
|
||||||
156 18
|
|
||||||
157 10
|
|
||||||
158 8
|
|
||||||
159 17
|
|
||||||
160 12
|
|
||||||
161 14
|
|
||||||
162 21
|
|
||||||
163 12
|
|
||||||
164 21
|
|
||||||
165 21
|
|
||||||
166 10
|
|
||||||
167 3
|
|
||||||
171 9
|
|
||||||
172 17
|
|
||||||
173 14
|
|
||||||
174 33
|
|
||||||
175 274
|
|
||||||
176 154
|
|
||||||
177 111
|
|
||||||
178 96
|
|
||||||
179 65
|
|
||||||
180 50
|
|
||||||
181 65
|
|
||||||
182 67
|
|
||||||
183 68
|
|
||||||
184 46
|
|
||||||
185 87
|
|
||||||
186 68
|
|
||||||
187 85
|
|
||||||
188 65
|
|
||||||
189 73
|
|
||||||
190 66
|
|
||||||
191 176
|
|
||||||
192 156
|
|
||||||
193 109
|
|
||||||
194 95
|
|
||||||
195 87
|
|
||||||
196 68
|
|
||||||
197 98
|
|
||||||
198 65
|
|
||||||
199 82
|
|
||||||
200 51
|
|
||||||
201 68
|
|
||||||
202 54
|
|
||||||
203 66
|
|
||||||
204 96
|
|
||||||
205 62
|
|
||||||
206 68
|
|
||||||
207 60
|
|
||||||
208 71
|
|
||||||
209 74
|
|
||||||
210 69
|
|
||||||
211 78
|
|
||||||
212 72
|
|
||||||
213 66
|
|
||||||
214 68
|
|
||||||
215 237
|
|
||||||
216 193
|
|
||||||
217 117
|
|
||||||
218 96
|
|
||||||
219 137
|
|
||||||
220 116
|
|
||||||
221 133
|
|
||||||
222 122
|
|
||||||
223 85
|
|
||||||
224 69
|
|
||||||
225 115
|
|
||||||
226 138
|
|
||||||
227 139
|
|
||||||
228 168
|
|
||||||
229 145
|
|
||||||
230 152
|
|
||||||
231 139
|
|
||||||
232 8
|
|
||||||
|
@@ -1,177 +0,0 @@
|
|||||||
54 25
|
|
||||||
55 122
|
|
||||||
56 146
|
|
||||||
57 90
|
|
||||||
58 146
|
|
||||||
59 130
|
|
||||||
60 103
|
|
||||||
61 21
|
|
||||||
62 22
|
|
||||||
63 88
|
|
||||||
64 108
|
|
||||||
65 92
|
|
||||||
66 94
|
|
||||||
67 116
|
|
||||||
68 174
|
|
||||||
69 124
|
|
||||||
70 173
|
|
||||||
71 146
|
|
||||||
72 179
|
|
||||||
73 142
|
|
||||||
74 138
|
|
||||||
75 165
|
|
||||||
76 160
|
|
||||||
77 114
|
|
||||||
78 122
|
|
||||||
79 133
|
|
||||||
80 124
|
|
||||||
81 110
|
|
||||||
82 141
|
|
||||||
83 128
|
|
||||||
84 119
|
|
||||||
85 117
|
|
||||||
86 90
|
|
||||||
87 124
|
|
||||||
88 71
|
|
||||||
89 37
|
|
||||||
90 38
|
|
||||||
91 71
|
|
||||||
92 92
|
|
||||||
93 106
|
|
||||||
94 98
|
|
||||||
95 117
|
|
||||||
96 90
|
|
||||||
97 80
|
|
||||||
98 55
|
|
||||||
99 104
|
|
||||||
100 131
|
|
||||||
101 101
|
|
||||||
102 92
|
|
||||||
103 98
|
|
||||||
104 137
|
|
||||||
105 113
|
|
||||||
106 134
|
|
||||||
107 103
|
|
||||||
108 105
|
|
||||||
109 75
|
|
||||||
110 100
|
|
||||||
111 70
|
|
||||||
112 47
|
|
||||||
113 10
|
|
||||||
115 73
|
|
||||||
116 137
|
|
||||||
117 78
|
|
||||||
118 178
|
|
||||||
119 191
|
|
||||||
120 206
|
|
||||||
121 205
|
|
||||||
122 138
|
|
||||||
123 160
|
|
||||||
124 149
|
|
||||||
125 167
|
|
||||||
126 130
|
|
||||||
127 126
|
|
||||||
128 175
|
|
||||||
129 87
|
|
||||||
130 148
|
|
||||||
131 129
|
|
||||||
132 181
|
|
||||||
133 194
|
|
||||||
134 124
|
|
||||||
135 114
|
|
||||||
136 101
|
|
||||||
137 107
|
|
||||||
138 157
|
|
||||||
139 127
|
|
||||||
140 100
|
|
||||||
141 39
|
|
||||||
142 106
|
|
||||||
143 105
|
|
||||||
144 52
|
|
||||||
145 77
|
|
||||||
146 60
|
|
||||||
147 43
|
|
||||||
148 103
|
|
||||||
149 127
|
|
||||||
150 88
|
|
||||||
151 98
|
|
||||||
152 84
|
|
||||||
153 104
|
|
||||||
154 93
|
|
||||||
155 74
|
|
||||||
156 119
|
|
||||||
157 72
|
|
||||||
158 101
|
|
||||||
159 110
|
|
||||||
160 72
|
|
||||||
161 95
|
|
||||||
162 129
|
|
||||||
163 139
|
|
||||||
164 148
|
|
||||||
165 105
|
|
||||||
166 9
|
|
||||||
167 4
|
|
||||||
171 44
|
|
||||||
172 160
|
|
||||||
173 146
|
|
||||||
174 92
|
|
||||||
175 117
|
|
||||||
176 156
|
|
||||||
177 151
|
|
||||||
178 140
|
|
||||||
179 110
|
|
||||||
180 48
|
|
||||||
181 99
|
|
||||||
182 128
|
|
||||||
183 140
|
|
||||||
184 149
|
|
||||||
185 127
|
|
||||||
186 100
|
|
||||||
187 76
|
|
||||||
188 131
|
|
||||||
189 103
|
|
||||||
190 125
|
|
||||||
191 108
|
|
||||||
192 120
|
|
||||||
193 82
|
|
||||||
194 125
|
|
||||||
195 123
|
|
||||||
196 166
|
|
||||||
197 226
|
|
||||||
198 178
|
|
||||||
199 216
|
|
||||||
200 148
|
|
||||||
201 201
|
|
||||||
202 219
|
|
||||||
203 166
|
|
||||||
204 157
|
|
||||||
205 217
|
|
||||||
206 246
|
|
||||||
207 190
|
|
||||||
208 221
|
|
||||||
209 209
|
|
||||||
210 172
|
|
||||||
211 221
|
|
||||||
212 183
|
|
||||||
213 91
|
|
||||||
214 104
|
|
||||||
215 52
|
|
||||||
216 165
|
|
||||||
217 157
|
|
||||||
218 99
|
|
||||||
219 213
|
|
||||||
220 246
|
|
||||||
221 260
|
|
||||||
222 169
|
|
||||||
223 174
|
|
||||||
224 199
|
|
||||||
225 233
|
|
||||||
226 190
|
|
||||||
227 233
|
|
||||||
228 181
|
|
||||||
229 257
|
|
||||||
230 240
|
|
||||||
231 284
|
|
||||||
232 214
|
|
||||||
233 271
|
|
||||||
234 48
|
|
||||||
|
@@ -1,206 +0,0 @@
|
|||||||
22 8
|
|
||||||
23 4
|
|
||||||
24 3
|
|
||||||
25 2
|
|
||||||
26 1
|
|
||||||
27 356
|
|
||||||
28 1108
|
|
||||||
29 118
|
|
||||||
30 36
|
|
||||||
31 28
|
|
||||||
32 19
|
|
||||||
33 24
|
|
||||||
34 11
|
|
||||||
35 6
|
|
||||||
36 20
|
|
||||||
37 14
|
|
||||||
38 17
|
|
||||||
39 1
|
|
||||||
40 3
|
|
||||||
43 1
|
|
||||||
44 2
|
|
||||||
47 2
|
|
||||||
48 1
|
|
||||||
50 1
|
|
||||||
51 1
|
|
||||||
52 1
|
|
||||||
53 5
|
|
||||||
54 85
|
|
||||||
55 68
|
|
||||||
56 54
|
|
||||||
57 116
|
|
||||||
58 43
|
|
||||||
59 36
|
|
||||||
60 24
|
|
||||||
61 25
|
|
||||||
62 26
|
|
||||||
63 31
|
|
||||||
64 28
|
|
||||||
65 23
|
|
||||||
66 19
|
|
||||||
67 15
|
|
||||||
68 12
|
|
||||||
69 22
|
|
||||||
70 15
|
|
||||||
71 21
|
|
||||||
72 18
|
|
||||||
73 19
|
|
||||||
74 11
|
|
||||||
75 15
|
|
||||||
76 8
|
|
||||||
77 7
|
|
||||||
78 10
|
|
||||||
79 3
|
|
||||||
80 10
|
|
||||||
81 9
|
|
||||||
82 30
|
|
||||||
83 17
|
|
||||||
84 13
|
|
||||||
85 12
|
|
||||||
86 4
|
|
||||||
87 25
|
|
||||||
88 58
|
|
||||||
89 7
|
|
||||||
90 13
|
|
||||||
91 8
|
|
||||||
92 10
|
|
||||||
93 11
|
|
||||||
94 38
|
|
||||||
95 112
|
|
||||||
96 44
|
|
||||||
97 22
|
|
||||||
98 36
|
|
||||||
99 25
|
|
||||||
100 10
|
|
||||||
101 32
|
|
||||||
102 11
|
|
||||||
103 12
|
|
||||||
104 20
|
|
||||||
105 107
|
|
||||||
106 38
|
|
||||||
107 33
|
|
||||||
108 41
|
|
||||||
109 21
|
|
||||||
110 27
|
|
||||||
111 17
|
|
||||||
112 22
|
|
||||||
113 10
|
|
||||||
114 9
|
|
||||||
115 54
|
|
||||||
116 32
|
|
||||||
117 35
|
|
||||||
118 26
|
|
||||||
119 19
|
|
||||||
120 30
|
|
||||||
121 83
|
|
||||||
122 30
|
|
||||||
123 33
|
|
||||||
124 31
|
|
||||||
125 28
|
|
||||||
126 13
|
|
||||||
127 77
|
|
||||||
128 41
|
|
||||||
129 27
|
|
||||||
130 36
|
|
||||||
131 26
|
|
||||||
132 26
|
|
||||||
133 248
|
|
||||||
134 166
|
|
||||||
135 138
|
|
||||||
136 91
|
|
||||||
137 63
|
|
||||||
138 61
|
|
||||||
139 169
|
|
||||||
140 144
|
|
||||||
141 69
|
|
||||||
142 45
|
|
||||||
143 45
|
|
||||||
144 43
|
|
||||||
145 59
|
|
||||||
146 50
|
|
||||||
147 86
|
|
||||||
148 114
|
|
||||||
149 96
|
|
||||||
150 93
|
|
||||||
151 74
|
|
||||||
152 944
|
|
||||||
153 352
|
|
||||||
154 130
|
|
||||||
155 159
|
|
||||||
156 111
|
|
||||||
157 105
|
|
||||||
158 64
|
|
||||||
159 113
|
|
||||||
160 121
|
|
||||||
161 150
|
|
||||||
162 167
|
|
||||||
163 120
|
|
||||||
164 208
|
|
||||||
165 151
|
|
||||||
166 92
|
|
||||||
167 96
|
|
||||||
168 116
|
|
||||||
169 108
|
|
||||||
170 139
|
|
||||||
171 156
|
|
||||||
172 162
|
|
||||||
173 108
|
|
||||||
174 353
|
|
||||||
175 4210
|
|
||||||
176 2601
|
|
||||||
177 1227
|
|
||||||
178 798
|
|
||||||
179 594
|
|
||||||
180 464
|
|
||||||
181 633
|
|
||||||
182 514
|
|
||||||
183 400
|
|
||||||
184 388
|
|
||||||
185 717
|
|
||||||
186 644
|
|
||||||
187 646
|
|
||||||
188 526
|
|
||||||
189 756
|
|
||||||
190 457
|
|
||||||
191 2248
|
|
||||||
192 1649
|
|
||||||
193 906
|
|
||||||
194 794
|
|
||||||
195 702
|
|
||||||
196 591
|
|
||||||
197 725
|
|
||||||
198 584
|
|
||||||
199 502
|
|
||||||
200 477
|
|
||||||
201 506
|
|
||||||
202 488
|
|
||||||
203 398
|
|
||||||
204 738
|
|
||||||
205 549
|
|
||||||
206 339
|
|
||||||
207 455
|
|
||||||
208 398
|
|
||||||
209 452
|
|
||||||
210 424
|
|
||||||
211 485
|
|
||||||
212 415
|
|
||||||
213 411
|
|
||||||
214 493
|
|
||||||
215 2359
|
|
||||||
216 1458
|
|
||||||
217 860
|
|
||||||
218 764
|
|
||||||
219 716
|
|
||||||
220 741
|
|
||||||
221 999
|
|
||||||
222 904
|
|
||||||
223 669
|
|
||||||
224 430
|
|
||||||
225 729
|
|
||||||
226 793
|
|
||||||
227 976
|
|
||||||
228 1032
|
|
||||||
229 973
|
|
||||||
230 998
|
|
||||||
231 1032
|
|
||||||
232 87
|
|
||||||
|
@@ -1,79 +0,0 @@
|
|||||||
import 'package:github_dataviz/mathutils.dart';
|
|
||||||
|
|
||||||
class ControlPointAndValue {
|
|
||||||
late int point;
|
|
||||||
double? value;
|
|
||||||
|
|
||||||
ControlPointAndValue() {
|
|
||||||
value = 0;
|
|
||||||
point = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CatmullInterpolator implements Interpolator {
|
|
||||||
List<Point2D> controlPoints;
|
|
||||||
|
|
||||||
CatmullInterpolator(this.controlPoints);
|
|
||||||
|
|
||||||
@override
|
|
||||||
double get(double v) {
|
|
||||||
for (int i = 2; i < controlPoints.length - 1; i++) {
|
|
||||||
if (controlPoints[i].x >= v) {
|
|
||||||
double t = (v - controlPoints[i - 1].x) /
|
|
||||||
(controlPoints[i].x - controlPoints[i - 1].x);
|
|
||||||
double p0 = controlPoints[i - 2].y;
|
|
||||||
double p1 = controlPoints[i - 1].y;
|
|
||||||
double p2 = controlPoints[i].y;
|
|
||||||
double p3 = controlPoints[i + 1].y;
|
|
||||||
return 0.5 *
|
|
||||||
((2 * p1) +
|
|
||||||
(p2 - p0) * t +
|
|
||||||
(2 * p0 - 5 * p1 + 4 * p2 - p3) * t * t +
|
|
||||||
(3 * p1 - p0 - 3 * p2 + p3) * t * t * t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Will be unreachable if the control points were set up right
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ControlPointAndValue progressiveGet(ControlPointAndValue cpv) {
|
|
||||||
double? v = cpv.value;
|
|
||||||
for (int i = cpv.point; i < controlPoints.length - 1; i++) {
|
|
||||||
if (controlPoints[i].x >= v!) {
|
|
||||||
double t = (v - controlPoints[i - 1].x) /
|
|
||||||
(controlPoints[i].x - controlPoints[i - 1].x);
|
|
||||||
double p0 = controlPoints[i - 2].y;
|
|
||||||
double p1 = controlPoints[i - 1].y;
|
|
||||||
double p2 = controlPoints[i].y;
|
|
||||||
double p3 = controlPoints[i + 1].y;
|
|
||||||
cpv.value = 0.5 *
|
|
||||||
((2 * p1) +
|
|
||||||
(p2 - p0) * t +
|
|
||||||
(2 * p0 - 5 * p1 + 4 * p2 - p3) * t * t +
|
|
||||||
(3 * p1 - p0 - 3 * p2 + p3) * t * t * t);
|
|
||||||
cpv.point = i;
|
|
||||||
return cpv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Will be unreachable if the control points were set up right
|
|
||||||
return cpv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test() {
|
|
||||||
final controlPoints = <Point2D>[];
|
|
||||||
controlPoints.add(Point2D(-1, 1));
|
|
||||||
controlPoints.add(Point2D(0, 1));
|
|
||||||
controlPoints.add(Point2D(1, -1));
|
|
||||||
controlPoints.add(Point2D(3, 4));
|
|
||||||
controlPoints.add(Point2D(10, -2));
|
|
||||||
controlPoints.add(Point2D(11, -2));
|
|
||||||
CatmullInterpolator catmull = CatmullInterpolator(controlPoints);
|
|
||||||
print(catmull.get(0));
|
|
||||||
print(catmull.get(1));
|
|
||||||
print(catmull.get(2));
|
|
||||||
print(catmull.get(5));
|
|
||||||
print(catmull.get(7));
|
|
||||||
print(catmull.get(8));
|
|
||||||
print(catmull.get(10));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class Constants {
|
|
||||||
static const Color backgroundColor = Color(0xFF000020);
|
|
||||||
static const Color timelineLineColor = Color(0x60FFFFFF);
|
|
||||||
static const Color milestoneColor = Color(0x40FFFFFF);
|
|
||||||
static const Color milestoneTimelineColor = Colors.white;
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
class ContributionData {
|
|
||||||
int weekTime;
|
|
||||||
int add;
|
|
||||||
int delete;
|
|
||||||
int change;
|
|
||||||
|
|
||||||
ContributionData(this.weekTime, this.add, this.delete, this.change);
|
|
||||||
|
|
||||||
static ContributionData fromJson(Map<String, dynamic> jsonMap) {
|
|
||||||
ContributionData data = ContributionData(
|
|
||||||
jsonMap['w'], jsonMap['a'], jsonMap['d'], jsonMap['c']);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
class DataSeries {
|
|
||||||
String label;
|
|
||||||
List<int> series;
|
|
||||||
|
|
||||||
DataSeries(this.label, this.series);
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
class StatForWeek {
|
|
||||||
int weekIndex;
|
|
||||||
int stat;
|
|
||||||
|
|
||||||
StatForWeek(this.weekIndex, this.stat);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
class User {
|
|
||||||
int id;
|
|
||||||
String username;
|
|
||||||
String avatarUrl;
|
|
||||||
|
|
||||||
User(this.id, this.username, this.avatarUrl);
|
|
||||||
|
|
||||||
static User fromJson(Map<String, dynamic> jsonMap) {
|
|
||||||
User user = User(jsonMap['id'], jsonMap['login'], jsonMap['avatar_url']);
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import 'package:github_dataviz/data/contribution_data.dart';
|
|
||||||
import 'package:github_dataviz/data/user.dart';
|
|
||||||
|
|
||||||
class UserContribution {
|
|
||||||
User user;
|
|
||||||
List<ContributionData> contributions;
|
|
||||||
|
|
||||||
UserContribution(this.user, this.contributions);
|
|
||||||
|
|
||||||
static UserContribution fromJson(Map<String, dynamic> jsonMap) {
|
|
||||||
List<ContributionData> contributionList = (jsonMap['weeks'] as List)
|
|
||||||
.map((e) => ContributionData.fromJson(e))
|
|
||||||
.toList();
|
|
||||||
var userContribution =
|
|
||||||
UserContribution(User.fromJson(jsonMap['author']), contributionList);
|
|
||||||
return userContribution;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import 'package:intl/intl.dart';
|
|
||||||
|
|
||||||
class WeekLabel {
|
|
||||||
int? weekNum;
|
|
||||||
String label;
|
|
||||||
|
|
||||||
WeekLabel(this.weekNum, this.label);
|
|
||||||
|
|
||||||
WeekLabel.forDate(DateTime date, this.label) {
|
|
||||||
int year = getYear(date);
|
|
||||||
int weekOfYearNum = getWeekNumber(date);
|
|
||||||
weekNum = 9 + ((year - 2015) * 52) + weekOfYearNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getYear(DateTime date) {
|
|
||||||
return int.parse(DateFormat('y').format(date));
|
|
||||||
}
|
|
||||||
|
|
||||||
int getWeekNumber(DateTime date) {
|
|
||||||
int dayOfYear = int.parse(DateFormat('D').format(date));
|
|
||||||
return ((dayOfYear - date.weekday + 10) / 7).floor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,336 +0,0 @@
|
|||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:github_dataviz/catmull.dart';
|
|
||||||
import 'package:github_dataviz/constants.dart';
|
|
||||||
import 'package:github_dataviz/data/data_series.dart';
|
|
||||||
import 'package:github_dataviz/data/week_label.dart';
|
|
||||||
import 'package:github_dataviz/mathutils.dart';
|
|
||||||
|
|
||||||
class LayeredChart extends StatefulWidget {
|
|
||||||
final List<DataSeries> dataToPlot;
|
|
||||||
final List<WeekLabel> milestones;
|
|
||||||
final double animationValue;
|
|
||||||
|
|
||||||
const LayeredChart(this.dataToPlot, this.milestones, this.animationValue,
|
|
||||||
{Key? key})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<LayeredChart> createState() {
|
|
||||||
return _LayeredChartState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _LayeredChartState extends State<LayeredChart> {
|
|
||||||
late List<Path> paths;
|
|
||||||
late List<Path> capPaths;
|
|
||||||
late List<double> maxValues;
|
|
||||||
late double theta;
|
|
||||||
late double graphHeight;
|
|
||||||
late List<TextPainter> labelPainter;
|
|
||||||
late List<TextPainter> milestonePainter;
|
|
||||||
Size? lastSize;
|
|
||||||
|
|
||||||
void buildPaths(
|
|
||||||
Size size,
|
|
||||||
List<DataSeries> dataToPlot,
|
|
||||||
List<WeekLabel> milestones,
|
|
||||||
int numPoints,
|
|
||||||
double graphGap,
|
|
||||||
double margin,
|
|
||||||
double capTheta,
|
|
||||||
double capSize) {
|
|
||||||
double screenRatio = size.width / size.height;
|
|
||||||
double degrees = MathUtils.clampedMap(screenRatio, 0.5, 2.5, 50, 5);
|
|
||||||
theta = pi * degrees / 180;
|
|
||||||
graphHeight = MathUtils.clampedMap(screenRatio, 0.5, 2.5, 50, 150);
|
|
||||||
|
|
||||||
int m = dataToPlot.length;
|
|
||||||
paths = [];
|
|
||||||
capPaths = [];
|
|
||||||
maxValues = [];
|
|
||||||
for (int i = 0; i < m; i++) {
|
|
||||||
int n = dataToPlot[i].series.length;
|
|
||||||
maxValues.add(0);
|
|
||||||
for (int j = 0; j < n; j++) {
|
|
||||||
double v = dataToPlot[i].series[j].toDouble();
|
|
||||||
if (v > maxValues[i]) {
|
|
||||||
maxValues[i] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
double totalGap = m * graphGap;
|
|
||||||
double xIndent = totalGap / tan(capTheta);
|
|
||||||
double startX = margin + xIndent;
|
|
||||||
double endX = size.width - margin;
|
|
||||||
double startY = size.height;
|
|
||||||
double endY = startY - (endX - startX) * tan(theta);
|
|
||||||
double xWidth = (endX - startX) / numPoints;
|
|
||||||
double capRangeX = capSize * cos(capTheta);
|
|
||||||
double tanCapTheta = tan(capTheta);
|
|
||||||
final curvePoints = <double>[];
|
|
||||||
for (int i = 0; i < m; i++) {
|
|
||||||
List<int> series = dataToPlot[i].series;
|
|
||||||
int n = series.length;
|
|
||||||
final controlPoints = <Point2D>[];
|
|
||||||
controlPoints.add(Point2D(-1, 0));
|
|
||||||
double last = 0;
|
|
||||||
for (int j = 0; j < n; j++) {
|
|
||||||
double v = series[j].toDouble();
|
|
||||||
controlPoints.add(Point2D(j.toDouble(), v));
|
|
||||||
last = v;
|
|
||||||
}
|
|
||||||
controlPoints.add(Point2D(n.toDouble(), last));
|
|
||||||
CatmullInterpolator curve = CatmullInterpolator(controlPoints);
|
|
||||||
ControlPointAndValue cpv = ControlPointAndValue();
|
|
||||||
for (int j = 0; j < numPoints; j++) {
|
|
||||||
cpv.value = MathUtils.map(
|
|
||||||
j.toDouble(), 0, (numPoints - 1).toDouble(), 0, (n - 1).toDouble());
|
|
||||||
curve.progressiveGet(cpv);
|
|
||||||
curvePoints.add(MathUtils.map(
|
|
||||||
max(0, cpv.value!), 0, maxValues[i].toDouble(), 0, graphHeight));
|
|
||||||
}
|
|
||||||
paths.add(Path());
|
|
||||||
capPaths.add(Path());
|
|
||||||
paths[i].moveTo(startX, startY);
|
|
||||||
capPaths[i].moveTo(startX, startY);
|
|
||||||
for (int j = 0; j < numPoints; j++) {
|
|
||||||
double v = curvePoints[j];
|
|
||||||
int k = j + 1;
|
|
||||||
double xDist = xWidth;
|
|
||||||
double capV = v;
|
|
||||||
while (k < numPoints && xDist <= capRangeX) {
|
|
||||||
double cy = curvePoints[k] + xDist * tanCapTheta;
|
|
||||||
capV = max(capV, cy);
|
|
||||||
k++;
|
|
||||||
xDist += xWidth;
|
|
||||||
}
|
|
||||||
double x = MathUtils.map(
|
|
||||||
j.toDouble(), 0, (numPoints - 1).toDouble(), startX, endX);
|
|
||||||
double baseY = MathUtils.map(
|
|
||||||
j.toDouble(), 0, (numPoints - 1).toDouble(), startY, endY);
|
|
||||||
double y = baseY - v;
|
|
||||||
double cY = baseY - capV;
|
|
||||||
paths[i].lineTo(x, y);
|
|
||||||
if (j == 0) {
|
|
||||||
int k = capRangeX ~/ xWidth;
|
|
||||||
double mx = MathUtils.map(
|
|
||||||
-k.toDouble(), 0, (numPoints - 1).toDouble(), startX, endX);
|
|
||||||
double my = MathUtils.map(
|
|
||||||
-k.toDouble(), 0, (numPoints - 1).toDouble(), startY, endY) -
|
|
||||||
capV;
|
|
||||||
capPaths[i].lineTo(mx, my);
|
|
||||||
}
|
|
||||||
capPaths[i].lineTo(x, cY);
|
|
||||||
}
|
|
||||||
paths[i].lineTo(endX, endY);
|
|
||||||
paths[i].lineTo(endX, endY + 1);
|
|
||||||
paths[i].lineTo(startX, startY + 1);
|
|
||||||
paths[i].close();
|
|
||||||
capPaths[i].lineTo(endX, endY);
|
|
||||||
capPaths[i].lineTo(endX, endY + 1);
|
|
||||||
capPaths[i].lineTo(startX, startY + 1);
|
|
||||||
capPaths[i].close();
|
|
||||||
}
|
|
||||||
labelPainter = [];
|
|
||||||
for (int i = 0; i < dataToPlot.length; i++) {
|
|
||||||
TextSpan span = TextSpan(
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Color.fromARGB(255, 255, 255, 255), fontSize: 12),
|
|
||||||
text: dataToPlot[i].label.toUpperCase());
|
|
||||||
TextPainter tp = TextPainter(
|
|
||||||
text: span,
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
textDirection: TextDirection.ltr);
|
|
||||||
tp.layout();
|
|
||||||
labelPainter.add(tp);
|
|
||||||
}
|
|
||||||
milestonePainter = [];
|
|
||||||
for (int i = 0; i < milestones.length; i++) {
|
|
||||||
TextSpan span = TextSpan(
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Color.fromARGB(255, 255, 255, 255), fontSize: 10),
|
|
||||||
text: milestones[i].label.toUpperCase());
|
|
||||||
TextPainter tp = TextPainter(
|
|
||||||
text: span,
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
textDirection: TextDirection.ltr);
|
|
||||||
tp.layout();
|
|
||||||
milestonePainter.add(tp);
|
|
||||||
}
|
|
||||||
lastSize = Size(size.width, size.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
color: Constants.backgroundColor,
|
|
||||||
child: CustomPaint(
|
|
||||||
foregroundPainter: _ChartPainter(this, widget.dataToPlot,
|
|
||||||
widget.milestones, 80, 50, 50, 12, 500, widget.animationValue),
|
|
||||||
child: Container()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ChartPainter extends CustomPainter {
|
|
||||||
static List<Color?> colors = [
|
|
||||||
Colors.red[900],
|
|
||||||
const Color(0xffc4721a),
|
|
||||||
Colors.lime[900],
|
|
||||||
Colors.green[900],
|
|
||||||
Colors.blue[900],
|
|
||||||
Colors.purple[900],
|
|
||||||
];
|
|
||||||
static List<Color?> capColors = [
|
|
||||||
Colors.red[500],
|
|
||||||
Colors.amber[500],
|
|
||||||
Colors.lime[500],
|
|
||||||
Colors.green[500],
|
|
||||||
Colors.blue[500],
|
|
||||||
Colors.purple[500],
|
|
||||||
];
|
|
||||||
|
|
||||||
List<DataSeries> dataToPlot;
|
|
||||||
List<WeekLabel> milestones;
|
|
||||||
|
|
||||||
double margin;
|
|
||||||
double graphGap;
|
|
||||||
late double capTheta;
|
|
||||||
double capSize;
|
|
||||||
int numPoints;
|
|
||||||
double amount = 1.0;
|
|
||||||
|
|
||||||
late Paint pathPaint;
|
|
||||||
late Paint capPaint;
|
|
||||||
late Paint textPaint;
|
|
||||||
late Paint milestonePaint;
|
|
||||||
late Paint linePaint;
|
|
||||||
late Paint fillPaint;
|
|
||||||
|
|
||||||
_LayeredChartState state;
|
|
||||||
|
|
||||||
_ChartPainter(
|
|
||||||
this.state,
|
|
||||||
this.dataToPlot,
|
|
||||||
this.milestones,
|
|
||||||
this.margin,
|
|
||||||
this.graphGap,
|
|
||||||
double capDegrees,
|
|
||||||
this.capSize,
|
|
||||||
this.numPoints,
|
|
||||||
this.amount) {
|
|
||||||
capTheta = pi * capDegrees / 180;
|
|
||||||
pathPaint = Paint();
|
|
||||||
pathPaint.style = PaintingStyle.fill;
|
|
||||||
capPaint = Paint();
|
|
||||||
capPaint.style = PaintingStyle.fill;
|
|
||||||
textPaint = Paint();
|
|
||||||
textPaint.color = const Color(0xFFFFFFFF);
|
|
||||||
milestonePaint = Paint();
|
|
||||||
milestonePaint.color = Constants.milestoneColor;
|
|
||||||
milestonePaint.style = PaintingStyle.stroke;
|
|
||||||
milestonePaint.strokeWidth = 2;
|
|
||||||
linePaint = Paint();
|
|
||||||
linePaint.style = PaintingStyle.stroke;
|
|
||||||
linePaint.strokeWidth = 0.5;
|
|
||||||
fillPaint = Paint();
|
|
||||||
fillPaint.style = PaintingStyle.fill;
|
|
||||||
fillPaint.color = const Color(0xFF000000);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
if (dataToPlot.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.lastSize == null ||
|
|
||||||
size.width != state.lastSize!.width ||
|
|
||||||
size.height != state.lastSize!.height) {
|
|
||||||
print('Building paths, lastsize = ${state.lastSize}');
|
|
||||||
state.buildPaths(size, dataToPlot, milestones, numPoints, graphGap,
|
|
||||||
margin, capTheta, capSize);
|
|
||||||
}
|
|
||||||
int m = dataToPlot.length;
|
|
||||||
int numWeeks = dataToPlot[0].series.length;
|
|
||||||
// How far along to draw
|
|
||||||
double totalGap = m * graphGap;
|
|
||||||
double xIndent = totalGap / tan(capTheta);
|
|
||||||
double dx = xIndent / (m - 1);
|
|
||||||
double startX = margin + xIndent;
|
|
||||||
double endX = size.width - margin;
|
|
||||||
double startY = size.height;
|
|
||||||
double endY = startY - (endX - startX) * tan(state.theta);
|
|
||||||
// MILESTONES
|
|
||||||
{
|
|
||||||
for (int i = 0; i < milestones.length; i++) {
|
|
||||||
WeekLabel milestone = milestones[i];
|
|
||||||
double p = (milestone.weekNum!.toDouble() / numWeeks) + (1 - amount);
|
|
||||||
if (p < 1) {
|
|
||||||
double x1 = MathUtils.map(p, 0, 1, startX, endX);
|
|
||||||
double y1 = MathUtils.map(p, 0, 1, startY, endY);
|
|
||||||
double x2 = x1 - xIndent;
|
|
||||||
double y2 = y1 - graphGap * (m - 1);
|
|
||||||
x1 += dx * 0.5;
|
|
||||||
y1 += graphGap * 0.5;
|
|
||||||
double textY = y1 + 5;
|
|
||||||
double textX = x1 + 5 * tan(capTheta);
|
|
||||||
canvas.drawLine(Offset(x1, y1), Offset(x2, y2), milestonePaint);
|
|
||||||
canvas.save();
|
|
||||||
TextPainter tp = state.milestonePainter[i];
|
|
||||||
canvas.translate(textX, textY);
|
|
||||||
canvas.skew(tan(capTheta * 1.0), -tan(state.theta));
|
|
||||||
canvas.translate(-tp.width / 2, 0);
|
|
||||||
tp.paint(canvas, const Offset(0, 0));
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = m - 1; i >= 0; i--) {
|
|
||||||
canvas.save();
|
|
||||||
canvas.translate(-dx * i, -graphGap * i);
|
|
||||||
|
|
||||||
{
|
|
||||||
// TEXT LABELS
|
|
||||||
canvas.save();
|
|
||||||
double textPosition = 0.2;
|
|
||||||
double textX = MathUtils.map(textPosition, 0, 1, startX, endX);
|
|
||||||
double textY = MathUtils.map(textPosition, 0, 1, startY, endY) + 5;
|
|
||||||
canvas.translate(textX, textY);
|
|
||||||
TextPainter tp = state.labelPainter[i];
|
|
||||||
canvas.skew(0, -tan(state.theta));
|
|
||||||
canvas.drawRect(
|
|
||||||
Rect.fromLTWH(-1, -1, tp.width + 2, tp.height + 2), fillPaint);
|
|
||||||
tp.paint(canvas, const Offset(0, 0));
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
linePaint.color = capColors[i]!;
|
|
||||||
canvas.drawLine(Offset(startX, startY), Offset(endX, endY), linePaint);
|
|
||||||
|
|
||||||
Path clipPath = Path();
|
|
||||||
clipPath.moveTo(startX - capSize, startY + 11);
|
|
||||||
clipPath.lineTo(endX, endY + 1);
|
|
||||||
clipPath.lineTo(endX, endY - state.graphHeight - capSize);
|
|
||||||
clipPath.lineTo(startX - capSize, startY - state.graphHeight - capSize);
|
|
||||||
clipPath.close();
|
|
||||||
canvas.clipPath(clipPath);
|
|
||||||
|
|
||||||
pathPaint.color = colors[i]!;
|
|
||||||
capPaint.color = capColors[i]!;
|
|
||||||
double offsetX = MathUtils.map(1 - amount, 0, 1, startX, endX);
|
|
||||||
double offsetY = MathUtils.map(1 - amount, 0, 1, startY, endY);
|
|
||||||
canvas.translate(offsetX - startX, offsetY - startY);
|
|
||||||
canvas.drawPath(state.capPaths[i], capPaint);
|
|
||||||
canvas.drawPath(state.paths[i], pathPaint);
|
|
||||||
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldRepaint(CustomPainter oldDelegate) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,259 +0,0 @@
|
|||||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:collection';
|
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:github_dataviz/constants.dart';
|
|
||||||
import 'package:github_dataviz/data/contribution_data.dart';
|
|
||||||
import 'package:github_dataviz/data/data_series.dart';
|
|
||||||
import 'package:github_dataviz/data/stat_for_week.dart';
|
|
||||||
import 'package:github_dataviz/data/user_contribution.dart';
|
|
||||||
import 'package:github_dataviz/data/week_label.dart';
|
|
||||||
import 'package:github_dataviz/layered_chart.dart';
|
|
||||||
import 'package:github_dataviz/mathutils.dart';
|
|
||||||
import 'package:github_dataviz/timeline.dart';
|
|
||||||
|
|
||||||
class MainLayout extends StatefulWidget {
|
|
||||||
const MainLayout({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MainLayout> createState() => _MainLayoutState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MainLayoutState extends State<MainLayout> with TickerProviderStateMixin {
|
|
||||||
AnimationController? _animation;
|
|
||||||
List<UserContribution>? contributions;
|
|
||||||
List<StatForWeek>? starsByWeek;
|
|
||||||
List<StatForWeek>? forksByWeek;
|
|
||||||
List<StatForWeek>? pushesByWeek;
|
|
||||||
List<StatForWeek>? issueCommentsByWeek;
|
|
||||||
List<StatForWeek>? pullRequestActivityByWeek;
|
|
||||||
late List<WeekLabel> weekLabels;
|
|
||||||
|
|
||||||
static const double earlyInterpolatorFraction = 0.8;
|
|
||||||
static final EarlyInterpolator interpolator =
|
|
||||||
EarlyInterpolator(earlyInterpolatorFraction);
|
|
||||||
double animationValue = 1.0;
|
|
||||||
double interpolatedAnimationValue = 1.0;
|
|
||||||
bool timelineOverride = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
createAnimation(0);
|
|
||||||
|
|
||||||
weekLabels = [];
|
|
||||||
weekLabels.add(WeekLabel.forDate(DateTime(2019, 2, 26), 'v1.2'));
|
|
||||||
weekLabels.add(WeekLabel.forDate(DateTime(2018, 12, 4), 'v1.0'));
|
|
||||||
// weekLabels.add(WeekLabel.forDate(new DateTime(2018, 9, 19), "Preview 2"));
|
|
||||||
weekLabels.add(WeekLabel.forDate(DateTime(2018, 6, 21), 'Preview 1'));
|
|
||||||
// weekLabels.add(WeekLabel.forDate(new DateTime(2018, 5, 7), "Beta 3"));
|
|
||||||
weekLabels.add(WeekLabel.forDate(DateTime(2018, 2, 27), 'Beta 1'));
|
|
||||||
weekLabels.add(WeekLabel.forDate(DateTime(2017, 5, 1), 'Alpha'));
|
|
||||||
weekLabels.add(WeekLabel(48, 'Repo Made Public'));
|
|
||||||
|
|
||||||
loadGitHubData();
|
|
||||||
}
|
|
||||||
|
|
||||||
void createAnimation(double startValue) {
|
|
||||||
_animation?.dispose();
|
|
||||||
_animation = AnimationController(
|
|
||||||
value: startValue,
|
|
||||||
duration: const Duration(milliseconds: 14400),
|
|
||||||
vsync: this,
|
|
||||||
)..repeat();
|
|
||||||
_animation!.addListener(() {
|
|
||||||
setState(() {
|
|
||||||
if (!timelineOverride) {
|
|
||||||
animationValue = _animation!.value;
|
|
||||||
interpolatedAnimationValue = interpolator.get(animationValue);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
// Combined contributions data
|
|
||||||
List<DataSeries> dataToPlot = [];
|
|
||||||
if (contributions != null) {
|
|
||||||
List<int> series = [];
|
|
||||||
for (UserContribution userContrib in contributions!) {
|
|
||||||
for (int i = 0; i < userContrib.contributions.length; i++) {
|
|
||||||
ContributionData data = userContrib.contributions[i];
|
|
||||||
if (series.length > i) {
|
|
||||||
series[i] = series[i] + data.add;
|
|
||||||
} else {
|
|
||||||
series.add(data.add);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dataToPlot.add(DataSeries('Added Lines', series));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (starsByWeek != null) {
|
|
||||||
dataToPlot
|
|
||||||
.add(DataSeries('Stars', starsByWeek!.map((e) => e.stat).toList()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forksByWeek != null) {
|
|
||||||
dataToPlot
|
|
||||||
.add(DataSeries('Forks', forksByWeek!.map((e) => e.stat).toList()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pushesByWeek != null) {
|
|
||||||
dataToPlot
|
|
||||||
.add(DataSeries('Pushes', pushesByWeek!.map((e) => e.stat).toList()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (issueCommentsByWeek != null) {
|
|
||||||
dataToPlot.add(DataSeries(
|
|
||||||
'Issue Comments', issueCommentsByWeek!.map((e) => e.stat).toList()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pullRequestActivityByWeek != null) {
|
|
||||||
dataToPlot.add(DataSeries('Pull Request Activity',
|
|
||||||
pullRequestActivityByWeek!.map((e) => e.stat).toList()));
|
|
||||||
}
|
|
||||||
|
|
||||||
LayeredChart layeredChart =
|
|
||||||
LayeredChart(dataToPlot, weekLabels, interpolatedAnimationValue);
|
|
||||||
|
|
||||||
const double timelinePadding = 60.0;
|
|
||||||
|
|
||||||
var timeline = Timeline(
|
|
||||||
numWeeks: dataToPlot.isNotEmpty ? dataToPlot.last.series.length : 0,
|
|
||||||
animationValue: interpolatedAnimationValue,
|
|
||||||
weekLabels: weekLabels,
|
|
||||||
mouseDownCallback: (double xFraction) {
|
|
||||||
setState(() {
|
|
||||||
timelineOverride = true;
|
|
||||||
_animation?.stop();
|
|
||||||
interpolatedAnimationValue = xFraction;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
mouseMoveCallback: (double xFraction) {
|
|
||||||
setState(() {
|
|
||||||
interpolatedAnimationValue = xFraction;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
mouseUpCallback: () {
|
|
||||||
setState(() {
|
|
||||||
timelineOverride = false;
|
|
||||||
createAnimation(
|
|
||||||
interpolatedAnimationValue * earlyInterpolatorFraction);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Column mainColumn = Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
children: [
|
|
||||||
Expanded(child: layeredChart),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: timelinePadding,
|
|
||||||
right: timelinePadding,
|
|
||||||
bottom: timelinePadding),
|
|
||||||
child: timeline,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
color: Constants.backgroundColor,
|
|
||||||
child:
|
|
||||||
Directionality(textDirection: TextDirection.ltr, child: mainColumn),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_animation?.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future loadGitHubData() async {
|
|
||||||
String contributorsJsonStr =
|
|
||||||
(await http.get(Uri.parse('assets/github_data/contributors.json')))
|
|
||||||
.body;
|
|
||||||
List jsonObjs = jsonDecode(contributorsJsonStr) as List;
|
|
||||||
List<UserContribution> contributionList =
|
|
||||||
jsonObjs.map((e) => UserContribution.fromJson(e)).toList();
|
|
||||||
print(
|
|
||||||
'Loaded ${contributionList.length} code contributions to /flutter/flutter repo.');
|
|
||||||
|
|
||||||
int numWeeksTotal = contributionList[0].contributions.length;
|
|
||||||
|
|
||||||
String starsByWeekStr =
|
|
||||||
(await http.get(Uri.parse('assets/github_data/stars.tsv'))).body;
|
|
||||||
List<StatForWeek> starsByWeekLoaded =
|
|
||||||
summarizeWeeksFromTSV(starsByWeekStr, numWeeksTotal);
|
|
||||||
|
|
||||||
String forksByWeekStr =
|
|
||||||
(await http.get(Uri.parse('assets/github_data/forks.tsv'))).body;
|
|
||||||
List<StatForWeek> forksByWeekLoaded =
|
|
||||||
summarizeWeeksFromTSV(forksByWeekStr, numWeeksTotal);
|
|
||||||
|
|
||||||
String commitsByWeekStr =
|
|
||||||
(await http.get(Uri.parse('assets/github_data/commits.tsv'))).body;
|
|
||||||
List<StatForWeek> commitsByWeekLoaded =
|
|
||||||
summarizeWeeksFromTSV(commitsByWeekStr, numWeeksTotal);
|
|
||||||
|
|
||||||
String commentsByWeekStr =
|
|
||||||
(await http.get(Uri.parse('assets/github_data/comments.tsv'))).body;
|
|
||||||
List<StatForWeek> commentsByWeekLoaded =
|
|
||||||
summarizeWeeksFromTSV(commentsByWeekStr, numWeeksTotal);
|
|
||||||
|
|
||||||
String pullRequestActivityByWeekStr =
|
|
||||||
(await http.get(Uri.parse('assets/github_data/pull_requests.tsv')))
|
|
||||||
.body;
|
|
||||||
List<StatForWeek> pullRequestActivityByWeekLoaded =
|
|
||||||
summarizeWeeksFromTSV(pullRequestActivityByWeekStr, numWeeksTotal);
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
contributions = contributionList;
|
|
||||||
starsByWeek = starsByWeekLoaded;
|
|
||||||
forksByWeek = forksByWeekLoaded;
|
|
||||||
pushesByWeek = commitsByWeekLoaded;
|
|
||||||
issueCommentsByWeek = commentsByWeekLoaded;
|
|
||||||
pullRequestActivityByWeek = pullRequestActivityByWeekLoaded;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
List<StatForWeek> summarizeWeeksFromTSV(
|
|
||||||
String statByWeekStr, int numWeeksTotal) {
|
|
||||||
List<StatForWeek> loadedStats = [];
|
|
||||||
HashMap<int, StatForWeek> statMap = HashMap();
|
|
||||||
statByWeekStr.split('\n').forEach((s) {
|
|
||||||
List<String> split = s.split('\t');
|
|
||||||
if (split.length == 2) {
|
|
||||||
int weekNum = int.parse(split[0]);
|
|
||||||
statMap[weekNum] = StatForWeek(weekNum, int.parse(split[1]));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
print('Loaded ${statMap.length} weeks.');
|
|
||||||
|
|
||||||
// Convert into a list by week, but fill in empty weeks with 0
|
|
||||||
for (int i = 0; i < numWeeksTotal; i++) {
|
|
||||||
StatForWeek? starsForWeek = statMap[i];
|
|
||||||
if (starsForWeek == null) {
|
|
||||||
loadedStats.add(StatForWeek(i, 0));
|
|
||||||
} else {
|
|
||||||
loadedStats.add(starsForWeek);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return loadedStats;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
runApp(const Center(child: MainLayout()));
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
abstract class Interpolator {
|
|
||||||
double get(double x);
|
|
||||||
}
|
|
||||||
|
|
||||||
class EarlyInterpolator implements Interpolator {
|
|
||||||
double amount;
|
|
||||||
|
|
||||||
EarlyInterpolator(this.amount);
|
|
||||||
|
|
||||||
@override
|
|
||||||
double get(double x) {
|
|
||||||
if (x >= amount) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return MathUtils.map(x, 0, amount, 0, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Point2D {
|
|
||||||
double x, y;
|
|
||||||
|
|
||||||
Point2D(this.x, this.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
class MathUtils {
|
|
||||||
static double map(double x, double a, double b, double u, double v) {
|
|
||||||
double p = (x - a) / (b - a);
|
|
||||||
return u + p * (v - u);
|
|
||||||
}
|
|
||||||
|
|
||||||
static double clampedMap(double x, double a, double b, double u, double v) {
|
|
||||||
if (x <= a) {
|
|
||||||
return u;
|
|
||||||
} else if (x >= b) {
|
|
||||||
return v;
|
|
||||||
} else {
|
|
||||||
double p = (x - a) / (b - a);
|
|
||||||
return u + p * (v - u);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static double clamp(double x, double a, double b) {
|
|
||||||
if (x < a) return a;
|
|
||||||
if (x > b) return b;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
import 'dart:collection';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:github_dataviz/constants.dart';
|
|
||||||
import 'package:github_dataviz/data/week_label.dart';
|
|
||||||
import 'package:github_dataviz/mathutils.dart';
|
|
||||||
|
|
||||||
typedef MouseDownCallback = void Function(double xFraction);
|
|
||||||
typedef MouseMoveCallback = void Function(double xFraction);
|
|
||||||
typedef MouseUpCallback = void Function();
|
|
||||||
|
|
||||||
class Timeline extends StatefulWidget {
|
|
||||||
final int numWeeks;
|
|
||||||
final double animationValue;
|
|
||||||
final List<WeekLabel> weekLabels;
|
|
||||||
|
|
||||||
final MouseDownCallback? mouseDownCallback;
|
|
||||||
final MouseMoveCallback? mouseMoveCallback;
|
|
||||||
final MouseUpCallback? mouseUpCallback;
|
|
||||||
|
|
||||||
const Timeline(
|
|
||||||
{required this.numWeeks,
|
|
||||||
required this.animationValue,
|
|
||||||
required this.weekLabels,
|
|
||||||
this.mouseDownCallback,
|
|
||||||
this.mouseMoveCallback,
|
|
||||||
this.mouseUpCallback,
|
|
||||||
Key? key})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<Timeline> createState() {
|
|
||||||
return _TimelineState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _TimelineState extends State<Timeline> {
|
|
||||||
HashMap<String, TextPainter> labelPainters = HashMap();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
for (int year = 2015; year < 2020; year++) {
|
|
||||||
String yearLabel = '$year';
|
|
||||||
labelPainters[yearLabel] =
|
|
||||||
_makeTextPainter(Constants.timelineLineColor, yearLabel);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var weekLabel in widget.weekLabels) {
|
|
||||||
labelPainters[weekLabel.label] =
|
|
||||||
_makeTextPainter(Constants.milestoneTimelineColor, weekLabel.label);
|
|
||||||
labelPainters['${weekLabel.label}_red'] =
|
|
||||||
_makeTextPainter(Colors.redAccent, weekLabel.label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GestureDetector(
|
|
||||||
behavior: HitTestBehavior.translucent,
|
|
||||||
onHorizontalDragDown: (DragDownDetails details) {
|
|
||||||
final mouseDownCallback = widget.mouseDownCallback;
|
|
||||||
if (mouseDownCallback != null) {
|
|
||||||
mouseDownCallback(
|
|
||||||
_getClampedXFractionLocalCoords(context, details.globalPosition));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onHorizontalDragEnd: (DragEndDetails details) {
|
|
||||||
final mouseUpCallback = widget.mouseUpCallback;
|
|
||||||
if (mouseUpCallback != null) {
|
|
||||||
mouseUpCallback();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onHorizontalDragUpdate: (DragUpdateDetails details) {
|
|
||||||
final mouseMoveCallback = widget.mouseMoveCallback;
|
|
||||||
if (mouseMoveCallback != null) {
|
|
||||||
mouseMoveCallback(
|
|
||||||
_getClampedXFractionLocalCoords(context, details.globalPosition));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: CustomPaint(
|
|
||||||
foregroundPainter: _TimelinePainter(
|
|
||||||
this, widget.numWeeks, widget.animationValue, widget.weekLabels),
|
|
||||||
child: Container(
|
|
||||||
height: 200,
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextPainter _makeTextPainter(Color color, String label) {
|
|
||||||
TextSpan span =
|
|
||||||
TextSpan(style: TextStyle(color: color, fontSize: 12), text: label);
|
|
||||||
TextPainter tp = TextPainter(
|
|
||||||
text: span,
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
textDirection: TextDirection.ltr);
|
|
||||||
tp.layout();
|
|
||||||
return tp;
|
|
||||||
}
|
|
||||||
|
|
||||||
double _getClampedXFractionLocalCoords(
|
|
||||||
BuildContext context, Offset globalOffset) {
|
|
||||||
final RenderBox box = context.findRenderObject() as RenderBox;
|
|
||||||
final Offset localOffset = box.globalToLocal(globalOffset);
|
|
||||||
return MathUtils.clamp(localOffset.dx / context.size!.width, 0, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _TimelinePainter extends CustomPainter {
|
|
||||||
_TimelineState state;
|
|
||||||
|
|
||||||
late Paint mainLinePaint;
|
|
||||||
late Paint milestoneLinePaint;
|
|
||||||
|
|
||||||
Color lineColor = Colors.white;
|
|
||||||
|
|
||||||
int numWeeks;
|
|
||||||
double animationValue;
|
|
||||||
int weekYearOffset =
|
|
||||||
9; // Week 0 in our data is 9 weeks before the year boundary (i.e. week 43)
|
|
||||||
|
|
||||||
List<WeekLabel> weekLabels;
|
|
||||||
|
|
||||||
int yearNumber = 2015;
|
|
||||||
|
|
||||||
_TimelinePainter(
|
|
||||||
this.state, this.numWeeks, this.animationValue, this.weekLabels) {
|
|
||||||
mainLinePaint = Paint();
|
|
||||||
mainLinePaint.style = PaintingStyle.stroke;
|
|
||||||
mainLinePaint.color = Constants.timelineLineColor;
|
|
||||||
milestoneLinePaint = Paint();
|
|
||||||
milestoneLinePaint.style = PaintingStyle.stroke;
|
|
||||||
milestoneLinePaint.color = Constants.milestoneTimelineColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
double labelHeight = 20;
|
|
||||||
double labelHeightDoubled = labelHeight * 2;
|
|
||||||
|
|
||||||
double mainLineY = size.height / 2;
|
|
||||||
canvas.drawLine(
|
|
||||||
Offset(0, mainLineY), Offset(size.width, mainLineY), mainLinePaint);
|
|
||||||
|
|
||||||
double currTimeX = size.width * animationValue;
|
|
||||||
canvas.drawLine(
|
|
||||||
Offset(currTimeX, labelHeightDoubled),
|
|
||||||
Offset(currTimeX, size.height - labelHeightDoubled),
|
|
||||||
milestoneLinePaint);
|
|
||||||
|
|
||||||
{
|
|
||||||
for (int week = 0; week < numWeeks; week++) {
|
|
||||||
double lineHeight = size.height / 32;
|
|
||||||
bool isYear = false;
|
|
||||||
if ((week - 9) % 52 == 0) {
|
|
||||||
// Year
|
|
||||||
isYear = true;
|
|
||||||
lineHeight = size.height / 2;
|
|
||||||
} else if ((week - 1) % 4 == 0) {
|
|
||||||
// Month
|
|
||||||
lineHeight = size.height / 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
double currX = (week / numWeeks.toDouble()) * size.width;
|
|
||||||
if (lineHeight > 0) {
|
|
||||||
double margin = (size.height - lineHeight) / 2;
|
|
||||||
double currTimeXDiff = (currTimeX - currX) / size.width;
|
|
||||||
if (currTimeXDiff > 0) {
|
|
||||||
var mappedValue =
|
|
||||||
MathUtils.clampedMap(currTimeXDiff, 0, 0.025, 0, 1);
|
|
||||||
var lerpedColor = Color.lerp(Constants.milestoneTimelineColor,
|
|
||||||
Constants.timelineLineColor, mappedValue)!;
|
|
||||||
mainLinePaint.color = lerpedColor;
|
|
||||||
} else {
|
|
||||||
mainLinePaint.color = Constants.timelineLineColor;
|
|
||||||
}
|
|
||||||
canvas.drawLine(Offset(currX, margin),
|
|
||||||
Offset(currX, size.height - margin), mainLinePaint);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isYear) {
|
|
||||||
var yearLabel = '$yearNumber';
|
|
||||||
state.labelPainters[yearLabel]!
|
|
||||||
.paint(canvas, Offset(currX, size.height - labelHeight));
|
|
||||||
yearNumber++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
for (int i = 0; i < weekLabels.length; i++) {
|
|
||||||
WeekLabel weekLabel = weekLabels[i];
|
|
||||||
double currX = (weekLabel.weekNum! / numWeeks.toDouble()) * size.width;
|
|
||||||
var timelineXDiff = (currTimeX - currX) / size.width;
|
|
||||||
double maxTimelineDiff = 0.08;
|
|
||||||
TextPainter textPainter = state.labelPainters[weekLabel.label]!;
|
|
||||||
if (timelineXDiff > 0 &&
|
|
||||||
timelineXDiff < maxTimelineDiff &&
|
|
||||||
animationValue < 1) {
|
|
||||||
var mappedValue =
|
|
||||||
MathUtils.clampedMap(timelineXDiff, 0, maxTimelineDiff, 0, 1);
|
|
||||||
var lerpedColor = Color.lerp(
|
|
||||||
Colors.redAccent, Constants.milestoneTimelineColor, mappedValue)!;
|
|
||||||
milestoneLinePaint.strokeWidth =
|
|
||||||
MathUtils.clampedMap(timelineXDiff, 0, maxTimelineDiff, 6, 1);
|
|
||||||
milestoneLinePaint.color = lerpedColor;
|
|
||||||
} else {
|
|
||||||
milestoneLinePaint.strokeWidth = 1;
|
|
||||||
milestoneLinePaint.color = Constants.milestoneTimelineColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
double lineHeight = size.height / 2;
|
|
||||||
double margin = (size.height - lineHeight) / 2;
|
|
||||||
canvas.drawLine(Offset(currX, margin),
|
|
||||||
Offset(currX, size.height - margin), milestoneLinePaint);
|
|
||||||
|
|
||||||
textPainter.paint(
|
|
||||||
canvas, Offset(currX, size.height - labelHeightDoubled));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldRepaint(CustomPainter oldDelegate) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
name: github_dataviz
|
|
||||||
|
|
||||||
environment:
|
|
||||||
sdk: ">=2.17.0-0 <3.0.0"
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
intl: ^0.17.0
|
|
||||||
http: ^0.13.4
|
|
||||||
|
|
||||||
dev_dependencies:
|
|
||||||
flutter_lints: ^2.0.1
|
|
||||||
|
|
||||||
flutter:
|
|
||||||
assets:
|
|
||||||
- preview.png
|
|
||||||
- github_data/
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title></title>
|
|
||||||
<script defer src="main.dart.js" type="application/javascript"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -617,23 +617,6 @@ samples:
|
|||||||
type: demo
|
type: demo
|
||||||
web: web/game_template
|
web: web/game_template
|
||||||
|
|
||||||
- name: GitHub Dataviz
|
|
||||||
author: Larva Labs
|
|
||||||
screenshots:
|
|
||||||
- url: images/github_dataviz1.png
|
|
||||||
alt: GitHub Dataviz screenshot
|
|
||||||
source: https://github.com/flutter/samples/tree/main/web/github_dataviz
|
|
||||||
description: >
|
|
||||||
A visualization for Flutter repository data
|
|
||||||
difficulty: intermediate
|
|
||||||
widgets: []
|
|
||||||
packages: []
|
|
||||||
platforms: ['web']
|
|
||||||
links: []
|
|
||||||
tags: ['demo', 'data', 'visualization']
|
|
||||||
web: web/github_dataviz
|
|
||||||
type: demo
|
|
||||||
|
|
||||||
- name: Dice
|
- name: Dice
|
||||||
author: Jaime Blasco
|
author: Jaime Blasco
|
||||||
screenshots:
|
screenshots:
|
||||||
|
|||||||
Reference in New Issue
Block a user