list view styling, lap detail screen and metadata widget

master
Stefan Haslinger 2020-01-18 12:29:26 +01:00
parent c551ba5498
commit eabdcecf22
9 changed files with 287 additions and 177 deletions

View File

@ -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';

View File

@ -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;

View File

@ -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),
]),
),
);
}
}

View File

@ -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'),
),
],
),
);
}
}

View File

@ -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'),
),
],
),
);
}
}

View File

@ -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 {