mirror of
https://github.com/supleed2/ELEC60013-ES-CW1.git
synced 2024-12-23 05:55:49 +00:00
Added metrics page
This commit is contained in:
parent
cdcd7d6d6b
commit
716d2f7b62
|
@ -2,5 +2,4 @@ package com.legbarkr.leg_barkr_app
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
class MainActivity: FlutterActivity() {
|
class MainActivity: FlutterActivity()
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:leg_barkr_app/view/metrics/metrics_page.dart';
|
||||||
import 'package:leg_barkr_app/view/steps/steps_page.dart';
|
import 'package:leg_barkr_app/view/steps/steps_page.dart';
|
||||||
import 'package:leg_barkr_app/view/data_page.dart';
|
|
||||||
import 'package:leg_barkr_app/view/map_page.dart';
|
import 'package:leg_barkr_app/view/map_page.dart';
|
||||||
import 'package:leg_barkr_app/view/settings_page.dart';
|
import 'package:leg_barkr_app/view/settings_page.dart';
|
||||||
|
|
||||||
|
@ -29,16 +29,21 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||||
body: PageView(
|
body: PageView(
|
||||||
controller: _pageController,
|
controller: _pageController,
|
||||||
children: const <Widget>[
|
children: const <Widget>[
|
||||||
DataPage(),
|
MetricsPage(),
|
||||||
StepsPage(),
|
StepsPage(),
|
||||||
MapPage(),
|
MapPage(),
|
||||||
SettingsPage()
|
SettingsPage()
|
||||||
],
|
],
|
||||||
|
onPageChanged: (page) {
|
||||||
|
setState(() {
|
||||||
|
_page = page;
|
||||||
|
});
|
||||||
|
},
|
||||||
),
|
),
|
||||||
bottomNavigationBar: BottomNavigationBar(
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
items: const <BottomNavigationBarItem>[
|
items: const <BottomNavigationBarItem>[
|
||||||
BottomNavigationBarItem(icon: Icon(Icons.data_usage), label: 'Home'),
|
BottomNavigationBarItem(icon: Icon(Icons.data_usage), label: 'Home'),
|
||||||
BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Search'),
|
BottomNavigationBarItem(icon: Icon(Icons.pets), label: 'Steps'),
|
||||||
BottomNavigationBarItem(icon: Icon(Icons.location_on_outlined), label: 'Location'),
|
BottomNavigationBarItem(icon: Icon(Icons.location_on_outlined), label: 'Location'),
|
||||||
BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
|
BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
|
||||||
],
|
],
|
||||||
|
|
16
lib/model/metrics_data.dart
Normal file
16
lib/model/metrics_data.dart
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
class MetricsData{
|
||||||
|
double currentReading, lowestReading, highestReading, minimumPossible, maximumPossible, lowCutOff, highCutOff;
|
||||||
|
String metric, units;
|
||||||
|
|
||||||
|
MetricsData(
|
||||||
|
this.currentReading,
|
||||||
|
this.lowestReading,
|
||||||
|
this.highestReading,
|
||||||
|
this.minimumPossible,
|
||||||
|
this.maximumPossible,
|
||||||
|
this.lowCutOff,
|
||||||
|
this.highCutOff,
|
||||||
|
this.metric,
|
||||||
|
this.units
|
||||||
|
);
|
||||||
|
}
|
6
lib/model/temp_series.dart
Normal file
6
lib/model/temp_series.dart
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
class TempSeries {
|
||||||
|
final DateTime date;
|
||||||
|
final double temperature;
|
||||||
|
|
||||||
|
TempSeries(this.date, this.temperature);
|
||||||
|
}
|
13
lib/utils/constants.dart
Normal file
13
lib/utils/constants.dart
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
const double LOW_AIR_TEMP_DOG = -5.0;
|
||||||
|
const double HIGH_AIR_TEMP_DOG = 29.0;
|
||||||
|
const double LOW_SKIN_TEMP_DOG = 37.5;
|
||||||
|
const double HIGH_SKIN_TEMP_DOG = 39.4;
|
||||||
|
const double LOW_HUMIDITY_DOG = 0.0;
|
||||||
|
const double HIGH_HUMIDITY_DOG = 100.0;
|
||||||
|
|
||||||
|
const double MAX_AIR_TEMP = 50.0;
|
||||||
|
const double MIN_AIR_TEMP = -25.0;
|
||||||
|
const double MAX_SKIN_TEMP = 42;
|
||||||
|
const double MIN_SKIN_TEMP = 35;
|
||||||
|
const double MAX_HUMIDITY = 100;
|
||||||
|
const double MIN_HUMIDITY = 0;
|
|
@ -1,10 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class DataPage extends StatelessWidget {
|
|
||||||
const DataPage({ Key? key }) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Text("Data Page");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,7 +11,7 @@ class MapPage extends StatefulWidget {
|
||||||
class _MapPageState extends State<MapPage> {
|
class _MapPageState extends State<MapPage> {
|
||||||
late GoogleMapController _mapController;
|
late GoogleMapController _mapController;
|
||||||
|
|
||||||
// This will be changed, to center around the dog (once app reads data from the server)
|
// This will be changed, to center around the dog (once app reads metrics from the server)
|
||||||
final LatLng _center = const LatLng(51.498356, -0.176894);
|
final LatLng _center = const LatLng(51.498356, -0.176894);
|
||||||
|
|
||||||
void _onMapCreated(GoogleMapController mapController) {
|
void _onMapCreated(GoogleMapController mapController) {
|
||||||
|
|
26
lib/view/metrics/metrics_min_max.dart
Normal file
26
lib/view/metrics/metrics_min_max.dart
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class MetricsMinMax extends StatelessWidget {
|
||||||
|
double lowest, highest;
|
||||||
|
String units;
|
||||||
|
|
||||||
|
MetricsMinMax(this.lowest, this.highest, this.units);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(0.0, 10.0, 5.0, 10.0),
|
||||||
|
child: Text("Minimum\n" + lowest.toString() + " " + units, textAlign: TextAlign.center, style: TextStyle(color: Colors.black, fontSize: 16, fontWeight: FontWeight.bold))
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(5.0, 10.0, 0.0, 10.0),
|
||||||
|
child: Text("Maximum\n" + highest.toString() + " " + units, textAlign: TextAlign.center, style: TextStyle(color: Colors.black, fontSize: 16, fontWeight: FontWeight.bold))
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
98
lib/view/metrics/metrics_now.dart
Normal file
98
lib/view/metrics/metrics_now.dart
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:leg_barkr_app/model/metrics_data.dart';
|
||||||
|
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
||||||
|
|
||||||
|
class MetricsNow extends StatelessWidget {
|
||||||
|
MetricsData data;
|
||||||
|
Color textColor;
|
||||||
|
bool showGauge;
|
||||||
|
|
||||||
|
MetricsNow(this.data, this.textColor, this.showGauge);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (showGauge) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 150,
|
||||||
|
width: 150,
|
||||||
|
child: SfRadialGauge(
|
||||||
|
axes: [
|
||||||
|
RadialAxis(
|
||||||
|
minimum: data.minimumPossible,
|
||||||
|
maximum: data.maximumPossible,
|
||||||
|
ranges: [
|
||||||
|
GaugeRange(
|
||||||
|
startValue: data.minimumPossible,
|
||||||
|
endValue: data.lowCutOff,
|
||||||
|
color: Colors.blue,
|
||||||
|
startWidth: 10,
|
||||||
|
endWidth: 10),
|
||||||
|
GaugeRange(
|
||||||
|
startValue: data.lowCutOff,
|
||||||
|
endValue: data.highCutOff,
|
||||||
|
color: textColor,
|
||||||
|
startWidth: 10,
|
||||||
|
endWidth: 10),
|
||||||
|
GaugeRange(
|
||||||
|
startValue: data.highCutOff,
|
||||||
|
endValue: data.maximumPossible,
|
||||||
|
color: Colors.red,
|
||||||
|
startWidth: 10,
|
||||||
|
endWidth: 10)
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
MarkerPointer(
|
||||||
|
value: data.currentReading,
|
||||||
|
color: Colors.black,
|
||||||
|
markerWidth: 20)
|
||||||
|
],
|
||||||
|
annotations: [
|
||||||
|
GaugeAnnotation(
|
||||||
|
angle: 90,
|
||||||
|
positionFactor: 0.75,
|
||||||
|
widget: Column(
|
||||||
|
children: [
|
||||||
|
Text(data.currentReading.toString(),
|
||||||
|
style: TextStyle(fontSize: 26,
|
||||||
|
color: textColor,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
Text(data.units, style: TextStyle(fontSize: 16,
|
||||||
|
color: textColor,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return ElevatedButton(
|
||||||
|
onPressed: () {},
|
||||||
|
child:Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(data.currentReading.toString(),
|
||||||
|
style: TextStyle(fontSize: 32,
|
||||||
|
color: textColor,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
Text(data.units, style: TextStyle(fontSize: 18,
|
||||||
|
color: textColor,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
side: BorderSide(width: 15.0, color: textColor),
|
||||||
|
shape: CircleBorder(),
|
||||||
|
padding: EdgeInsets.all(25),
|
||||||
|
primary: Colors.green,
|
||||||
|
minimumSize: Size(150, 150),
|
||||||
|
maximumSize: Size(150, 150)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
54
lib/view/metrics/metrics_page.dart
Normal file
54
lib/view/metrics/metrics_page.dart
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:leg_barkr_app/model/metrics_data.dart';
|
||||||
|
import 'package:leg_barkr_app/model/temp_series.dart';
|
||||||
|
import 'metrics_row.dart';
|
||||||
|
import 'package:leg_barkr_app/utils/constants.dart' as Constants;
|
||||||
|
|
||||||
|
import 'temp_chart.dart';
|
||||||
|
|
||||||
|
class MetricsPage extends StatefulWidget {
|
||||||
|
const MetricsPage({ Key? key }) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_MetricsPageState createState() => _MetricsPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MetricsPageState extends State<MetricsPage> {
|
||||||
|
// Dummy data, will be removed
|
||||||
|
final List<TempSeries> data = [
|
||||||
|
TempSeries(DateTime.parse('2022-02-09 20:00:00Z'), 38.4),
|
||||||
|
TempSeries(DateTime.parse('2022-02-09 19:30:00Z'), 38.8),
|
||||||
|
TempSeries(DateTime.parse('2022-02-09 19:00:00Z'), 38.2),
|
||||||
|
TempSeries(DateTime.parse('2022-02-09 18:30:00Z'), 39.2),
|
||||||
|
TempSeries(DateTime.parse('2022-02-09 18:00:00Z'), 39.5),
|
||||||
|
TempSeries(DateTime.parse('2022-02-09 17:30:00Z'), 37.8)
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(0.0, 50.0, 0.0, 0.0),
|
||||||
|
child: Expanded(
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.all(5.0),
|
||||||
|
children: <Widget>[
|
||||||
|
Text("Today's summary", textAlign: TextAlign.center, style: TextStyle(color: Colors.black, fontSize: 36, fontWeight: FontWeight.bold)),
|
||||||
|
|
||||||
|
// Dummy data
|
||||||
|
MetricsRow(new MetricsData(38.6, 38.1, 39.2, Constants.MIN_SKIN_TEMP, Constants.MAX_SKIN_TEMP, Constants.LOW_SKIN_TEMP_DOG, Constants.HIGH_SKIN_TEMP_DOG, "Skin temperature", "°C"), Colors.white, Colors.green, true),
|
||||||
|
MetricsRow(new MetricsData(22, 16, 34, Constants.MIN_HUMIDITY, Constants.MAX_HUMIDITY, Constants.LOW_HUMIDITY_DOG, Constants.HIGH_HUMIDITY_DOG, "Humidity", "%"), Colors.green, Colors.black, false),
|
||||||
|
MetricsRow(new MetricsData(24, 21, 29, Constants.MIN_AIR_TEMP, Constants.MAX_AIR_TEMP, Constants.LOW_AIR_TEMP_DOG, Constants.HIGH_AIR_TEMP_DOG, "Air temperature", "°C"), Colors.white, Colors.green, true),
|
||||||
|
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 0.0),
|
||||||
|
child: Text("Today's temperature", textAlign: TextAlign.center, style: TextStyle(color: Colors.black, fontSize: 36, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
TempChart(data)
|
||||||
|
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
32
lib/view/metrics/metrics_row.dart
Normal file
32
lib/view/metrics/metrics_row.dart
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:leg_barkr_app/model/metrics_data.dart';
|
||||||
|
import 'metrics_now.dart';
|
||||||
|
import 'metrics_summary.dart';
|
||||||
|
|
||||||
|
class MetricsRow extends StatelessWidget {
|
||||||
|
MetricsData data;
|
||||||
|
Color backgroundColour;
|
||||||
|
Color textColour;
|
||||||
|
bool showGauge;
|
||||||
|
|
||||||
|
MetricsRow(this.data, this.backgroundColour, this.textColour, this.showGauge);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
elevation: 10,
|
||||||
|
shadowColor: Colors.black,
|
||||||
|
color: backgroundColour,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(5.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
MetricsNow(data, textColour, showGauge),
|
||||||
|
MetricsSummary(data, textColour)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
27
lib/view/metrics/metrics_summary.dart
Normal file
27
lib/view/metrics/metrics_summary.dart
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:leg_barkr_app/model/metrics_data.dart';
|
||||||
|
import 'metrics_min_max.dart';
|
||||||
|
|
||||||
|
class MetricsSummary extends StatelessWidget {
|
||||||
|
MetricsData data;
|
||||||
|
Color textColour;
|
||||||
|
|
||||||
|
MetricsSummary(this.data, this.textColour);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(15.0),
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(data.metric, textAlign: TextAlign.center, style: TextStyle(color: textColour, fontSize: 24, fontWeight: FontWeight.bold)),
|
||||||
|
MetricsMinMax(data.lowestReading, data.highestReading, data.units)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
36
lib/view/metrics/temp_chart.dart
Normal file
36
lib/view/metrics/temp_chart.dart
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:charts_flutter/flutter.dart' as charts;
|
||||||
|
import 'package:leg_barkr_app/model/temp_series.dart';
|
||||||
|
import 'package:leg_barkr_app/utils/constants.dart' as Constants;
|
||||||
|
|
||||||
|
class TempChart extends StatelessWidget {
|
||||||
|
List<TempSeries> tempData;
|
||||||
|
|
||||||
|
TempChart(this.tempData);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
List<charts.Series<TempSeries, DateTime>> series = [
|
||||||
|
charts.Series(
|
||||||
|
id: "Temperature",
|
||||||
|
data: tempData,
|
||||||
|
domainFn: (TempSeries series, _) => series.date,
|
||||||
|
measureFn: (TempSeries series, _) => series.temperature,
|
||||||
|
colorFn: (TempSeries series, _) => charts.ColorUtil.fromDartColor(Colors.green)
|
||||||
|
)
|
||||||
|
];
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
height: 600,
|
||||||
|
width: double.infinity,
|
||||||
|
child: charts.TimeSeriesChart(
|
||||||
|
series,
|
||||||
|
animate: true,
|
||||||
|
primaryMeasureAxis: const charts.NumericAxisSpec(
|
||||||
|
tickProviderSpec: charts.BasicNumericTickProviderSpec(zeroBound: false),
|
||||||
|
viewport: charts.NumericExtents(Constants.MIN_SKIN_TEMP, Constants.MAX_SKIN_TEMP),
|
||||||
|
),)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:charts_flutter/flutter.dart' as charts;
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:leg_barkr_app/model/steps_series.dart';
|
import 'package:leg_barkr_app/model/steps_series.dart';
|
||||||
import 'package:leg_barkr_app/view/steps/steps_chart.dart';
|
import 'package:leg_barkr_app/view/steps/steps_chart.dart';
|
||||||
|
@ -12,7 +11,7 @@ class StepsPage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _StepsPageState extends State<StepsPage> {
|
class _StepsPageState extends State<StepsPage> {
|
||||||
// Dummy data
|
// Dummy metrics
|
||||||
final List<StepsSeries> data = [
|
final List<StepsSeries> data = [
|
||||||
StepsSeries(DateTime.utc(2022, 2, 9), 9867),
|
StepsSeries(DateTime.utc(2022, 2, 9), 9867),
|
||||||
StepsSeries(DateTime.utc(2022, 2, 8), 8123),
|
StepsSeries(DateTime.utc(2022, 2, 8), 8123),
|
||||||
|
|
14
pubspec.lock
14
pubspec.lock
|
@ -266,6 +266,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
|
syncfusion_flutter_core:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: syncfusion_flutter_core
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "19.4.50"
|
||||||
|
syncfusion_flutter_gauges:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: syncfusion_flutter_gauges
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "19.4.50"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -37,6 +37,8 @@ dependencies:
|
||||||
google_maps_flutter: ^2.1.1
|
google_maps_flutter: ^2.1.1
|
||||||
google_maps_flutter_web: ^0.3.2+1
|
google_maps_flutter_web: ^0.3.2+1
|
||||||
charts_flutter: ^0.12.0
|
charts_flutter: ^0.12.0
|
||||||
|
syncfusion_flutter_gauges: ^19.4.50
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in a new issue