Skip to content

Commit d2d38d3

Browse files
committed
First version of timeline and labels.
1 parent 414e1e8 commit d2d38d3

File tree

6 files changed

+155
-22
lines changed

6 files changed

+155
-22
lines changed

lib/constants.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import 'package:flutter_web_ui/ui.dart';
2+
3+
class Constants {
4+
static final Color backgroundColor = const Color(0xFF000020);
5+
}

lib/data/week_label.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import 'package:intl/intl.dart';
2+
3+
class WeekLabel {
4+
int weekNum;
5+
String label;
6+
7+
WeekLabel(this.weekNum, this.label);
8+
9+
WeekLabel.forDate(DateTime date, String label) {
10+
this.label = label;
11+
int year = getYear(date);
12+
int weekOfYearNum = getWeekNumber(date);
13+
this.weekNum = 9 + ((year - 2015) * 52) + weekOfYearNum;
14+
}
15+
16+
int getYear(DateTime date) {
17+
return int.parse(DateFormat("y").format(date));
18+
}
19+
20+
int getWeekNumber(DateTime date) {
21+
int dayOfYear = int.parse(DateFormat("D").format(date));
22+
return ((dayOfYear - date.weekday + 10) / 7).floor();
23+
}
24+
25+
26+
}
27+
28+
void main() {
29+
var weekLabel = WeekLabel.forDate(new DateTime(2019, 2, 26), "Testing");
30+
print("${weekLabel.weekNum}");
31+
32+
}

lib/layered_chart.dart

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
import 'dart:math';
22

33
import 'package:flutter_web.examples.github_dataviz/catmull.dart';
4+
import 'package:flutter_web.examples.github_dataviz/constants.dart';
45
import 'package:flutter_web.examples.github_dataviz/data/data_series.dart';
56
import 'package:flutter_web.examples.github_dataviz/data/milestone.dart';
7+
import 'package:flutter_web.examples.github_dataviz/data/week_label.dart';
68
import 'package:flutter_web.examples.github_dataviz/mathutils.dart';
79
import 'package:flutter_web/material.dart';
810
import 'package:flutter_web/widgets.dart';
911
import 'package:flutter_web/painting.dart';
1012

1113
class LayeredChart extends StatefulWidget {
1214
List<DataSeries> dataToPlot;
13-
List<Milestone> milestones;
15+
List<WeekLabel> milestones;
1416
double animationValue;
1517

16-
static EarlyInterpolator interpolator = new EarlyInterpolator(0.8);
17-
1818
LayeredChart(this.dataToPlot, this.milestones, animationValue) {
19-
this.animationValue = interpolator.get(animationValue);
19+
this.animationValue = animationValue;
2020
}
2121

2222
@override
@@ -34,7 +34,7 @@ class LayeredChartState extends State<LayeredChart> {
3434
List<TextPainter> milestonePainter;
3535
Size lastSize = null;
3636

37-
void buildPaths(Size size, List<DataSeries> dataToPlot, List<Milestone> milestones, int numPoints, double graphHeight, double graphGap, double margin, double theta, double capTheta, double capSize) {
37+
void buildPaths(Size size, List<DataSeries> dataToPlot, List<WeekLabel> milestones, int numPoints, double graphHeight, double graphGap, double margin, double theta, double capTheta, double capSize) {
3838
int m = dataToPlot.length;
3939
paths = new List<Path>(m);
4040
capPaths = new List<Path>(m);
@@ -132,15 +132,15 @@ class LayeredChartState extends State<LayeredChart> {
132132
@override
133133
Widget build(BuildContext context) {
134134
return new Container(
135-
color: const Color(0xFF000020),
135+
color: Constants.backgroundColor,
136136
child: new CustomPaint(foregroundPainter: new ChartPainter(this, widget.dataToPlot, widget.milestones, 80, 200, 110, 10, 50, 10, 500, widget.animationValue), child: new Container()));
137137
}
138138
}
139139

140140
class ChartPainter extends CustomPainter {
141141

142142
List<DataSeries> dataToPlot;
143-
List<Milestone> milestones;
143+
List<WeekLabel> milestones;
144144

145145
double margin;
146146
double graphHeight;
@@ -201,6 +201,7 @@ class ChartPainter extends CustomPainter {
201201
Colors.purple[500],
202202
];
203203
int m = dataToPlot.length;
204+
int numWeeks = dataToPlot[0].series.length;
204205
// How far along to draw
205206
double totalGap = m * graphGap;
206207
double xIndent = totalGap / tan(capTheta);
@@ -218,8 +219,8 @@ class ChartPainter extends CustomPainter {
218219
// MILESTONES
219220
{
220221
for (int i = 0; i < milestones.length; i++) {
221-
Milestone milestone = milestones[i];
222-
double p = milestone.position + (1 - amount);
222+
WeekLabel milestone = milestones[i];
223+
double p = (milestone.weekNum.toDouble() / numWeeks) + (1 - amount);
223224
if (p < 1) {
224225
double x1 = MathUtils.map(p, 0, 1, startX, endX);
225226
double y1 = MathUtils.map(p, 0, 1, startY, endY);

lib/main.dart

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@ import 'dart:collection';
66
import 'dart:convert';
77
import 'dart:html';
88

9+
import 'package:flutter_web.examples.github_dataviz/constants.dart';
910
import 'package:flutter_web.examples.github_dataviz/data/contribution_data.dart';
1011
import 'package:flutter_web.examples.github_dataviz/data/data_series.dart';
1112
import 'package:flutter_web.examples.github_dataviz/data/milestone.dart';
1213
import 'package:flutter_web.examples.github_dataviz/data/stat_for_week.dart';
1314
import 'package:flutter_web.examples.github_dataviz/data/user_contribution.dart';
15+
import 'package:flutter_web.examples.github_dataviz/data/week_label.dart';
1416
import 'package:flutter_web.examples.github_dataviz/layered_chart.dart';
17+
import 'package:flutter_web.examples.github_dataviz/mathutils.dart';
1518
import 'package:flutter_web.examples.github_dataviz/timeline.dart';
1619
import 'package:flutter_web/io.dart';
1720
import 'package:flutter_web/material.dart';
@@ -31,7 +34,11 @@ class _MainLayoutState extends State<MainLayout>
3134
List<StatForWeek> pushesByWeek;
3235
List<StatForWeek> issueCommentsByWeek;
3336
List<StatForWeek> pullRequestActivityByWeek;
37+
List<WeekLabel> weekLabels;
38+
39+
static EarlyInterpolator interpolator = new EarlyInterpolator(0.8);
3440
double animationValue = 1.0;
41+
double interpolatedAnimationValue;
3542

3643
@override
3744
void initState() {
@@ -46,9 +53,19 @@ class _MainLayoutState extends State<MainLayout>
4653
_animation.addListener(() {
4754
setState(() {
4855
animationValue = _animation.value;
56+
interpolatedAnimationValue = interpolator.get(animationValue);
4957
});
5058
// print("New anim value ${value}");
5159
});
60+
61+
weekLabels = new List();
62+
weekLabels.add(WeekLabel.forDate(new DateTime(2019, 2, 26), "v1.2"));
63+
weekLabels.add(WeekLabel.forDate(new DateTime(2018, 12, 4), "v1.0"));
64+
weekLabels.add(WeekLabel.forDate(new DateTime(2018, 9, 19), "Preview 2"));
65+
weekLabels.add(WeekLabel.forDate(new DateTime(2018, 6, 21), "Preview 1"));
66+
weekLabels.add(WeekLabel.forDate(new DateTime(2018, 5, 7), "Beta 3"));
67+
weekLabels.add(WeekLabel.forDate(new DateTime(2018, 2, 27), "Beta 1"));
68+
weekLabels.add(WeekLabel.forDate(new DateTime(2017, 5, 1), "Alpha"));
5269

5370
loadGitHubData();
5471
}
@@ -98,25 +115,28 @@ class _MainLayoutState extends State<MainLayout>
98115
milestones.add(new Milestone(new DateTime.now(), 0.25, "Beta"));
99116
milestones.add(new Milestone(new DateTime.now(), 0.7, "1.0"));
100117

101-
LayeredChart layeredChart = new LayeredChart(dataToPlot, milestones, animationValue);
118+
LayeredChart layeredChart = new LayeredChart(dataToPlot, weekLabels, interpolatedAnimationValue);
102119

103-
List<ContributionData> timelineData = new List<ContributionData>();
104-
if (contributions != null) {
105-
timelineData = contributions[0].contributions;
106-
}
107-
Timeline timeline = new Timeline(timelineData);
120+
Timeline timeline = new Timeline(dataToPlot != null ? dataToPlot.last.series.length : 0, interpolatedAnimationValue);
108121

109122
Column mainColumn = new Column(
110123
mainAxisAlignment: MainAxisAlignment.center,
111124
mainAxisSize: MainAxisSize.max,
112125
children: <Widget>[
113126
new Expanded(child: layeredChart),
127+
Padding(
128+
padding: const EdgeInsets.all(60.0),
129+
child: timeline,
130+
),
114131
],
115132
);
116133

117-
return new Directionality(
118-
textDirection: TextDirection.ltr,
119-
child: mainColumn
134+
return new Container(
135+
color: Constants.backgroundColor,
136+
child: new Directionality(
137+
textDirection: TextDirection.ltr,
138+
child: mainColumn
139+
),
120140
);
121141
}
122142

lib/timeline.dart

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import 'package:flutter_web.examples.github_dataviz/data/contribution_data.dart'
22
import 'package:flutter_web/material.dart';
33

44
class Timeline extends StatefulWidget {
5-
List<ContributionData> contributions;
5+
int numWeeks;
6+
double animationValue;
67

7-
Timeline(this.contributions);
8+
Timeline(this.numWeeks, this.animationValue);
89

910
@override
1011
State<StatefulWidget> createState() {
@@ -15,7 +16,76 @@ class Timeline extends StatefulWidget {
1516
class TimelineState extends State<Timeline> {
1617
@override
1718
Widget build(BuildContext context) {
18-
var totalLoaded = widget.contributions != null ? widget.contributions.length : 0;
19-
return new Text("Weeks loaded: ${totalLoaded}");
19+
return new Container(
20+
child: new CustomPaint(foregroundPainter: new TimelinePainter(widget.numWeeks, widget.animationValue), child: new Container(height: 200,)));
2021
}
22+
}
23+
24+
class TimelinePainter extends CustomPainter {
25+
26+
Paint mainLinePaint;
27+
28+
Color lineColor = Colors.white;
29+
30+
int numWeeks;
31+
double animationValue;
32+
int weekYearOffset = 9; // Week 0 in our data is 9 weeks before the year boundary (i.e. week 43)
33+
34+
int yearNumber = 2015;
35+
36+
TimelinePainter(this.numWeeks, this.animationValue) {
37+
mainLinePaint = new Paint();
38+
mainLinePaint.style = PaintingStyle.stroke;
39+
mainLinePaint.color = lineColor;
40+
}
41+
42+
@override
43+
void paint(Canvas canvas, Size size) {
44+
45+
double yearHeight = 20;
46+
47+
double mainLineY = size.height/2;
48+
canvas.drawLine(new Offset(0, mainLineY), new Offset(size.width, mainLineY), mainLinePaint);
49+
50+
{
51+
double currWeekX = size.width * animationValue;
52+
canvas.drawLine(new Offset(currWeekX, yearHeight), new Offset(currWeekX, size.height - yearHeight), mainLinePaint);
53+
}
54+
55+
{
56+
for (int week=0; week<numWeeks; week++) {
57+
double lineHeight = size.height/32;
58+
bool isYear = false;
59+
if ((week - 9) % 52 == 0) {
60+
// Year
61+
isYear = true;
62+
lineHeight = size.height/2;
63+
} else if (week % 4 == 0) {
64+
// Month
65+
lineHeight = size.height/8;
66+
}
67+
68+
double currX = (week / numWeeks.toDouble()) * size.width;
69+
if (lineHeight > 0) {
70+
double margin = (size.height - lineHeight) / 2;
71+
canvas.drawLine(new Offset(currX, margin), new Offset(currX, size.height - margin), mainLinePaint);
72+
}
73+
74+
if (isYear) {
75+
TextSpan span = new TextSpan(style: new TextStyle(color: Color.fromARGB(255, 255, 255, 255), fontSize: 12), text: "${yearNumber}");
76+
TextPainter tp = new TextPainter(text: span, textAlign: TextAlign.left, textDirection: TextDirection.ltr);
77+
tp.layout();
78+
tp.paint(canvas, new Offset(currX, size.height - yearHeight));
79+
yearNumber++;
80+
}
81+
}
82+
}
83+
}
84+
85+
@override
86+
bool shouldRepaint(CustomPainter oldDelegate) {
87+
return true;
88+
}
89+
90+
2191
}

readme.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,8 @@
3333
3434
Open <http://localhost:8080> in Chrome and you should see `Hello World` in
3535
red text in the upper-left corner.
36+
37+
38+
## Data Notes
39+
40+
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.

0 commit comments

Comments
 (0)