implementing API graphs and getting frustrated at async stuff
This commit is contained in:
parent
bf1046c4e5
commit
d7625f7b88
7 changed files with 134 additions and 169 deletions
|
@ -1,17 +0,0 @@
|
||||||
TODO
|
|
||||||
⁃ Show username
|
|
||||||
⁃ add ‘confirm logout’
|
|
||||||
|
|
||||||
Splash Screen
|
|
||||||
⁃ Splash screen transition - fade
|
|
||||||
|
|
||||||
Login
|
|
||||||
⁃ save login details option (checkbox or something)
|
|
||||||
|
|
||||||
Navigation
|
|
||||||
⁃ Make it look good
|
|
||||||
|
|
||||||
Submit Receipt
|
|
||||||
⁃ Categories
|
|
||||||
⁃ Recurring
|
|
||||||
⁃ 'organisation name' dialog works
|
|
|
@ -26,4 +26,4 @@ subprojects {
|
||||||
|
|
||||||
task clean(type: Delete) {
|
task clean(type: Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
|
@ -2,13 +2,12 @@ import 'dart:convert';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:charts_flutter/flutter.dart' as charts;
|
||||||
enum DataPoint {date, String}
|
|
||||||
|
|
||||||
class GraphData {
|
class GraphData {
|
||||||
List<DataPoint> data = new List<DataPoint>();
|
List<charts.Series> data = new List<charts.Series>();
|
||||||
|
|
||||||
Future<List<DataPoint>> getGraphData(String graphType) async {
|
Future<List<charts.Series>> getGraphData(String graphType) async {
|
||||||
/// Graph types:
|
/// Graph types:
|
||||||
/// - total_last_week
|
/// - total_last_week
|
||||||
/// - avg_spend_last_week
|
/// - avg_spend_last_week
|
||||||
|
@ -18,6 +17,7 @@ class GraphData {
|
||||||
/// HTTP POST request sample:
|
/// HTTP POST request sample:
|
||||||
/// {"graph":"total_last_week","session_key":"blahblahblah"}
|
/// {"graph":"total_last_week","session_key":"blahblahblah"}
|
||||||
|
|
||||||
|
charts.Series dataSeries = new charts.Series();
|
||||||
final url = "https://dev.peartrade.org/api/v1/customer/graphs";
|
final url = "https://dev.peartrade.org/api/v1/customer/graphs";
|
||||||
SharedPreferences preferences = await SharedPreferences.getInstance();
|
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
@ -33,17 +33,62 @@ class GraphData {
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final responseJson = jsonDecode(response.body);
|
final responseJson = jsonDecode(response.body);
|
||||||
final labels = responseJson['graph']['labels'];
|
final List<dynamic> labels = responseJson['graph']['labels'];
|
||||||
final data = responseJson['graph']['data'];
|
final List<dynamic> data = responseJson['graph']['data'];
|
||||||
// final bounds = responseJson['graph']['bounds']; // why is this even returned?
|
// final List<String> bounds = responseJson['graph']['bounds']; // why is this even returned?
|
||||||
|
|
||||||
|
/*
|
||||||
|
final data = [
|
||||||
|
new TimeSeriesSales(new DateTime(2017, 9, 19), 5),
|
||||||
|
new TimeSeriesSales(new DateTime(2017, 9, 26), 25),
|
||||||
|
new TimeSeriesSales(new DateTime(2017, 10, 3), 100),
|
||||||
|
new TimeSeriesSales(new DateTime(2017, 10, 10), 75),
|
||||||
|
];
|
||||||
|
*/
|
||||||
|
|
||||||
|
List<TimeSeriesSpend> timeSeriesSpendList = new List<TimeSeriesSpend>();
|
||||||
|
|
||||||
|
for (int i = 0; i < labels.length; i++) {
|
||||||
|
print(DateTime(labels[i]));
|
||||||
|
timeSeriesSpendList.add(new TimeSeriesSpend(data[i], DateTime(labels[i])));
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
new charts.Series<TimeSeriesSpend, DateTime>(
|
||||||
|
id: 'Spend',
|
||||||
|
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
|
||||||
|
domainFn: (TimeSeriesSpend spend, _) => spend.time,
|
||||||
|
measureFn: (TimeSeriesSpend spend, _) => spend.spend,
|
||||||
|
data: timeSeriesSpendList,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
|
||||||
|
/*
|
||||||
|
new charts.Series<TimeSeriesSales, DateTime>(
|
||||||
|
id: 'Sales',
|
||||||
|
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
|
||||||
|
domainFn: (TimeSeriesSales sales, _) => sales.time,
|
||||||
|
measureFn: (TimeSeriesSales sales, _) => sales.sales,
|
||||||
|
data: data,
|
||||||
|
)*/
|
||||||
|
|
||||||
// print(labels[5]);
|
// print(labels[5]);
|
||||||
// print(data[5]);
|
// print(data[5]);
|
||||||
} else {
|
} else {
|
||||||
final errorMessage = json.decode(response.body)['message'];
|
final errorMessage = json.decode(response.body)['message'];
|
||||||
print("Error: " + errorMessage);
|
print("Error: " + errorMessage);
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TimeSeriesSpend {
|
||||||
|
final DateTime time;
|
||||||
|
final int spend;
|
||||||
|
|
||||||
|
TimeSeriesSpend(this.spend, this.time);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
60
lib/common/widgets/charts/time_series_simple.dart
Normal file
60
lib/common/widgets/charts/time_series_simple.dart
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/// Timeseries chart example
|
||||||
|
import 'package:charts_flutter/flutter.dart' as charts;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SimpleTimeSeriesChart extends StatelessWidget {
|
||||||
|
final List<charts.Series> seriesList;
|
||||||
|
final bool animate;
|
||||||
|
|
||||||
|
SimpleTimeSeriesChart(this.seriesList, {this.animate});
|
||||||
|
|
||||||
|
/// Creates a [TimeSeriesChart] with sample data and no transition.
|
||||||
|
factory SimpleTimeSeriesChart.withSampleData() {
|
||||||
|
return new SimpleTimeSeriesChart(
|
||||||
|
_createSampleData(),
|
||||||
|
// Disable animations for image tests.
|
||||||
|
animate: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return new charts.TimeSeriesChart(
|
||||||
|
seriesList,
|
||||||
|
animate: animate,
|
||||||
|
// Optionally pass in a [DateTimeFactory] used by the chart. The factory
|
||||||
|
// should create the same type of [DateTime] as the data provided. If none
|
||||||
|
// specified, the default creates local date time.
|
||||||
|
dateTimeFactory: const charts.LocalDateTimeFactory(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create one series with sample hard coded data.
|
||||||
|
static List<charts.Series<TimeSeriesSales, DateTime>> _createSampleData() {
|
||||||
|
final data = [
|
||||||
|
new TimeSeriesSales(new DateTime(2017, 9, 19), 5),
|
||||||
|
new TimeSeriesSales(new DateTime(2017, 9, 26), 25),
|
||||||
|
new TimeSeriesSales(new DateTime(2017, 10, 3), 100),
|
||||||
|
new TimeSeriesSales(new DateTime(2017, 10, 10), 75),
|
||||||
|
];
|
||||||
|
|
||||||
|
return [
|
||||||
|
new charts.Series<TimeSeriesSales, DateTime>(
|
||||||
|
id: 'Sales',
|
||||||
|
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
|
||||||
|
domainFn: (TimeSeriesSales sales, _) => sales.time,
|
||||||
|
measureFn: (TimeSeriesSales sales, _) => sales.sales,
|
||||||
|
data: data,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sample time series data type.
|
||||||
|
class TimeSeriesSales {
|
||||||
|
final DateTime time;
|
||||||
|
final int sales;
|
||||||
|
|
||||||
|
TimeSeriesSales(this.time, this.sales);
|
||||||
|
}
|
|
@ -15,8 +15,8 @@ void main() {
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
GraphData gd = new GraphData();
|
// GraphData gd = new GraphData();
|
||||||
gd.getGraphData('total_last_week');
|
// gd.getGraphData('total_last_week');
|
||||||
|
|
||||||
//var config = ConfigWrapper.of(context);
|
//var config = ConfigWrapper.of(context);
|
||||||
return new MaterialApp(
|
return new MaterialApp(
|
||||||
|
|
|
@ -204,7 +204,7 @@ class ReceiptPageState extends State<ReceiptPage> {
|
||||||
|
|
||||||
List<String> getRecurringOptions() {
|
List<String> getRecurringOptions() {
|
||||||
var options = new List<String>(7);
|
var options = new List<String>(7);
|
||||||
options[0] = "None";
|
options[0] = "None"; // this should not be hardcoded and should be fetched from API instead
|
||||||
options[1] = "Daily";
|
options[1] = "Daily";
|
||||||
options[2] = "Weekly";
|
options[2] = "Weekly";
|
||||||
options[3] = "Fortnightly";
|
options[3] = "Fortnightly";
|
||||||
|
@ -556,7 +556,7 @@ class ReceiptPageState extends State<ReceiptPage> {
|
||||||
value: value,
|
value: value,
|
||||||
child: Text(value),
|
child: Text(value),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(), // fix errors here by [items] being an empty container while _categoryDropDownItems is null
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -12,6 +12,9 @@ import 'package:local_spend/common/widgets/charts/grouped_bar_chart.dart';
|
||||||
import 'package:local_spend/common/widgets/charts/scatter_bucketingAxis_legend.dart';
|
import 'package:local_spend/common/widgets/charts/scatter_bucketingAxis_legend.dart';
|
||||||
import 'package:local_spend/common/widgets/charts/numeric_line_bar_combo.dart';
|
import 'package:local_spend/common/widgets/charts/numeric_line_bar_combo.dart';
|
||||||
import 'package:local_spend/common/widgets/charts/series_legend_with_measures.dart';
|
import 'package:local_spend/common/widgets/charts/series_legend_with_measures.dart';
|
||||||
|
import 'package:local_spend/common/widgets/charts/time_series_simple.dart';
|
||||||
|
import 'package:local_spend/common/apifunctions/get_graph_data.dart';
|
||||||
|
import 'package:charts_flutter/flutter.dart' as charts;
|
||||||
|
|
||||||
const URL = "https://flutter.io/";
|
const URL = "https://flutter.io/";
|
||||||
const demonstration = false;
|
const demonstration = false;
|
||||||
|
@ -25,6 +28,9 @@ class StatsPage extends StatefulWidget {
|
||||||
|
|
||||||
class StatsPageState extends State<StatsPage> {
|
class StatsPageState extends State<StatsPage> {
|
||||||
|
|
||||||
|
GraphData graphData = new GraphData();
|
||||||
|
List<charts.Series> totalLastWeek;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -43,6 +49,15 @@ class StatsPageState extends State<StatsPage> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (graphData.data != null) {
|
||||||
|
graphData.getGraphData('total_last_week').then((val) {
|
||||||
|
totalLastWeek = val;
|
||||||
|
setState(() {
|
||||||
|
// update view
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return PlatformScaffold(
|
return PlatformScaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.blue[400],
|
backgroundColor: Colors.blue[400],
|
||||||
|
@ -63,30 +78,11 @@ class StatsPageState extends State<StatsPage> {
|
||||||
padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
|
padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
// some graphs and charts here etc
|
|
||||||
// Container(
|
|
||||||
// padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
|
||||||
// child : Text(
|
|
||||||
// "Really Cool Chart",
|
|
||||||
// textAlign: TextAlign.center,
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: 22.0,
|
|
||||||
// color: Colors.black,
|
|
||||||
// fontWeight: FontWeight.bold,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
//
|
|
||||||
// Container(
|
|
||||||
// height: 250,
|
|
||||||
//// width: 250,
|
|
||||||
// child: new DonutPieChart.withSampleData()
|
|
||||||
// ),
|
|
||||||
|
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
||||||
child : Text(
|
child : Text(
|
||||||
"GroupedBarChart",
|
"This Week's Spend",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 22.0,
|
fontSize: 22.0,
|
||||||
|
@ -100,127 +96,8 @@ class StatsPageState extends State<StatsPage> {
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||||
height: 200,
|
height: 200,
|
||||||
// width: 250,
|
// width: 250,
|
||||||
child: new GroupedBarChart.withSampleData()
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
child: new SimpleTimeSeriesChart(totalLastWeek),//seriesList: List<charts.Series>
|
||||||
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
|
||||||
child : Text(
|
|
||||||
"BucketingAxisScatterPlotChart",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
height: 200,
|
|
||||||
// width: 250,
|
|
||||||
child: new BucketingAxisScatterPlotChart.withSampleData()
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,20,0.0,0.0),
|
|
||||||
child : Text(
|
|
||||||
"PieOutsideLabelChart",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
height: 200,
|
|
||||||
// width: 250,
|
|
||||||
child: new PieOutsideLabelChart.withSampleData()
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
|
||||||
child : Text(
|
|
||||||
"DonutAutoLabelChart",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
height: 200,
|
|
||||||
// width: 250,
|
|
||||||
child: new DonutAutoLabelChart.withSampleData()
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
|
||||||
child : Text(
|
|
||||||
"DonutPieChart",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
height: 200,
|
|
||||||
// width: 250,
|
|
||||||
child: new DonutPieChart.withSampleData()
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
|
||||||
child : Text(
|
|
||||||
"NumericComboLineBarChart",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
height: 200,
|
|
||||||
// width: 250,
|
|
||||||
child: new NumericComboLineBarChart.withSampleData()
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
|
||||||
child : Text(
|
|
||||||
"LegendWithMeasures",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
height: 200,
|
|
||||||
// width: 250,
|
|
||||||
child: new LegendWithMeasures.withSampleData()
|
|
||||||
),
|
),
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
Reference in a new issue