mirror of
https://github.com/flutter/samples.git
synced 2025-11-08 22:09:06 +00:00
web/github_dataviz: Migrate to null safety (#919)
This commit is contained in:
6
web/github_dataviz/analysis_options.yaml
Normal file
6
web/github_dataviz/analysis_options.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
rules:
|
||||
avoid_print: false
|
||||
prefer_single_quotes: true
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:github_dataviz/mathutils.dart';
|
||||
|
||||
class ControlPointAndValue {
|
||||
int point;
|
||||
double value;
|
||||
late int point;
|
||||
double? value;
|
||||
|
||||
ControlPointAndValue() {
|
||||
value = 0;
|
||||
@@ -37,9 +37,9 @@ class CatmullInterpolator implements Interpolator {
|
||||
}
|
||||
|
||||
ControlPointAndValue progressiveGet(ControlPointAndValue cpv) {
|
||||
double v = cpv.value;
|
||||
double? v = cpv.value;
|
||||
for (int i = cpv.point; i < controlPoints.length - 1; i++) {
|
||||
if (controlPoints[i].x >= v) {
|
||||
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;
|
||||
|
||||
@@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
|
||||
import 'dart:ui';
|
||||
|
||||
class Constants {
|
||||
static final Color backgroundColor = const Color(0xFF000020);
|
||||
static final Color timelineLineColor = Color(0x60FFFFFF);
|
||||
static final Color milestoneColor = Color(0x40FFFFFF);
|
||||
static final Color milestoneTimelineColor = Colors.white;
|
||||
static const Color backgroundColor = Color(0xFF000020);
|
||||
static const Color timelineLineColor = Color(0x60FFFFFF);
|
||||
static const Color milestoneColor = Color(0x40FFFFFF);
|
||||
static const Color milestoneTimelineColor = Colors.white;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ class ContributionData {
|
||||
|
||||
static ContributionData fromJson(Map<String, dynamic> jsonMap) {
|
||||
ContributionData data = ContributionData(
|
||||
jsonMap["w"], jsonMap["a"], jsonMap["d"], jsonMap["c"]);
|
||||
jsonMap['w'], jsonMap['a'], jsonMap['d'], jsonMap['c']);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ class User {
|
||||
User(this.id, this.username, this.avatarUrl);
|
||||
|
||||
static User fromJson(Map<String, dynamic> jsonMap) {
|
||||
User user = User(jsonMap["id"], jsonMap["login"], jsonMap["avatar_url"]);
|
||||
User user = User(jsonMap['id'], jsonMap['login'], jsonMap['avatar_url']);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ class UserContribution {
|
||||
UserContribution(this.user, this.contributions);
|
||||
|
||||
static UserContribution fromJson(Map<String, dynamic> jsonMap) {
|
||||
List<ContributionData> contributionList = (jsonMap["weeks"] as List)
|
||||
List<ContributionData> contributionList = (jsonMap['weeks'] as List)
|
||||
.map((e) => ContributionData.fromJson(e))
|
||||
.toList();
|
||||
var userContribution =
|
||||
UserContribution(User.fromJson(jsonMap["author"]), contributionList);
|
||||
UserContribution(User.fromJson(jsonMap['author']), contributionList);
|
||||
return userContribution;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class WeekLabel {
|
||||
int weekNum;
|
||||
int? weekNum;
|
||||
String label;
|
||||
|
||||
WeekLabel(this.weekNum, this.label);
|
||||
|
||||
WeekLabel.forDate(DateTime date, String label) {
|
||||
this.label = label;
|
||||
WeekLabel.forDate(DateTime date, this.label) {
|
||||
int year = getYear(date);
|
||||
int weekOfYearNum = getWeekNumber(date);
|
||||
this.weekNum = 9 + ((year - 2015) * 52) + weekOfYearNum;
|
||||
weekNum = 9 + ((year - 2015) * 52) + weekOfYearNum;
|
||||
}
|
||||
|
||||
int getYear(DateTime date) {
|
||||
return int.parse(DateFormat("y").format(date));
|
||||
return int.parse(DateFormat('y').format(date));
|
||||
}
|
||||
|
||||
int getWeekNumber(DateTime date) {
|
||||
int dayOfYear = int.parse(DateFormat("D").format(date));
|
||||
int dayOfYear = int.parse(DateFormat('D').format(date));
|
||||
return ((dayOfYear - date.weekday + 10) / 7).floor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ class LayeredChart extends StatefulWidget {
|
||||
final List<WeekLabel> milestones;
|
||||
final double animationValue;
|
||||
|
||||
LayeredChart(this.dataToPlot, this.milestones, this.animationValue);
|
||||
const LayeredChart(this.dataToPlot, this.milestones, this.animationValue,
|
||||
{Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
@@ -23,14 +25,14 @@ class LayeredChart extends StatefulWidget {
|
||||
}
|
||||
|
||||
class LayeredChartState extends State<LayeredChart> {
|
||||
List<Path> paths;
|
||||
List<Path> capPaths;
|
||||
List<double> maxValues;
|
||||
double theta;
|
||||
double graphHeight;
|
||||
List<TextPainter> labelPainter;
|
||||
List<TextPainter> milestonePainter;
|
||||
Size lastSize;
|
||||
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,
|
||||
@@ -89,7 +91,7 @@ class LayeredChartState extends State<LayeredChart> {
|
||||
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));
|
||||
max(0, cpv.value!), 0, maxValues[i].toDouble(), 0, graphHeight));
|
||||
}
|
||||
paths.add(Path());
|
||||
capPaths.add(Path());
|
||||
@@ -136,7 +138,7 @@ class LayeredChartState extends State<LayeredChart> {
|
||||
labelPainter = <TextPainter>[];
|
||||
for (int i = 0; i < dataToPlot.length; i++) {
|
||||
TextSpan span = TextSpan(
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
color: Color.fromARGB(255, 255, 255, 255), fontSize: 12),
|
||||
text: dataToPlot[i].label.toUpperCase());
|
||||
TextPainter tp = TextPainter(
|
||||
@@ -149,7 +151,7 @@ class LayeredChartState extends State<LayeredChart> {
|
||||
milestonePainter = <TextPainter>[];
|
||||
for (int i = 0; i < milestones.length; i++) {
|
||||
TextSpan span = TextSpan(
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
color: Color.fromARGB(255, 255, 255, 255), fontSize: 10),
|
||||
text: milestones[i].label.toUpperCase());
|
||||
TextPainter tp = TextPainter(
|
||||
@@ -174,15 +176,15 @@ class LayeredChartState extends State<LayeredChart> {
|
||||
}
|
||||
|
||||
class ChartPainter extends CustomPainter {
|
||||
static List<Color> colors = [
|
||||
static List<Color?> colors = [
|
||||
Colors.red[900],
|
||||
Color(0xffc4721a),
|
||||
const Color(0xffc4721a),
|
||||
Colors.lime[900],
|
||||
Colors.green[900],
|
||||
Colors.blue[900],
|
||||
Colors.purple[900],
|
||||
];
|
||||
static List<Color> capColors = [
|
||||
static List<Color?> capColors = [
|
||||
Colors.red[500],
|
||||
Colors.amber[500],
|
||||
Colors.lime[500],
|
||||
@@ -196,17 +198,17 @@ class ChartPainter extends CustomPainter {
|
||||
|
||||
double margin;
|
||||
double graphGap;
|
||||
double capTheta;
|
||||
late double capTheta;
|
||||
double capSize;
|
||||
int numPoints;
|
||||
double amount = 1.0;
|
||||
|
||||
Paint pathPaint;
|
||||
Paint capPaint;
|
||||
Paint textPaint;
|
||||
Paint milestonePaint;
|
||||
Paint linePaint;
|
||||
Paint fillPaint;
|
||||
late Paint pathPaint;
|
||||
late Paint capPaint;
|
||||
late Paint textPaint;
|
||||
late Paint milestonePaint;
|
||||
late Paint linePaint;
|
||||
late Paint fillPaint;
|
||||
|
||||
LayeredChartState state;
|
||||
|
||||
@@ -220,13 +222,13 @@ class ChartPainter extends CustomPainter {
|
||||
this.capSize,
|
||||
this.numPoints,
|
||||
this.amount) {
|
||||
this.capTheta = pi * capDegrees / 180;
|
||||
capTheta = pi * capDegrees / 180;
|
||||
pathPaint = Paint();
|
||||
pathPaint.style = PaintingStyle.fill;
|
||||
capPaint = Paint();
|
||||
capPaint.style = PaintingStyle.fill;
|
||||
textPaint = Paint();
|
||||
textPaint.color = Color(0xFFFFFFFF);
|
||||
textPaint.color = const Color(0xFFFFFFFF);
|
||||
milestonePaint = Paint();
|
||||
milestonePaint.color = Constants.milestoneColor;
|
||||
milestonePaint.style = PaintingStyle.stroke;
|
||||
@@ -236,19 +238,19 @@ class ChartPainter extends CustomPainter {
|
||||
linePaint.strokeWidth = 0.5;
|
||||
fillPaint = Paint();
|
||||
fillPaint.style = PaintingStyle.fill;
|
||||
fillPaint.color = Color(0xFF000000);
|
||||
fillPaint.color = const Color(0xFF000000);
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
if (dataToPlot.length == 0) {
|
||||
if (dataToPlot.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.lastSize == null ||
|
||||
size.width != state.lastSize.width ||
|
||||
size.height != state.lastSize.height) {
|
||||
print("Building paths, lastsize = ${state.lastSize}");
|
||||
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);
|
||||
}
|
||||
@@ -266,7 +268,7 @@ class ChartPainter extends CustomPainter {
|
||||
{
|
||||
for (int i = 0; i < milestones.length; i++) {
|
||||
WeekLabel milestone = milestones[i];
|
||||
double p = (milestone.weekNum.toDouble() / numWeeks) + (1 - amount);
|
||||
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);
|
||||
@@ -282,7 +284,7 @@ class ChartPainter extends CustomPainter {
|
||||
canvas.translate(textX, textY);
|
||||
canvas.skew(tan(capTheta * 1.0), -tan(state.theta));
|
||||
canvas.translate(-tp.width / 2, 0);
|
||||
tp.paint(canvas, Offset(0, 0));
|
||||
tp.paint(canvas, const Offset(0, 0));
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
@@ -302,11 +304,11 @@ class ChartPainter extends CustomPainter {
|
||||
canvas.skew(0, -tan(state.theta));
|
||||
canvas.drawRect(
|
||||
Rect.fromLTWH(-1, -1, tp.width + 2, tp.height + 2), fillPaint);
|
||||
tp.paint(canvas, Offset(0, 0));
|
||||
tp.paint(canvas, const Offset(0, 0));
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
linePaint.color = capColors[i];
|
||||
linePaint.color = capColors[i]!;
|
||||
canvas.drawLine(Offset(startX, startY), Offset(endX, endY), linePaint);
|
||||
|
||||
Path clipPath = Path();
|
||||
@@ -317,8 +319,8 @@ class ChartPainter extends CustomPainter {
|
||||
clipPath.close();
|
||||
canvas.clipPath(clipPath);
|
||||
|
||||
pathPaint.color = colors[i];
|
||||
capPaint.color = capColors[i];
|
||||
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);
|
||||
|
||||
@@ -19,21 +19,23 @@ import 'package:github_dataviz/mathutils.dart';
|
||||
import 'package:github_dataviz/timeline.dart';
|
||||
|
||||
class MainLayout extends StatefulWidget {
|
||||
const MainLayout({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_MainLayoutState 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;
|
||||
List<WeekLabel> weekLabels;
|
||||
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 final double earlyInterpolatorFraction = 0.8;
|
||||
static const double earlyInterpolatorFraction = 0.8;
|
||||
static final EarlyInterpolator interpolator =
|
||||
EarlyInterpolator(earlyInterpolatorFraction);
|
||||
double animationValue = 1.0;
|
||||
@@ -47,14 +49,14 @@ class _MainLayoutState extends State<MainLayout> with TickerProviderStateMixin {
|
||||
createAnimation(0);
|
||||
|
||||
weekLabels = <WeekLabel>[];
|
||||
weekLabels.add(WeekLabel.forDate(DateTime(2019, 2, 26), "v1.2"));
|
||||
weekLabels.add(WeekLabel.forDate(DateTime(2018, 12, 4), "v1.0"));
|
||||
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(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"));
|
||||
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();
|
||||
}
|
||||
@@ -66,10 +68,10 @@ class _MainLayoutState extends State<MainLayout> with TickerProviderStateMixin {
|
||||
duration: const Duration(milliseconds: 14400),
|
||||
vsync: this,
|
||||
)..repeat();
|
||||
_animation.addListener(() {
|
||||
_animation!.addListener(() {
|
||||
setState(() {
|
||||
if (!timelineOverride) {
|
||||
animationValue = _animation.value;
|
||||
animationValue = _animation!.value;
|
||||
interpolatedAnimationValue = interpolator.get(animationValue);
|
||||
}
|
||||
});
|
||||
@@ -82,7 +84,7 @@ class _MainLayoutState extends State<MainLayout> with TickerProviderStateMixin {
|
||||
List<DataSeries> dataToPlot = [];
|
||||
if (contributions != null) {
|
||||
List<int> series = [];
|
||||
for (UserContribution userContrib in contributions) {
|
||||
for (UserContribution userContrib in contributions!) {
|
||||
for (int i = 0; i < userContrib.contributions.length; i++) {
|
||||
ContributionData data = userContrib.contributions[i];
|
||||
if (series.length > i) {
|
||||
@@ -92,32 +94,32 @@ class _MainLayoutState extends State<MainLayout> with TickerProviderStateMixin {
|
||||
}
|
||||
}
|
||||
}
|
||||
dataToPlot.add(DataSeries("Added Lines", series));
|
||||
dataToPlot.add(DataSeries('Added Lines', series));
|
||||
}
|
||||
|
||||
if (starsByWeek != null) {
|
||||
dataToPlot
|
||||
.add(DataSeries("Stars", starsByWeek.map((e) => e.stat).toList()));
|
||||
.add(DataSeries('Stars', starsByWeek!.map((e) => e.stat).toList()));
|
||||
}
|
||||
|
||||
if (forksByWeek != null) {
|
||||
dataToPlot
|
||||
.add(DataSeries("Forks", forksByWeek.map((e) => e.stat).toList()));
|
||||
.add(DataSeries('Forks', forksByWeek!.map((e) => e.stat).toList()));
|
||||
}
|
||||
|
||||
if (pushesByWeek != null) {
|
||||
dataToPlot
|
||||
.add(DataSeries("Pushes", pushesByWeek.map((e) => e.stat).toList()));
|
||||
.add(DataSeries('Pushes', pushesByWeek!.map((e) => e.stat).toList()));
|
||||
}
|
||||
|
||||
if (issueCommentsByWeek != null) {
|
||||
dataToPlot.add(DataSeries(
|
||||
"Issue Comments", issueCommentsByWeek.map((e) => e.stat).toList()));
|
||||
'Issue Comments', issueCommentsByWeek!.map((e) => e.stat).toList()));
|
||||
}
|
||||
|
||||
if (pullRequestActivityByWeek != null) {
|
||||
dataToPlot.add(DataSeries("Pull Request Activity",
|
||||
pullRequestActivityByWeek.map((e) => e.stat).toList()));
|
||||
dataToPlot.add(DataSeries('Pull Request Activity',
|
||||
pullRequestActivityByWeek!.map((e) => e.stat).toList()));
|
||||
}
|
||||
|
||||
LayeredChart layeredChart =
|
||||
@@ -126,15 +128,13 @@ class _MainLayoutState extends State<MainLayout> with TickerProviderStateMixin {
|
||||
const double timelinePadding = 60.0;
|
||||
|
||||
var timeline = Timeline(
|
||||
numWeeks: dataToPlot != null && dataToPlot.length > 0
|
||||
? dataToPlot.last.series.length
|
||||
: 0,
|
||||
numWeeks: dataToPlot.isNotEmpty ? dataToPlot.last.series.length : 0,
|
||||
animationValue: interpolatedAnimationValue,
|
||||
weekLabels: weekLabels,
|
||||
mouseDownCallback: (double xFraction) {
|
||||
setState(() {
|
||||
timelineOverride = true;
|
||||
_animation.stop();
|
||||
_animation?.stop();
|
||||
interpolatedAnimationValue = xFraction;
|
||||
});
|
||||
},
|
||||
@@ -176,53 +176,55 @@ class _MainLayoutState extends State<MainLayout> with TickerProviderStateMixin {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animation.dispose();
|
||||
_animation?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future loadGitHubData() async {
|
||||
String contributorsJsonStr =
|
||||
(await http.get("assets/github_data/contributors.json")).body;
|
||||
(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.");
|
||||
'Loaded ${contributionList.length} code contributions to /flutter/flutter repo.');
|
||||
|
||||
int numWeeksTotal = contributionList[0].contributions.length;
|
||||
|
||||
String starsByWeekStr =
|
||||
(await http.get("assets/github_data/stars.tsv")).body;
|
||||
(await http.get(Uri.parse('assets/github_data/stars.tsv'))).body;
|
||||
List<StatForWeek> starsByWeekLoaded =
|
||||
summarizeWeeksFromTSV(starsByWeekStr, numWeeksTotal);
|
||||
|
||||
String forksByWeekStr =
|
||||
(await http.get("assets/github_data/forks.tsv")).body;
|
||||
(await http.get(Uri.parse('assets/github_data/forks.tsv'))).body;
|
||||
List<StatForWeek> forksByWeekLoaded =
|
||||
summarizeWeeksFromTSV(forksByWeekStr, numWeeksTotal);
|
||||
|
||||
String commitsByWeekStr =
|
||||
(await http.get("assets/github_data/commits.tsv")).body;
|
||||
(await http.get(Uri.parse('assets/github_data/commits.tsv'))).body;
|
||||
List<StatForWeek> commitsByWeekLoaded =
|
||||
summarizeWeeksFromTSV(commitsByWeekStr, numWeeksTotal);
|
||||
|
||||
String commentsByWeekStr =
|
||||
(await http.get("assets/github_data/comments.tsv")).body;
|
||||
(await http.get(Uri.parse('assets/github_data/comments.tsv'))).body;
|
||||
List<StatForWeek> commentsByWeekLoaded =
|
||||
summarizeWeeksFromTSV(commentsByWeekStr, numWeeksTotal);
|
||||
|
||||
String pullRequestActivityByWeekStr =
|
||||
(await http.get("assets/github_data/pull_requests.tsv")).body;
|
||||
(await http.get(Uri.parse('assets/github_data/pull_requests.tsv')))
|
||||
.body;
|
||||
List<StatForWeek> pullRequestActivityByWeekLoaded =
|
||||
summarizeWeeksFromTSV(pullRequestActivityByWeekStr, numWeeksTotal);
|
||||
|
||||
setState(() {
|
||||
this.contributions = contributionList;
|
||||
this.starsByWeek = starsByWeekLoaded;
|
||||
this.forksByWeek = forksByWeekLoaded;
|
||||
this.pushesByWeek = commitsByWeekLoaded;
|
||||
this.issueCommentsByWeek = commentsByWeekLoaded;
|
||||
this.pullRequestActivityByWeek = pullRequestActivityByWeekLoaded;
|
||||
contributions = contributionList;
|
||||
starsByWeek = starsByWeekLoaded;
|
||||
forksByWeek = forksByWeekLoaded;
|
||||
pushesByWeek = commitsByWeekLoaded;
|
||||
issueCommentsByWeek = commentsByWeekLoaded;
|
||||
pullRequestActivityByWeek = pullRequestActivityByWeekLoaded;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -230,18 +232,18 @@ class _MainLayoutState extends State<MainLayout> with TickerProviderStateMixin {
|
||||
String statByWeekStr, int numWeeksTotal) {
|
||||
List<StatForWeek> loadedStats = [];
|
||||
HashMap<int, StatForWeek> statMap = HashMap();
|
||||
statByWeekStr.split("\n").forEach((s) {
|
||||
List<String> split = s.split("\t");
|
||||
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.");
|
||||
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];
|
||||
StatForWeek? starsForWeek = statMap[i];
|
||||
if (starsForWeek == null) {
|
||||
loadedStats.add(StatForWeek(i, 0));
|
||||
} else {
|
||||
@@ -253,5 +255,5 @@ class _MainLayoutState extends State<MainLayout> with TickerProviderStateMixin {
|
||||
}
|
||||
|
||||
void main() {
|
||||
runApp(Center(child: MainLayout()));
|
||||
runApp(const Center(child: MainLayout()));
|
||||
}
|
||||
|
||||
@@ -14,17 +14,19 @@ class Timeline extends StatefulWidget {
|
||||
final double animationValue;
|
||||
final List<WeekLabel> weekLabels;
|
||||
|
||||
final MouseDownCallback mouseDownCallback;
|
||||
final MouseMoveCallback mouseMoveCallback;
|
||||
final MouseUpCallback mouseUpCallback;
|
||||
final MouseDownCallback? mouseDownCallback;
|
||||
final MouseMoveCallback? mouseMoveCallback;
|
||||
final MouseUpCallback? mouseUpCallback;
|
||||
|
||||
Timeline(
|
||||
{@required this.numWeeks,
|
||||
@required this.animationValue,
|
||||
@required this.weekLabels,
|
||||
const Timeline(
|
||||
{required this.numWeeks,
|
||||
required this.animationValue,
|
||||
required this.weekLabels,
|
||||
this.mouseDownCallback,
|
||||
this.mouseMoveCallback,
|
||||
this.mouseUpCallback});
|
||||
this.mouseUpCallback,
|
||||
Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
@@ -39,17 +41,17 @@ class TimelineState extends State<Timeline> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
for (int year = 2015; year < 2020; year++) {
|
||||
String yearLabel = "$year";
|
||||
String yearLabel = '$year';
|
||||
labelPainters[yearLabel] =
|
||||
_makeTextPainter(Constants.timelineLineColor, yearLabel);
|
||||
}
|
||||
|
||||
widget.weekLabels.forEach((WeekLabel weekLabel) {
|
||||
for (var weekLabel in widget.weekLabels) {
|
||||
labelPainters[weekLabel.label] =
|
||||
_makeTextPainter(Constants.milestoneTimelineColor, weekLabel.label);
|
||||
labelPainters[weekLabel.label + "_red"] =
|
||||
labelPainters[weekLabel.label + '_red'] =
|
||||
_makeTextPainter(Colors.redAccent, weekLabel.label);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -57,19 +59,22 @@ class TimelineState extends State<Timeline> {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onHorizontalDragDown: (DragDownDetails details) {
|
||||
if (widget.mouseDownCallback != null) {
|
||||
widget.mouseDownCallback(
|
||||
final mouseDownCallback = widget.mouseDownCallback;
|
||||
if (mouseDownCallback != null) {
|
||||
mouseDownCallback(
|
||||
_getClampedXFractionLocalCoords(context, details.globalPosition));
|
||||
}
|
||||
},
|
||||
onHorizontalDragEnd: (DragEndDetails details) {
|
||||
if (widget.mouseUpCallback != null) {
|
||||
widget.mouseUpCallback();
|
||||
final mouseUpCallback = widget.mouseUpCallback;
|
||||
if (mouseUpCallback != null) {
|
||||
mouseUpCallback();
|
||||
}
|
||||
},
|
||||
onHorizontalDragUpdate: (DragUpdateDetails details) {
|
||||
if (widget.mouseMoveCallback != null) {
|
||||
widget.mouseMoveCallback(
|
||||
final mouseMoveCallback = widget.mouseMoveCallback;
|
||||
if (mouseMoveCallback != null) {
|
||||
mouseMoveCallback(
|
||||
_getClampedXFractionLocalCoords(context, details.globalPosition));
|
||||
}
|
||||
},
|
||||
@@ -95,17 +100,17 @@ class TimelineState extends State<Timeline> {
|
||||
|
||||
double _getClampedXFractionLocalCoords(
|
||||
BuildContext context, Offset globalOffset) {
|
||||
final RenderBox box = context.findRenderObject();
|
||||
final RenderBox box = context.findRenderObject() as RenderBox;
|
||||
final Offset localOffset = box.globalToLocal(globalOffset);
|
||||
return MathUtils.clamp(localOffset.dx / context.size.width, 0, 1);
|
||||
return MathUtils.clamp(localOffset.dx / context.size!.width, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
class TimelinePainter extends CustomPainter {
|
||||
TimelineState state;
|
||||
|
||||
Paint mainLinePaint;
|
||||
Paint milestoneLinePaint;
|
||||
late Paint mainLinePaint;
|
||||
late Paint milestoneLinePaint;
|
||||
|
||||
Color lineColor = Colors.white;
|
||||
|
||||
@@ -164,7 +169,7 @@ class TimelinePainter extends CustomPainter {
|
||||
var mappedValue =
|
||||
MathUtils.clampedMap(currTimeXDiff, 0, 0.025, 0, 1);
|
||||
var lerpedColor = Color.lerp(Constants.milestoneTimelineColor,
|
||||
Constants.timelineLineColor, mappedValue);
|
||||
Constants.timelineLineColor, mappedValue)!;
|
||||
mainLinePaint.color = lerpedColor;
|
||||
} else {
|
||||
mainLinePaint.color = Constants.timelineLineColor;
|
||||
@@ -174,8 +179,8 @@ class TimelinePainter extends CustomPainter {
|
||||
}
|
||||
|
||||
if (isYear) {
|
||||
var yearLabel = "$yearNumber";
|
||||
state.labelPainters[yearLabel]
|
||||
var yearLabel = '$yearNumber';
|
||||
state.labelPainters[yearLabel]!
|
||||
.paint(canvas, Offset(currX, size.height - labelHeight));
|
||||
yearNumber++;
|
||||
}
|
||||
@@ -185,17 +190,17 @@ class TimelinePainter extends CustomPainter {
|
||||
{
|
||||
for (int i = 0; i < weekLabels.length; i++) {
|
||||
WeekLabel weekLabel = weekLabels[i];
|
||||
double currX = (weekLabel.weekNum / numWeeks.toDouble()) * size.width;
|
||||
double currX = (weekLabel.weekNum! / numWeeks.toDouble()) * size.width;
|
||||
var timelineXDiff = (currTimeX - currX) / size.width;
|
||||
double maxTimelineDiff = 0.08;
|
||||
TextPainter textPainter = state.labelPainters[weekLabel.label];
|
||||
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);
|
||||
Colors.redAccent, Constants.milestoneTimelineColor, mappedValue)!;
|
||||
milestoneLinePaint.strokeWidth =
|
||||
MathUtils.clampedMap(timelineXDiff, 0, maxTimelineDiff, 6, 1);
|
||||
milestoneLinePaint.color = lerpedColor;
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.8.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.2.0"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -15,6 +22,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -27,27 +41,41 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.2"
|
||||
version: "0.13.4"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.4"
|
||||
version: "4.0.0"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.16.1"
|
||||
version: "0.17.0"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -62,13 +90,6 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.0"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pedantic
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@@ -110,4 +131,4 @@ packages:
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
sdks:
|
||||
dart: ">=2.12.0 <3.0.0"
|
||||
dart: ">=2.14.0 <3.0.0"
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
name: github_dataviz
|
||||
|
||||
environment:
|
||||
sdk: ">=2.2.0 <3.0.0"
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
intl: ^0.16.0
|
||||
http: ^0.12.0
|
||||
intl: ^0.17.0
|
||||
http: ^0.13.4
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^1.0.4
|
||||
|
||||
flutter:
|
||||
assets:
|
||||
- preview.png
|
||||
|
||||
Reference in New Issue
Block a user