list view styling, lap detail screen and metadata widget
parent
c551ba5498
commit
eabdcecf22
Binary file not shown.
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'edit_athlete.dart';
|
||||
import 'edit_athlete_screen.dart';
|
||||
import 'package:encrateia/models/athlete.dart';
|
||||
import 'list_activities_screen.dart';
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'package:encrateia/models/activity.dart';
|
|||
import 'package:encrateia/models/athlete.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'show_activity.dart';
|
||||
import 'show_activity_screen.dart';
|
||||
|
||||
class ListActivitiesScreen extends StatefulWidget {
|
||||
final Athlete athlete;
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import 'package:encrateia/widgets/lap_metadata_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:encrateia/models/lap.dart';
|
||||
|
||||
class ShowLapScreen extends StatelessWidget {
|
||||
final Lap lap;
|
||||
|
||||
const ShowLapScreen({
|
||||
Key key,
|
||||
this.lap,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTabController(
|
||||
length: 1,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
bottom: TabBar(
|
||||
tabs: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Tab(icon: Icon(Icons.directions_run)),
|
||||
Text(" Overview"),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
title: Text(
|
||||
'Lap ${lap.index}',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
body: TabBarView(children: [
|
||||
LapMetadataWidget(lap: lap),
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -10,171 +10,177 @@ class ActivityMetadataWidget extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(context) {
|
||||
return new ListView(
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
leading: Icon(Icons.title),
|
||||
title: Text(activity.db.name),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.fingerprint),
|
||||
title: Text(activity.db.stravaId.toString()),
|
||||
subtitle: Text(activity.db.serialNumber.toString()),
|
||||
trailing: Text("Strava id \nGarmin id"),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.timer),
|
||||
title: Text(Duration(seconds: activity.db.movingTime).print()),
|
||||
subtitle: Text(
|
||||
Duration(seconds: activity.db.totalElapsedTime).print() +
|
||||
"\n" +
|
||||
Duration(seconds: activity.db.totalTimerTime).print()),
|
||||
trailing: Text(
|
||||
'moving time (hh:mm:ss)\ntotal elapsed time\ntotal timer time'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.directions_bike),
|
||||
title: Text(activity.db.type),
|
||||
subtitle: Text(activity.db.sport + '/' + activity.db.subSport),
|
||||
trailing: Text('activity type \nsport / sub sport'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.access_time),
|
||||
title: Text(DateFormat("dd MMM yyyy hh:mm:ss")
|
||||
.format(activity.db.timeCreated)),
|
||||
trailing: Text('time created'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.redo),
|
||||
title: Text('${(activity.db.distance / 1000).toStringAsFixed(2)} km'),
|
||||
trailing: Text('distance'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.event),
|
||||
title: Text(activity.db.event + " / " + activity.db.eventType),
|
||||
trailing: Text('last event / event type'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.battery_charging_full),
|
||||
title: Text('${activity.db.totalCalories} kcal'),
|
||||
trailing: Text('total calories'),
|
||||
),
|
||||
if (activity.db.totalStrides != null)
|
||||
return new ListTileTheme(
|
||||
dense: true,
|
||||
iconColor: Colors.deepOrange,
|
||||
child: ListView(
|
||||
padding: EdgeInsets.only(left: 8, right: 8),
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
leading: Icon(Icons.directions_walk),
|
||||
title: Text(activity.db.totalStrides.toString()),
|
||||
trailing: Text('total strides'),
|
||||
leading: Icon(Icons.title),
|
||||
title: Text(activity.db.name),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.shutter_speed),
|
||||
title:
|
||||
Text((activity.db.avgSpeed * 3.6).toStringAsFixed(2) + " km/h"),
|
||||
trailing: Text('avg speed'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.airplanemode_active),
|
||||
title:
|
||||
Text((activity.db.maxSpeed * 3.6).toStringAsFixed(2) + " km/h"),
|
||||
trailing: Text('max speed'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||||
Icon(Icons.trending_up),
|
||||
Icon(Icons.landscape),
|
||||
]),
|
||||
title: Text("${activity.db.totalAscent} m"),
|
||||
trailing: Text('total ascent'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||||
Icon(Icons.trending_down),
|
||||
Icon(Icons.landscape),
|
||||
]),
|
||||
title: Text("${activity.db.totalDescent} m"),
|
||||
trailing: Text('total descent'),
|
||||
),
|
||||
if (activity.db.maxRunningCadence != null)
|
||||
ListTile(
|
||||
leading: Icon(Icons.directions_run),
|
||||
title: Text(activity.db.maxRunningCadence.toString()),
|
||||
trailing: Text('max running cadence'),
|
||||
leading: Icon(Icons.fingerprint),
|
||||
title: Text(activity.db.stravaId.toString()),
|
||||
subtitle: Text(activity.db.serialNumber.toString()),
|
||||
trailing: Text("Strava id \nGarmin id"),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.settings_input_antenna),
|
||||
title: Text(activity.db.trigger),
|
||||
trailing: Text('trigger'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.ac_unit),
|
||||
title: Text(
|
||||
'${activity.db.avgTemperature}° ... ${activity.db.maxTemperature}°'),
|
||||
trailing: Text('avg ... max temperature'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.linear_scale),
|
||||
title: Text(
|
||||
'${activity.db.avgFractionalCadence.toStringAsFixed(2)} ... ${activity.db.maxFractionalCadence.toStringAsFixed(2)}'),
|
||||
trailing: Text('avg ... max\nfractional cadence'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.spa),
|
||||
title: Text(
|
||||
'${activity.db.avgHeartRate} ... ${activity.db.maxHeartRate}'),
|
||||
trailing: Text('avg ... max heart rate'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.repeat),
|
||||
title: Text('${activity.db.numLaps} / ${activity.db.numSessions}'),
|
||||
trailing: Text('number of laps / sessions'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.pets),
|
||||
title: Text(
|
||||
'${activity.db.avgStanceTime}ms / ${activity.db.avgStanceTimePercent}%'),
|
||||
trailing: Text('avg stance time / %'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.fitness_center),
|
||||
title: Text(activity.db.totalTrainingEffect.toString()),
|
||||
trailing: Text('total training effect'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.unfold_more),
|
||||
title: Text(activity.db.avgVerticalOscillation.toString()),
|
||||
trailing: Text('avg vertical oscillation'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.pets),
|
||||
title: Text(activity.db.avgRunningCadence.toString()),
|
||||
trailing: Text('avg running cadence'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.repeat),
|
||||
title: Text(activity.db.totalFractionalCycles.toString()),
|
||||
trailing: Text('total fractional cycles'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.map),
|
||||
title: Text(activity.db.startPositionLong.semicirclesAsDegrees() +
|
||||
" /\n" +
|
||||
activity.db.startPositionLat.semicirclesAsDegrees()),
|
||||
trailing: Text('start position long /\nlat'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.map),
|
||||
title: Text(activity.db.necLong.semicirclesAsDegrees() +
|
||||
" /\n" +
|
||||
activity.db.necLat.semicirclesAsDegrees()),
|
||||
trailing: Text('north east corner long /\nlat'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.map),
|
||||
title: Text(activity.db.swcLong.semicirclesAsDegrees() +
|
||||
" /\n" +
|
||||
activity.db.swcLat.semicirclesAsDegrees()),
|
||||
trailing: Text('south west corner long /\nlat'),
|
||||
),
|
||||
],
|
||||
ListTile(
|
||||
leading: Icon(Icons.timer),
|
||||
title: Text(Duration(seconds: activity.db.movingTime).print()),
|
||||
subtitle: Text(
|
||||
Duration(seconds: activity.db.totalElapsedTime).print() +
|
||||
"\n" +
|
||||
Duration(seconds: activity.db.totalTimerTime).print()),
|
||||
trailing: Text(
|
||||
'moving time (hh:mm:ss)\ntotal elapsed time\ntotal timer time'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.directions_bike),
|
||||
title: Text(activity.db.type),
|
||||
subtitle: Text(activity.db.sport + '/' + activity.db.subSport),
|
||||
trailing: Text('activity type \nsport / sub sport'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.access_time),
|
||||
title: Text(DateFormat("dd MMM yyyy hh:mm:ss")
|
||||
.format(activity.db.timeCreated)),
|
||||
trailing: Text('time created'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.redo),
|
||||
title:
|
||||
Text('${(activity.db.distance / 1000).toStringAsFixed(2)} km'),
|
||||
trailing: Text('distance'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.event),
|
||||
title: Text(activity.db.event + " / " + activity.db.eventType),
|
||||
trailing: Text('last event / event type'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.battery_charging_full),
|
||||
title: Text('${activity.db.totalCalories} kcal'),
|
||||
trailing: Text('total calories'),
|
||||
),
|
||||
if (activity.db.totalStrides != null)
|
||||
ListTile(
|
||||
leading: Icon(Icons.directions_walk),
|
||||
title: Text(activity.db.totalStrides.toString()),
|
||||
trailing: Text('total strides'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.shutter_speed),
|
||||
title:
|
||||
Text((activity.db.avgSpeed * 3.6).toStringAsFixed(2) + " km/h"),
|
||||
trailing: Text('avg speed'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.airplanemode_active),
|
||||
title:
|
||||
Text((activity.db.maxSpeed * 3.6).toStringAsFixed(2) + " km/h"),
|
||||
trailing: Text('max speed'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||||
Icon(Icons.trending_up),
|
||||
Icon(Icons.landscape),
|
||||
]),
|
||||
title: Text("${activity.db.totalAscent} m"),
|
||||
trailing: Text('total ascent'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||||
Icon(Icons.trending_down),
|
||||
Icon(Icons.landscape),
|
||||
]),
|
||||
title: Text("${activity.db.totalDescent} m"),
|
||||
trailing: Text('total descent'),
|
||||
),
|
||||
if (activity.db.maxRunningCadence != null)
|
||||
ListTile(
|
||||
leading: Icon(Icons.directions_run),
|
||||
title: Text(activity.db.maxRunningCadence.toString()),
|
||||
trailing: Text('max running cadence'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.settings_input_antenna),
|
||||
title: Text(activity.db.trigger),
|
||||
trailing: Text('trigger'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.ac_unit),
|
||||
title: Text(
|
||||
'${activity.db.avgTemperature}° ... ${activity.db.maxTemperature}°'),
|
||||
trailing: Text('avg ... max temperature'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.linear_scale),
|
||||
title: Text(
|
||||
'${activity.db.avgFractionalCadence.toStringAsFixed(2)} ... ${activity.db.maxFractionalCadence.toStringAsFixed(2)}'),
|
||||
trailing: Text('avg ... max\nfractional cadence'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.spa),
|
||||
title: Text(
|
||||
'${activity.db.avgHeartRate} ... ${activity.db.maxHeartRate}'),
|
||||
trailing: Text('avg ... max heart rate'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.repeat),
|
||||
title: Text('${activity.db.numLaps} / ${activity.db.numSessions}'),
|
||||
trailing: Text('number of laps / sessions'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.pets),
|
||||
title: Text(
|
||||
'${activity.db.avgStanceTime}ms / ${activity.db.avgStanceTimePercent}%'),
|
||||
trailing: Text('avg stance time / %'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.fitness_center),
|
||||
title: Text(activity.db.totalTrainingEffect.toString()),
|
||||
trailing: Text('total training effect'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.unfold_more),
|
||||
title: Text(activity.db.avgVerticalOscillation.toString()),
|
||||
trailing: Text('avg vertical oscillation'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.pets),
|
||||
title: Text(activity.db.avgRunningCadence.toString()),
|
||||
trailing: Text('avg running cadence'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.repeat),
|
||||
title: Text(activity.db.totalFractionalCycles.toString()),
|
||||
trailing: Text('total fractional cycles'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.map),
|
||||
title: Text(activity.db.startPositionLong.semicirclesAsDegrees() +
|
||||
" /\n" +
|
||||
activity.db.startPositionLat.semicirclesAsDegrees()),
|
||||
trailing: Text('start position long /\nlat'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.map),
|
||||
title: Text(activity.db.necLong.semicirclesAsDegrees() +
|
||||
" /\n" +
|
||||
activity.db.necLat.semicirclesAsDegrees()),
|
||||
trailing: Text('north east corner long /\nlat'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.map),
|
||||
title: Text(activity.db.swcLong.semicirclesAsDegrees() +
|
||||
" /\n" +
|
||||
activity.db.swcLat.semicirclesAsDegrees()),
|
||||
trailing: Text('south west corner long /\nlat'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:encrateia/utils/date_time_utils.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:encrateia/models/lap.dart';
|
||||
|
||||
class LapMetadataWidget extends StatelessWidget {
|
||||
final Lap lap;
|
||||
|
||||
LapMetadataWidget({this.lap});
|
||||
|
||||
@override
|
||||
Widget build(context) {
|
||||
return new ListTileTheme(
|
||||
dense: true,
|
||||
iconColor: Colors.lightGreen,
|
||||
child: ListView(
|
||||
padding: EdgeInsets.only(left: 8, right: 8),
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
leading: Icon(Icons.repeat_one),
|
||||
title: Text('Lap ${lap.index}'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.event),
|
||||
title: Text(lap.db.event),
|
||||
trailing: Text("event"),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.directions_bike),
|
||||
title: Text(lap.db.sport + '/' + lap.db.subSport),
|
||||
trailing: Text('sport / sub sport'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:encrateia/models/activity.dart';
|
||||
import 'package:encrateia/models/lap.dart';
|
||||
import 'package:encrateia/screens/show_lap_screen.dart';
|
||||
|
||||
class LapsListWidget extends StatelessWidget {
|
||||
final Activity activity;
|
||||
|
@ -17,6 +18,7 @@ class LapsListWidget extends StatelessWidget {
|
|||
if (snapshot.hasData) {
|
||||
widget = DataTable(
|
||||
dataRowHeight: kMinInteractiveDimension * 0.60,
|
||||
columnSpacing: 20,
|
||||
columns: <DataColumn>[
|
||||
const DataColumn(
|
||||
label: Icon(Icons.loop),
|
||||
|
@ -33,6 +35,11 @@ class LapsListWidget extends StatelessWidget {
|
|||
tooltip: 'speed',
|
||||
numeric: true,
|
||||
),
|
||||
const DataColumn(
|
||||
label: Icon(Icons.swap_calls),
|
||||
tooltip: 'distance',
|
||||
numeric: true,
|
||||
),
|
||||
const DataColumn(
|
||||
label: Icon(Icons.trending_up),
|
||||
tooltip: 'ascent',
|
||||
|
@ -40,20 +47,39 @@ class LapsListWidget extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
rows: snapshot.data.map((Lap lap) {
|
||||
return DataRow(key: Key(lap.db.id.toString()), cells: [
|
||||
DataCell(
|
||||
Text(lap.index.toString()),
|
||||
),
|
||||
DataCell(
|
||||
Text(lap.db.avgHeartRate.toString()),
|
||||
),
|
||||
DataCell(
|
||||
Text((lap.db.avgSpeed * 3.6).toStringAsFixed(2)),
|
||||
),
|
||||
DataCell(
|
||||
Text((lap.db.totalAscent - lap.db.totalDescent).toString()),
|
||||
),
|
||||
]);
|
||||
return DataRow(
|
||||
key: Key(lap.db.id.toString()),
|
||||
onSelectChanged: (bool selected) {
|
||||
if (selected) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ShowLapScreen(
|
||||
lap: lap,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
cells: [
|
||||
DataCell(
|
||||
Text(lap.index.toString()),
|
||||
),
|
||||
DataCell(
|
||||
Text(lap.db.avgHeartRate.toString()),
|
||||
),
|
||||
DataCell(
|
||||
Text((lap.db.avgSpeed * 3.6).toStringAsFixed(2)),
|
||||
),
|
||||
DataCell(
|
||||
Text((lap.db.totalDistance / 1000).toStringAsFixed(3) +
|
||||
' km'),
|
||||
),
|
||||
DataCell(
|
||||
Text((lap.db.totalAscent - lap.db.totalDescent).toString()),
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue