Commit 91f218f2 authored by Administrator's avatar Administrator

power and power/heartrate charts on athlete level

parent 0501737c
......@@ -68,26 +68,12 @@ class Activity extends ChangeNotifier {
return "";
}
timeString() {
if (db.avgHeartRate != null) {
return DateFormat("H:mm").format(db.timeCreated);
} else
return "";
}
timeString() => DateFormat("H:mm").format(db.timeCreated);
dateString() {
if (db.avgHeartRate != null) {
return DateFormat("d MMM yy").format(db.timeCreated);
} else
return "";
}
dateString() => DateFormat("d MMM yy").format(db.timeCreated);
shortDateString() => DateFormat("d.M.").format(db.timeCreated);
paceString() {
if (db.avgHeartRate != null) {
return db.avgSpeed.toPace() + "/km";
} else
return "";
}
paceString() => db.avgSpeed.toPace() + "/km";
Future<List<Event>> get records async {
if (_records == null) {
......@@ -444,7 +430,7 @@ class Activity extends ChangeNotifier {
}
}
resetCurrentLap() async{
resetCurrentLap() async {
currentLap = Lap();
await currentLap.db.save();
eventsForCurrentLap = [];
......@@ -488,11 +474,12 @@ class Activity extends ChangeNotifier {
});
}
static Future<List<Activity>> all() async {
List<DbActivity> dbActivityList =
await DbActivity().select().orderByDesc('stravaId').toList();
return dbActivityList
static Future<List<Activity>> all({@required Athlete athlete}) async {
var dbActivityList =
await athlete.db.getDbActivities().orderByDesc('stravaId').toList();
var activities = dbActivityList
.map((dbActivity) => Activity.fromDb(dbActivity))
.toList();
return activities;
}
}
import 'package:flutter/material.dart';
import 'package:encrateia/model/model.dart';
import 'package:strava_flutter/Models/detailedAthlete.dart';
import 'package:encrateia/models/activity.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class Athlete extends ChangeNotifier {
......@@ -55,4 +56,7 @@ class Athlete extends ChangeNotifier {
List<DbAthlete> dbAthleteList = await DbAthlete().select().toList();
return dbAthleteList.map((dbAthlete) => Athlete.fromDb(dbAthlete)).toList();
}
get activities => Activity.all(athlete: this);
}
......@@ -3,7 +3,6 @@ import 'package:fit_parser/fit_parser.dart';
import 'package:encrateia/utils/date_time_utils.dart';
import 'package:flutter/cupertino.dart';
import 'activity.dart';
import 'dart:developer';
import 'package:encrateia/models/lap.dart';
import 'package:encrateia/models/plot_point.dart';
......@@ -43,7 +42,8 @@ class Event {
..data = dataMessage.get('data')
..timeStamp = dateTimeFromStrava(dataMessage.get('timestamp'));
} else {
debugger();
// Use this debugger to include new event messages, such as heart rate alerts, ...
// debugger();
}
}
......
......@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'edit_athlete_screen.dart';
import 'package:encrateia/models/athlete.dart';
import 'list_activities_screen.dart';
import 'show_athlete_screen.dart';
import 'package:encrateia/utils/icon_utils.dart';
class Dashboard extends StatefulWidget {
......@@ -128,7 +128,7 @@ class _DashboardState extends State<Dashboard> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ListActivitiesScreen(athlete: athlete),
builder: (context) => ShowAthleteScreen(athlete: athlete),
),
);
}
......
......@@ -2,19 +2,21 @@ import 'package:flutter/material.dart';
import 'package:encrateia/models/athlete.dart';
import 'package:encrateia/models/activity.dart';
import 'package:encrateia/widgets/activities_list_widget.dart';
import 'package:encrateia/widgets/athlete_power_widget.dart';
import 'package:encrateia/widgets/athlete_power_per_heart_rate_widget.dart';
import 'package:encrateia/utils/icon_utils.dart';
import 'package:flushbar/flushbar.dart';
class ListActivitiesScreen extends StatefulWidget {
class ShowAthleteScreen extends StatefulWidget {
final Athlete athlete;
const ListActivitiesScreen({Key key, this.athlete}) : super(key: key);
const ShowAthleteScreen({Key key, this.athlete}) : super(key: key);
@override
_ListActivitiesScreenState createState() => _ListActivitiesScreenState();
_ShowAthleteScreenState createState() => _ShowAthleteScreenState();
}
class _ListActivitiesScreenState extends State<ListActivitiesScreen> {
class _ShowAthleteScreenState extends State<ShowAthleteScreen> {
Flushbar flushbar;
Visibility floatingActionButton;
bool floatingActionButtonVisible;
......@@ -22,23 +24,47 @@ class _ListActivitiesScreenState extends State<ListActivitiesScreen> {
@override
void initState() {
floatingActionButtonVisible =
(widget.athlete.email != null && widget.athlete.password != null);
(widget.athlete.email != null && widget.athlete.password != null);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: Visibility(
visible: floatingActionButtonVisible,
child: FloatingActionButton.extended(
onPressed: () => updateJob(),
label: Text("from Strava"),
icon: MyIcon.stravaDownload,
return DefaultTabController(
length: 3,
child: Scaffold(
floatingActionButton: Visibility(
visible: floatingActionButtonVisible,
child: FloatingActionButton.extended(
onPressed: () => updateJob(),
label: Text("from Strava"),
icon: MyIcon.stravaDownload,
),
),
appBar: AppBar(
title: Text(
widget.athlete.db.firstName + " " + widget.athlete.db.lastName),
bottom: TabBar(isScrollable: true, tabs: [
Tab(
icon: MyIcon.activities,
text: "Activities",
),
Tab(
icon: MyIcon.power,
text: "Power",
),
Tab(
icon: MyIcon.power,
text: "Power/HR",
)
]),
),
body: TabBarView(children: [
ActivitiesListWidget(athlete: widget.athlete),
AthletePowerWidget(athlete: widget.athlete),
AthletePowerPerHeartRateWidget(athlete: widget.athlete),
]),
),
appBar: AppBar(title: Text('Activities')),
body: ActivitiesListWidget(athlete: widget.athlete),
);
}
......@@ -49,7 +75,7 @@ class _ListActivitiesScreenState extends State<ListActivitiesScreen> {
await queryStrava();
activities = await Activity.all();
activities = await Activity.all(athlete: widget.athlete);
var newActivities =
activities.where((activity) => activity.db.state == "new");
for (Activity activity in newActivities) {
......
......@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
class MyIcon {
// A
static final activities = Icon(Icons.view_list);
static final amount = Icon(Icons.playlist_add);
static final ascent = Icon(Icons.trending_up);
static final athlete = Icon(Icons.face);
......
......@@ -149,12 +149,12 @@ class _ActivitiesListWidgetState extends State<ActivitiesListWidget> {
)
..show(context);
}
activities = await Activity.all();
activities = await Activity.all(athlete: widget.athlete);
setState(() {});
}
Future getActivities() async {
activities = await Activity.all();
activities = await Activity.all(athlete: widget.athlete);
setState(() {});
}
......
import 'package:charts_flutter/flutter.dart';
import 'package:flutter/material.dart';
import 'package:encrateia/models/activity.dart';
class AthletePowerChart extends StatelessWidget {
final List<Activity> activities;
AthletePowerChart({@required this.activities});
@override
Widget build(BuildContext context) {
var nonZero = activities.where((value) => value.db.avgPower > 0).toList();
var data = [
new Series<Activity, DateTime>(
id: 'Average Power',
colorFn: (_, __) => MaterialPalette.blue.shadeDefault,
domainFn: (Activity activity, _) => activity.db.timeCreated,
measureFn: (Activity activity, _) => activity.db.avgPower,
data: nonZero,
)
];
return new Container(
height: 300,
child: TimeSeriesChart(
data,
animate: false,
primaryMeasureAxis: NumericAxisSpec(
tickProviderSpec: BasicNumericTickProviderSpec(
zeroBound: false,
dataIsInWholeNumbers: false,
desiredTickCount: 6,
),
),
behaviors: [
ChartTitle(
'Power (W)',
titleStyleSpec: TextStyleSpec(fontSize: 13),
behaviorPosition: BehaviorPosition.start,
titleOutsideJustification: OutsideJustification.end,
),
ChartTitle(
'Date',
titleStyleSpec: TextStyleSpec(fontSize: 13),
behaviorPosition: BehaviorPosition.bottom,
titleOutsideJustification: OutsideJustification.end,
),
],
),
);
}
}
import 'package:charts_flutter/flutter.dart';
import 'package:flutter/material.dart';
import 'package:encrateia/models/activity.dart';
class AthletePowerPerHeartRateChart extends StatelessWidget {
final List<Activity> activities;
AthletePowerPerHeartRateChart({@required this.activities});
@override
Widget build(BuildContext context) {
var nonZero = activities.where((value) => value.db.avgPower > 0).toList();
var data = [
new Series<Activity, DateTime>(
id: 'Average Power',
colorFn: (_, __) => MaterialPalette.blue.shadeDefault,
domainFn: (Activity activity, _) => activity.db.timeCreated,
measureFn: (Activity activity, _) =>
(activity.db.avgPower / activity.db.avgHeartRate),
data: nonZero,
)
];
return new Container(
height: 300,
child: TimeSeriesChart(
data,
animate: false,
primaryMeasureAxis: NumericAxisSpec(
tickProviderSpec: BasicNumericTickProviderSpec(
zeroBound: false,
dataIsInWholeNumbers: false,
desiredTickCount: 6,
),
),
behaviors: [
ChartTitle(
'Power per Heart Rate (W / bpm)',
titleStyleSpec: TextStyleSpec(fontSize: 13),
behaviorPosition: BehaviorPosition.start,
titleOutsideJustification: OutsideJustification.end,
),
ChartTitle(
'Date',
titleStyleSpec: TextStyleSpec(fontSize: 13),
behaviorPosition: BehaviorPosition.bottom,
titleOutsideJustification: OutsideJustification.end,
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:encrateia/models/athlete.dart';
import 'package:encrateia/models/activity.dart';
import 'package:encrateia/utils/list_utils.dart';
import 'athlete_power_per_heart_rate_chart.dart';
class AthletePowerPerHeartRateWidget extends StatefulWidget {
final Athlete athlete;
AthletePowerPerHeartRateWidget({this.athlete});
@override
_AthletePowerPerHeartRateWidgetState createState() => _AthletePowerPerHeartRateWidgetState();
}
class _AthletePowerPerHeartRateWidgetState extends State<AthletePowerPerHeartRateWidget> {
List<Activity> activities = [];
@override
void initState() {
getData();
super.initState();
}
@override
Widget build(context) {
if (activities.length > 0) {
var powerValues = activities.map((value) => value.db.avgPower).nonZeroDoubles();
if (powerValues.length > 0) {
return ListTileTheme(
iconColor: Colors.deepOrange,
child: ListView(
padding: EdgeInsets.only(left: 25),
children: <Widget>[
AthletePowerPerHeartRateChart(activities: activities),
],
),
);
} else {
return Center(
child: Text("No power per heart rate data available."),
);
}
} else {
return Center(
child: Text("Loading"),
);
}
}
getData() async {
Athlete athlete = widget.athlete;
activities = await athlete.activities;
setState(() {});
}
}
import 'package:flutter/material.dart';
import 'package:encrateia/models/athlete.dart';
import 'package:encrateia/models/activity.dart';
import 'package:encrateia/utils/list_utils.dart';
import 'athlete_power_chart.dart';
class AthletePowerWidget extends StatefulWidget {
final Athlete athlete;
AthletePowerWidget({this.athlete});
@override
_AthletePowerWidgetState createState() => _AthletePowerWidgetState();
}
class _AthletePowerWidgetState extends State<AthletePowerWidget> {
List<Activity> activities = [];
@override
void initState() {
getData();
super.initState();
}
@override
Widget build(context) {
if (activities.length > 0) {
var powerValues = activities.map((value) => value.db.avgPower).nonZeroDoubles();
if (powerValues.length > 0) {
return ListTileTheme(
iconColor: Colors.deepOrange,
child: ListView(
padding: EdgeInsets.only(left: 25),
children: <Widget>[
AthletePowerChart(activities: activities),
],
),
);
} else {
return Center(
child: Text("No power data available."),
);
}
} else {
return Center(
child: Text("Loading"),
);
}
}
getData() async {
Athlete athlete = widget.athlete;
activities = await athlete.activities;
setState(() {});
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment