Compare commits

..

1 commit

Author SHA1 Message Date
Felix
a777bc4e1e experiment failed
we'll get em next time
2019-08-19 10:03:13 +01:00
71 changed files with 1305 additions and 2446 deletions

2
.gitignore vendored
View file

@ -235,5 +235,3 @@ fabric.properties
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
# End of https://www.gitignore.io/api/android,flutter,androidstudio # End of https://www.gitignore.io/api/android,flutter,androidstudio
lib/common/felixApiCreds.dart
android/app/src/main/AndroidManifest.xml

View file

@ -1,116 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

106
README.md
View file

@ -1,99 +1,31 @@
# LocalSpend (Mobile App.) # local_spend
Looking to discover if the value of spending local can be measured, understood and shown. Local Spend Tracker
This repository contains the mobile application for the LocalSpend system. See also: ## Getting Started
* the [Web application](https://github.com/Pear-Trading/Foodloop-Web); and This project is a starting point for a Flutter application.
* the [server](https://github.com/Pear-Trading/Foodloop-Server).
## Table of Contents A few resources to get you started if this is your first Flutter project:
* [Tech Stack](#tech-stack) - [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab)
* [Features](#features) - [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook)
* [Installation](#installation)
* [Configuration](#configuration)
* [Usage](#usage)
* [Testing](#testing)
* [Code Formatting](#code-formatting)
* [Documentation](#documentation)
* [Acknowledgments](#acknowledgements)
* [License](#license)
* [Contact](#contact)
## Technology Stack For help getting started with Flutter, view our
[online documentation](https://flutter.io/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
The mobile app. is written in [Dart](https://dart.dev/). ## Environments
| Technology | Description | Link | To build an apk from dev, use:
|-------------|---------------------------------|---------------------| flutter build apk -t lib/main_dev.dart
| Flutter | Cross-platform mobile framework | [Link][flutter] |
[flutter]: https://flutter.dev/ ## Links and Things
## Features https://github.com/putraxor/flutter-login-ui
https://github.com/pbirdsall/medium_splash_tokenauth
This mobile app. provides: ## How to debug code
// debug
- user authentication; and
- transaction logging.
## Installation
1. Install [Flutter](https://flutter.dev/docs/get-started/install);
1. if this is your first Flutter project, install the [Flutter SDK](https://flutter.dev/docs/get-started/test-drive);
1. set up [your editor](https://flutter.dev/docs/get-started/editor):
- we recommend using [Android Studio](https://developer.android.com/studio).
1. add the line `flutter.sdk=⟨ path to Flutter SDK ⟩` to the file `android/local.properties`.
## Configuration
App. configuration settings are found in `pubspec.yaml`.
Build settings are found in the `android/` directory, in the `build.gradle`, `gradle.properties` and `settings.gradle` files.
## Usage
### Development
To activate debugging, add the following import statement:
```dart
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
``` debugPrint('$foo');
After that, you can generate debugging output using `debugPrint()`.
### Production
Run `flutter build apk -t lib/main_dev.dart` to generate an APK file.
## Testing
TODO
## Code Formatting
TODO
## Documentation
TODO
## Acknowledgements
LocalLoop is the result of collaboration between the [Small Green Consultancy](http://www.smallgreenconsultancy.co.uk/), [Shadowcat Systems](https://shadow.cat/), [Independent Lancaster](http://www.independent-lancaster.co.uk/) and the [Ethical Small Traders Association](http://www.lancasteresta.org/).
## License
This project is released under the [MIT license](https://mit-license.org/).
## Contact
| Name | Link(s) |
|----------------|-------------------|
| Mark Keating | [Email][mkeating] |
| Michael Hallam | [Email][mhallam] |
[mkeating]: mailto:m.keating@shadowcat.co.uk
[mhallam]: mailto:info@lancasteresta.org

View file

@ -1,18 +0,0 @@
include: package:pedantic/analysis_options.yaml
analyzer:
exclude:
- lib/src/locations.g.dart
linter:
rules:
- always_declare_return_types
- camel_case_types
- empty_constructor_bodies
- annotate_overrides
- avoid_init_to_null
- constant_identifier_names
- one_member_abstracts
- slash_for_doc_comments
- sort_constructors_first
- unnecessary_brace_in_string_interps

View file

@ -25,7 +25,7 @@ apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion 28 compileSdkVersion 27
lintOptions { lintOptions {
disable 'InvalidPackage' disable 'InvalidPackage'

View file

@ -13,18 +13,11 @@
additional functionality it is fine to subclass or reimplement additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. --> FlutterApplication and put your custom class here. -->
<application <application
android:label="SpendTracker" android:name="io.flutter.app.FlutterApplication"
android:label="local_spend"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity
<meta-data android:name=".MainActivity"
android:name="flutterEmbedding"
android:value="2" />
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyD0vsoT6Omnn01hbUiCjAhiS47uFYWnEHE"/>
<!-- Please don't be stupid with the above key-->
<activity android:name=".MainActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
@ -34,19 +27,9 @@
until Flutter renders its first frame. It can be removed if until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). --> defined in @style/LaunchTheme). -->
<!-- Specify that the launch screen should continue being displayed -->
<!-- until Flutter renders its first frame. -->
<meta-data <meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable" android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:resource="@drawable/launch_background" /> android:value="true" />
<!-- Theme to apply as soon as Flutter begins rendering frames -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>

View file

@ -1,6 +1,13 @@
package uk.co.localspend.localspend; package uk.co.localspend.localspend;
import io.flutter.embedding.android.FlutterActivity; import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity { public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
} }

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!--<item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item>-->
</layer-list>

View file

@ -5,8 +5,4 @@
Flutter draws its first frame --> Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
</style> </style>
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@drawable/normal_background</item>
</style>
</resources> </resources>

View file

@ -1,4 +1 @@
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
android.enableR8=true

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

View file

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project # Uncomment this line to define a global platform for your project
platform :ios, '9.0' # platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View file

@ -1,13 +1,5 @@
PODS: PODS:
- Flutter (1.0.0) - Flutter (1.0.0)
- google_maps_flutter (0.0.1):
- Flutter
- GoogleMaps
- GoogleMaps (3.3.0):
- GoogleMaps/Maps (= 3.3.0)
- GoogleMaps/Base (3.3.0)
- GoogleMaps/Maps (3.3.0):
- GoogleMaps/Base
- shared_preferences (0.0.1): - shared_preferences (0.0.1):
- Flutter - Flutter
- url_launcher (0.0.1): - url_launcher (0.0.1):
@ -15,19 +7,12 @@ PODS:
DEPENDENCIES: DEPENDENCIES:
- Flutter (from `.symlinks/flutter/ios-profile`) - Flutter (from `.symlinks/flutter/ios-profile`)
- google_maps_flutter (from `.symlinks/plugins/google_maps_flutter/ios`)
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
- url_launcher (from `.symlinks/plugins/url_launcher/ios`) - url_launcher (from `.symlinks/plugins/url_launcher/ios`)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
- GoogleMaps
EXTERNAL SOURCES: EXTERNAL SOURCES:
Flutter: Flutter:
:path: ".symlinks/flutter/ios-profile" :path: ".symlinks/flutter/ios-profile"
google_maps_flutter:
:path: ".symlinks/plugins/google_maps_flutter/ios"
shared_preferences: shared_preferences:
:path: ".symlinks/plugins/shared_preferences/ios" :path: ".symlinks/plugins/shared_preferences/ios"
url_launcher: url_launcher:
@ -35,11 +20,9 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
Flutter: 58dd7d1b27887414a370fcccb9e645c08ffd7a6a Flutter: 58dd7d1b27887414a370fcccb9e645c08ffd7a6a
google_maps_flutter: 78a52114c898b42ea647919679a4c58b70abe876
GoogleMaps: cfee83da305b9aaeccf92c24ac79df11c3003492
shared_preferences: 1feebfa37bb57264736e16865e7ffae7fc99b523 shared_preferences: 1feebfa37bb57264736e16865e7ffae7fc99b523
url_launcher: 0067ddb8f10d36786672aa0722a21717dba3a298 url_launcher: 0067ddb8f10d36786672aa0722a21717dba3a298
PODFILE CHECKSUM: 3389836f37640698630b8f0670315d626d5f1469 PODFILE CHECKSUM: aff02bfeed411c636180d6812254b2daeea14d09
COCOAPODS: 1.7.5 COCOAPODS: 1.7.5

View file

@ -163,7 +163,6 @@
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
ACE3DC71CDB8FA5537935604 /* [CP] Embed Pods Frameworks */, ACE3DC71CDB8FA5537935604 /* [CP] Embed Pods Frameworks */,
E6C2807EE928990FB790046F /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
@ -293,24 +292,6 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
E6C2807EE928990FB790046F /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
"${PODS_ROOT}/GoogleMaps/Maps/Frameworks/GoogleMaps.framework/Resources/GoogleMaps.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleMaps.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
@ -409,7 +390,6 @@
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@ -539,7 +519,6 @@
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@ -567,7 +546,6 @@
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",

View file

@ -1,12 +1,10 @@
#include "AppDelegate.h" #include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h" #include "GeneratedPluginRegistrant.h"
#import "GoogleMaps/GoogleMaps.h"
@implementation AppDelegate @implementation AppDelegate
- (BOOL)application:(UIApplication *)application - (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GMSServices provideAPIKey:@"AIzaSyBkVgDYRQoKjCWlGMyl3V6ROzmLEsa5a0w"];
[GeneratedPluginRegistrant registerWithRegistry:self]; [GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch. // Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions]; return [super application:application didFinishLaunchingWithOptions:launchOptions];

View file

@ -2,8 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>io.flutter.embedded_views_preview</key>
<true/>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>

View file

@ -2,9 +2,10 @@ 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:local_spend/common/functions/get_token.dart'; import 'package:local_spend/common/functions/get_token.dart';
import 'package:flutter/material.dart';
Future<List<String>> getCategories() async { Future<List<String>> getCategories() async {
const url = "https://dev.localspend.co.uk/api/search/category"; const url = "https://dev.peartrade.org/api/search/category";
var token; var token;
await getToken().then((result) { await getToken().then((result) {
@ -37,6 +38,7 @@ Future<List<String>> getCategories() async {
int i = 1; // starts on 1. that was annoying to debug! int i = 1; // starts on 1. that was annoying to debug!
while (true) { while (true) {
if (categoriesJSON[i.toString()] != null) { if (categoriesJSON[i.toString()] != null) {
// print("Iteration " + i.toString()); // print("Iteration " + i.toString());
// print(categoriesJSON[i.toString()]); // print(categoriesJSON[i.toString()]);

View file

@ -1,9 +1,16 @@
import 'dart:convert'; 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:flutter/material.dart';
import 'package:local_spend/common/functions/get_token.dart'; import 'package:local_spend/common/functions/get_token.dart';
class Organisation { class Organisation {
var id = 0;
var name = "";
var postcode = "";
var streetName = ""; //street_name
var town = "";
Organisation( Organisation(
this.id, this.id,
this.name, this.name,
@ -11,22 +18,22 @@ class Organisation {
this.streetName, this.streetName,
this.town, this.town,
); );
var id = 0;
var name = "";
var postcode = "";
var streetName = ""; //street_name
var town = "";
} }
class Organisations { class Organisations {
List<Organisation> getTestData() { List<Organisation> getTestData() {
var numItems = 10; var numItems = 10;
var itemsList = new List<Organisation>(); var itemsList = new List<Organisation>();
for (int i = 0; i < numItems; i++) { for (int i = 0; i < numItems; i++) {
itemsList.add(new Organisation(i, "Payee " + (i + 1).toString(), itemsList.add(new Organisation(
"tee hee hee", "yeet street", "Robloxia")); i,
"Payee " + (i + 1).toString(),
"tee hee hee",
"yeet street",
"Robloxia"
));
} }
return itemsList; return itemsList;
@ -57,7 +64,7 @@ class Organisations {
} }
Future<List<Organisation>> findOrganisations(String search) async { Future<List<Organisation>> findOrganisations(String search) async {
final url = "https://dev.localspend.co.uk/api/search"; final url = "https://dev.peartrade.org/api/search";
var token; var token;
await getToken().then((result) { await getToken().then((result) {
@ -81,5 +88,6 @@ class Organisations {
// not successful // not successful
return null; return null;
} }
} }
} }

View file

@ -4,137 +4,19 @@ 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; import 'package:charts_flutter/flutter.dart' as charts;
/// Customer graph types: https://dev.localspend.co.uk/api/v1/customer/graphs
/// - total_last_week
/// - avg_spend_last_week
/// - total_last_month
/// - avg_spend_last_month
/// Organisations' graphs types: to fetch, POST to https://dev.localspend.co.uk/api/stats/[name] as {"session_key":"[boop beep]"}
/// - organisations_all : organisation
/// - pies : organisation/pies
/// - snippets : organisation/snippets
/// - graphs : organisation/graphs
/// - {"graph":"customers_last_7_days","session_key":"[bleep]"}
/// - {"graph":"customers_last_30_days","session_key":"[blah]"}
/// - {"graph":"sales_last_7_days","session_key":"[bloop]"}
/// - {"graph":"sales_last_7_days","session_key":"[reee]"}
/// - {"graph":"purchases_last_7_days","session_key":"[yee]"}
/// - {"graph":"purchases_last_30_days","session_key":"[yah]"}
/// - {"graph":"purchases_all;","session_key":"[kappa]"} // I don't think this one works
///
/// HTTP POST request sample:
/// {"graph":"total_last_week","session_key":"blahblahblah"}
class OrganisationGraph {
OrganisationGraph(this.chartType, {this.graphsType = ""});
String graphsType =
""; // type of graph, eg customers_last_7_days, sales_last_30_days, purchases_last_30_days etc
String
chartType; // type of chart, eg organisations_all, pies, snippets or graphs
List<charts.Series<TimeSeriesCustomersOrSales, DateTime>> graph;
List<TimeSeriesCustomersOrSales> cachedData;
bool loaded = false;
Future<void> getGraphData() async {
if (loaded) {
this.graph = [
new charts.Series<TimeSeriesCustomersOrSales, DateTime>(
id: 'Chart',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
domainFn: (TimeSeriesCustomersOrSales spend, _) => spend.time,
measureFn: (TimeSeriesCustomersOrSales spend, _) =>
spend.numberOfStuff,
data: cachedData,
)
];
return null;
}
String url = "https://dev.localspend.co.uk/api/v1/organisation/";
if (!(this.chartType == "organisations_all")) {
url += this.chartType;
}
SharedPreferences preferences = await SharedPreferences.getInstance();
Map<String, String> body;
if (this.chartType == "graphs") {
body = {
'graph': this.graphsType,
'session_key': preferences.get('LastToken'),
};
} else {
body = {
'session_key': preferences.get('LastToken'),
};
}
print(url);
print(json.encode(body).toString());
final response = await http.post(
url,
body: json.encode(body),
);
try {
if (response.statusCode == 200) {
final responseJson = jsonDecode(response.body);
final List<dynamic> labels = responseJson['graph']['labels'];
final List<dynamic> data = responseJson['graph']['data'];
List<TimeSeriesCustomersOrSales> graphDataList =
new List<TimeSeriesCustomersOrSales>();
for (int i = 0; i < labels.length; i++) {
graphDataList.add(new TimeSeriesCustomersOrSales(
data[i] * 1.00, DateTime.parse(labels[i])));
}
this.cachedData = graphDataList;
this.loaded = true;
this.graph = [
new charts.Series<TimeSeriesCustomersOrSales, DateTime>(
id: 'Chart',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
domainFn: (TimeSeriesCustomersOrSales spend, _) => spend.time,
measureFn: (TimeSeriesCustomersOrSales spend, _) =>
spend.numberOfStuff,
data: graphDataList,
)
];
return this.graph;
} else {
final errorMessage = json.decode(response.body)['message'];
print("Error: " + errorMessage);
this.graph = null;
}
} catch (error) {
print(error.toString());
}
}
}
class GraphData { class GraphData {
var chartType;
List<charts.Series<dynamic, DateTime>> graph;
GraphData( GraphData(
this.chartType, this.chartType,
); );
var chartType;
List<charts.Series<dynamic, DateTime>> graph;
List<TimeSeriesSpend> cachedData; List<TimeSeriesSpend> cachedData;
bool loaded = false; bool loaded = false;
Future<List<charts.Series<dynamic, DateTime>>> setGraphData() async { Future<void> setGraphData() async {
if (loaded) { if (loaded == true) {
this.graph = [ this.graph = [
new charts.Series<TimeSeriesSpend, DateTime>( new charts.Series<TimeSeriesSpend, DateTime>(
id: 'Spend', id: 'Spend',
@ -144,10 +26,9 @@ class GraphData {
data: cachedData, data: cachedData,
) )
]; ];
return null;
} }
final url = "https://dev.localspend.co.uk/api/v1/customer/graphs"; final url = "https://dev.peartrade.org/api/v1/customer/graphs";
SharedPreferences preferences = await SharedPreferences.getInstance(); SharedPreferences preferences = await SharedPreferences.getInstance();
Map<String, String> body = { Map<String, String> body = {
@ -168,8 +49,7 @@ class GraphData {
List<TimeSeriesSpend> timeSeriesSpendList = new List<TimeSeriesSpend>(); List<TimeSeriesSpend> timeSeriesSpendList = new List<TimeSeriesSpend>();
for (int i = 0; i < labels.length; i++) { for (int i = 0; i < labels.length; i++) {
timeSeriesSpendList.add( timeSeriesSpendList.add(new TimeSeriesSpend(data[i]*1.00, DateTime.parse(labels[i])));
new TimeSeriesSpend(data[i] * 1.00, DateTime.parse(labels[i])));
// print(timeSeriesSpendList[i].time.toString() + " : " + timeSeriesSpendList[i].spend.toString()); // print(timeSeriesSpendList[i].time.toString() + " : " + timeSeriesSpendList[i].spend.toString());
} }
@ -185,13 +65,11 @@ class GraphData {
data: timeSeriesSpendList, data: timeSeriesSpendList,
) )
]; ];
return this.graph;
} else { } else {
final errorMessage = json.decode(response.body)['message']; final errorMessage = json.decode(response.body)['message'];
print("Error: " + errorMessage); print("Error: " + errorMessage);
this.graph = null; this.graph = null;
return null;
} }
} }
@ -208,7 +86,16 @@ class GraphData {
]; ];
} }
final url = "https://dev.localspend.co.uk/api/v1/customer/graphs"; /// Graph types:
/// - total_last_week
/// - avg_spend_last_week
/// - total_last_month
/// - avg_spend_last_month
///
/// HTTP POST request sample:
/// {"graph":"total_last_week","session_key":"blahblahblah"}
final url = "https://dev.peartrade.org/api/v1/customer/graphs";
SharedPreferences preferences = await SharedPreferences.getInstance(); SharedPreferences preferences = await SharedPreferences.getInstance();
Map<String, String> body = { Map<String, String> body = {
@ -229,8 +116,7 @@ class GraphData {
List<TimeSeriesSpend> timeSeriesSpendList = new List<TimeSeriesSpend>(); List<TimeSeriesSpend> timeSeriesSpendList = new List<TimeSeriesSpend>();
for (int i = 0; i < labels.length; i++) { for (int i = 0; i < labels.length; i++) {
timeSeriesSpendList.add( timeSeriesSpendList.add(new TimeSeriesSpend(data[i]*1.00, DateTime.parse(labels[i])));
new TimeSeriesSpend(data[i] * 1.00, DateTime.parse(labels[i])));
// print(timeSeriesSpendList[i].time.toString() + " : " + timeSeriesSpendList[i].spend.toString()); // print(timeSeriesSpendList[i].time.toString() + " : " + timeSeriesSpendList[i].spend.toString());
} }
@ -253,18 +139,14 @@ class GraphData {
return null; return null;
} }
} }
} }
class TimeSeriesSpend { class TimeSeriesSpend {
TimeSeriesSpend(this.spend, this.time);
final DateTime time; final DateTime time;
final double spend; final double spend;
TimeSeriesSpend(this.spend, this.time);
} }
class TimeSeriesCustomersOrSales {
TimeSeriesCustomersOrSales(this.numberOfStuff, this.time);
final DateTime time;
final double numberOfStuff;
}

View file

@ -1,87 +0,0 @@
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:json_annotation/json_annotation.dart';
import 'package:local_spend/common/apifunctions/find_organisations.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart' as gmaps;
// /v1/supplier/location
@JsonSerializable()
class LatLng {
LatLng({
this.lat,
this.lng
});
final double lat, lng;
}
@JsonSerializable()
class Region {
Region({
this.coords,
this.id,
this.name,
this.zoom
});
final LatLng coords;
final String id, name;
final double zoom;
}
@JsonSerializable()
class Location {
Location({
this.organisation,
this.lat,
this.lng
});
final Organisation organisation;
final double lat, lng;
}
@JsonSerializable()
class Locations {
Locations({
this.locations,
this.regions
});
final List<Location> locations;
final List<Region> regions;
}
Future<Locations> getLocations(gmaps.LatLng ne, gmaps.LatLng sw) async {
const pearLocationsURL = 'https://dev.localspend.co.uk/api/v1/supplier/location';
SharedPreferences preferences = await SharedPreferences.getInstance();
Map<String, Map<String, double>> mapData = {
'session_key': preferences.get('LastToken'),
'north_east': {
'latitude': ne.latitude,
'longitude': ne.longitude
},
'south_west': {
'latitude': sw.latitude,
'longitude': sw.longitude
},
};
final response = await http.post(
pearLocationsURL,
body: json.encode(mapData),
);
if (response.statusCode == 200) {
print(response.body.toString());
} else {
print(response.body.toString());
throw HttpException(
'Error - ' + response.reasonPhrase,
);
}
}

View file

@ -11,13 +11,9 @@ Future<void> _incorrectDialog(BuildContext context, bool isLoginWrong) async {
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (BuildContext context) { builder: (BuildContext context) {
return AnimatedContainer( return AlertDialog(
duration: Duration(seconds: 2), title: Text("Unable to Login"),
child: AlertDialog( content: Text(isLoginWrong ? "Incorrect login details. Please try again." : "Our servers are having issues at the moment; sorry for the inconvenience. Please try again later."),
title: Text("Uh-oh!"),
content: Text(isLoginWrong
? "Incorrect login details. Please try again."
: "Our servers are having issues at the moment; sorry for the inconvenience. Please try again later."),
actions: <Widget>[ actions: <Widget>[
FlatButton( FlatButton(
child: Text('OK'), child: Text('OK'),
@ -26,7 +22,6 @@ Future<void> _incorrectDialog(BuildContext context, bool isLoginWrong) async {
}, },
), ),
], ],
),
); );
}, },
); );
@ -34,26 +29,27 @@ Future<void> _incorrectDialog(BuildContext context, bool isLoginWrong) async {
Future<LoginModel> requestLoginAPI( Future<LoginModel> requestLoginAPI(
BuildContext context, String email, String password) async { BuildContext context, String email, String password) async {
final url = "https://dev.localspend.co.uk/api/login"; //var apiUrl = ConfigWrapper.of(context).apiKey;
final url = "https://dev.peartrade.org/api/login";
Map<String, String> body = { Map<String, String> body = {
'email': email, 'email': email,
'password': password, 'password': password,
}; };
// debugPrint('$body');
try { try {
final response = await http final response = await http.post(
.post(
url, url,
body: json.encode(body), body: json.encode(body),
) );
.timeout(Duration(seconds: 5));
if (response.statusCode == 200) { if (response.statusCode == 200) {
final responseJson = json.decode(response.body); final responseJson = json.decode(response.body);
saveCurrentLogin(responseJson, body["email"]); saveCurrentLogin(responseJson, body["email"]);
await Navigator.of(context).pushReplacementNamed('/HomePage'); Navigator.of(context).pushReplacementNamed('/HomePage');
return LoginModel.fromJson(responseJson); return LoginModel.fromJson(responseJson);
} else { } else {
@ -61,15 +57,13 @@ Future<LoginModel> requestLoginAPI(
saveCurrentLogin(responseJson, body["email"]); saveCurrentLogin(responseJson, body["email"]);
await _incorrectDialog(context, true); _incorrectDialog(context, true);
return null; return null;
} }
} on TimeoutException catch (_) {
await _incorrectDialog(context, false);
} catch (error) { } catch (error) {
debugPrint(error.toString()); debugPrint(error.toString());
await _incorrectDialog(context, false); _incorrectDialog(context, false);
} }
return null;
} }

View file

@ -1,14 +1,14 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:local_spend/common/functions/get_token.dart'; import 'package:local_spend/common/functions/get_token.dart';
import 'package:local_spend/common/functions/save_logout.dart'; import 'package:local_spend/common/functions/save_logout.dart';
import 'package:local_spend/model/json/login_model.dart';
Future<bool> requestLogoutAPI() async { Future<LoginModel> requestLogoutAPI(BuildContext context) async {
saveLogout(); final url = "https://dev.peartrade.org/api/logout";
final url = "https://dev.localspend.co.uk/api/logout";
var token; var token;
@ -20,10 +20,20 @@ Future<bool> requestLogoutAPI() async {
"Token":token, "Token":token,
}; };
await http.post( final response = await http.post(
url, url,
body: json.encode(body), body: json.encode(body),
); );
return true; if (response.statusCode == 200) {
// debugPrint("Logout successful: " + response.body);
saveLogout();
return null;
} else {
// debugPrint("Logout unsuccessful: " + response.body);
saveLogout();
return null;
}
} }

View file

@ -23,7 +23,7 @@ class Receipt {
Future<LoginModel> submitReceiptAPI( Future<LoginModel> submitReceiptAPI(
BuildContext context, Receipt receipt) async { BuildContext context, Receipt receipt) async {
//var apiUrl = ConfigWrapper.of(context).apiKey; //var apiUrl = ConfigWrapper.of(context).apiKey;
final url = "https://dev.localspend.co.uk/api/upload"; final url = "https://dev.peartrade.org/api/upload";
SharedPreferences preferences = await SharedPreferences.getInstance(); SharedPreferences preferences = await SharedPreferences.getInstance();
@ -38,6 +38,7 @@ Future<LoginModel> submitReceiptAPI(
'street_name': receipt.street, 'street_name': receipt.street,
'postcode': receipt.postcode, 'postcode': receipt.postcode,
'town': receipt.town, 'town': receipt.town,
'session_key': preferences.get('LastToken'), 'session_key': preferences.get('LastToken'),
}; };
@ -60,11 +61,13 @@ Future<LoginModel> submitReceiptAPI(
context, context,
responseJson[0] == "" ? responseJson[0] : "Upload Successful", responseJson[0] == "" ? responseJson[0] : "Upload Successful",
"Transaction successfully submitted to server", "Transaction successfully submitted to server",
"OK"); "OK"
);
return LoginModel.fromJson(responseJson); return LoginModel.fromJson(responseJson);
} else { } else {
final responseJson = json.decode(response.body); final responseJson = json.decode(response.body);
showDialogSingleButton( showDialogSingleButton(
context, context,
"Unable to Submit Receipt", "Unable to Submit Receipt",

View file

@ -103,8 +103,7 @@ class AboutListTile extends StatelessWidget {
return ListTile( return ListTile(
leading: icon, leading: icon,
title: child ?? title: child ??
Text(MaterialLocalizations.of(context).aboutListTileTitle( Text(MaterialLocalizations.of(context).aboutListTileTitle(applicationName ?? _defaultApplicationName(context))),
applicationName ?? _defaultApplicationName(context))),
onTap: () { onTap: () {
showAboutDialog( showAboutDialog(
context: context, context: context,
@ -179,14 +178,13 @@ void showLicensePage({
String applicationLegalese, String applicationLegalese,
}) { }) {
assert(context != null); assert(context != null);
Navigator.push( Navigator.push(context, MaterialPageRoute<void>(
context,
MaterialPageRoute<void>(
builder: (BuildContext context) => LicensePage( builder: (BuildContext context) => LicensePage(
applicationName: applicationName, applicationName: applicationName,
applicationVersion: applicationVersion, applicationVersion: applicationVersion,
applicationLegalese: applicationLegalese, applicationLegalese: applicationLegalese,
))); )
));
} }
/// An about box. This is a dialog box with the application's icon, name, /// An about box. This is a dialog box with the application's icon, name,
@ -258,27 +256,19 @@ class AboutDialog extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMaterialLocalizations(context));
final String name = applicationName ?? _defaultApplicationName(context); final String name = applicationName ?? _defaultApplicationName(context);
final String version = final String version = applicationVersion ?? _defaultApplicationVersion(context);
applicationVersion ?? _defaultApplicationVersion(context);
final Widget icon = applicationIcon ?? _defaultApplicationIcon(context); final Widget icon = applicationIcon ?? _defaultApplicationIcon(context);
List<Widget> body = <Widget>[]; List<Widget> body = <Widget>[];
if (icon != null) { if (icon != null)
body.add( body.add(IconTheme(data: const IconThemeData(size: 45.0), child: icon));
IconTheme(data: const IconThemeData(size: 45.0), child: icon),
);
}
body.add(Expanded( body.add(Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0), padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: ListBody( child: ListBody(
children: <Widget>[ children: <Widget>[
Container( Text(name, style: Theme.of(context).textTheme.title),
child: Text(name, style: Theme.of(context).textTheme.title),
),
Text(version, style: Theme.of(context).textTheme.body1), Text(version, style: Theme.of(context).textTheme.body1),
Text(applicationLegalese ?? '', Text(applicationLegalese ?? '', style: Theme.of(context).textTheme.caption),
style: Theme.of(context).textTheme.caption),
], ],
), ),
), ),
@ -289,15 +279,15 @@ class AboutDialog extends StatelessWidget {
children: body, children: body,
), ),
]; ];
if (children != null) body.addAll(children); if (children != null)
body.addAll(children);
return AlertDialog( return AlertDialog(
content: SingleChildScrollView( content: SingleChildScrollView(
child: ListBody(children: body), child: ListBody(children: body),
), ),
actions: <Widget>[ actions: <Widget>[
FlatButton( FlatButton(
child: child: Text(MaterialLocalizations.of(context).viewLicensesButtonLabel),
Text(MaterialLocalizations.of(context).viewLicensesButtonLabel),
onPressed: () { onPressed: () {
showLicensePage( showLicensePage(
context: context, context: context,
@ -391,8 +381,7 @@ class _LicensePageState extends State<LicensePage> {
return; return;
} }
assert(() { assert(() {
dev.Timeline.timeSync('_initLicenses()', () {}, dev.Timeline.timeSync('_initLicenses()', () { }, flow: dev.Flow.step(debugFlowId));
flow: dev.Flow.step(debugFlowId));
return true; return true;
}()); }());
final List<LicenseParagraph> paragraphs = final List<LicenseParagraph> paragraphs =
@ -411,7 +400,8 @@ class _LicensePageState extends State<LicensePage> {
)); ));
_licenses.add(Container( _licenses.add(Container(
decoration: const BoxDecoration( decoration: const BoxDecoration(
border: Border(bottom: BorderSide(width: 0.0))), border: Border(bottom: BorderSide(width: 0.0))
),
child: Text( child: Text(
license.packages.join(', '), license.packages.join(', '),
style: const TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(fontWeight: FontWeight.bold),
@ -431,8 +421,7 @@ class _LicensePageState extends State<LicensePage> {
} else { } else {
assert(paragraph.indent >= 0); assert(paragraph.indent >= 0);
_licenses.add(Padding( _licenses.add(Padding(
padding: EdgeInsetsDirectional.only( padding: EdgeInsetsDirectional.only(top: 8.0, start: 16.0 * paragraph.indent),
top: 8.0, start: 16.0 * paragraph.indent),
child: Text(paragraph.text), child: Text(paragraph.text),
)); ));
} }
@ -443,8 +432,7 @@ class _LicensePageState extends State<LicensePage> {
_loaded = true; _loaded = true;
}); });
assert(() { assert(() {
dev.Timeline.timeSync('Build scheduled', () {}, dev.Timeline.timeSync('Build scheduled', () { }, flow: dev.Flow.end(debugFlowId));
flow: dev.Flow.end(debugFlowId));
return true; return true;
}()); }());
} }
@ -452,27 +440,16 @@ class _LicensePageState extends State<LicensePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMaterialLocalizations(context));
final String name = final String name = widget.applicationName ?? _defaultApplicationName(context);
widget.applicationName ?? _defaultApplicationName(context); final String version = widget.applicationVersion ?? _defaultApplicationVersion(context);
final String version = final MaterialLocalizations localizations = MaterialLocalizations.of(context);
widget.applicationVersion ?? _defaultApplicationVersion(context);
final MaterialLocalizations localizations =
MaterialLocalizations.of(context);
final List<Widget> contents = <Widget>[ final List<Widget> contents = <Widget>[
Text(name, Text(name, style: Theme.of(context).textTheme.headline, textAlign: TextAlign.center),
style: Theme.of(context).textTheme.headline, Text(version, style: Theme.of(context).textTheme.body1, textAlign: TextAlign.center),
textAlign: TextAlign.center),
Text(version,
style: Theme.of(context).textTheme.body1,
textAlign: TextAlign.center),
Container(height: 18.0), Container(height: 18.0),
Text(widget.applicationLegalese ?? '', Text(widget.applicationLegalese ?? '', style: Theme.of(context).textTheme.caption, textAlign: TextAlign.center),
style: Theme.of(context).textTheme.caption,
textAlign: TextAlign.center),
Container(height: 18.0), Container(height: 18.0),
Text('Powered by Flutter', Text('Powered by Flutter', style: Theme.of(context).textTheme.body1, textAlign: TextAlign.center),
style: Theme.of(context).textTheme.body1,
textAlign: TextAlign.center),
Container(height: 24.0), Container(height: 24.0),
]; ];
contents.addAll(_licenses); contents.addAll(_licenses);
@ -499,8 +476,7 @@ class _LicensePageState extends State<LicensePage> {
bottom: false, bottom: false,
child: Scrollbar( child: Scrollbar(
child: ListView( child: ListView(
padding: padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 12.0),
const EdgeInsets.symmetric(horizontal: 8.0, vertical: 12.0),
children: contents, children: contents,
), ),
), ),
@ -519,8 +495,7 @@ String _defaultApplicationName(BuildContext context) {
// can provide an explicit applicationName to the widgets defined in this // can provide an explicit applicationName to the widgets defined in this
// file, instead of relying on the default. // file, instead of relying on the default.
final Title ancestorTitle = context.ancestorWidgetOfExactType(Title); final Title ancestorTitle = context.ancestorWidgetOfExactType(Title);
return ancestorTitle?.title ?? return ancestorTitle?.title ?? Platform.resolvedExecutable.split(Platform.pathSeparator).last;
Platform.resolvedExecutable.split(Platform.pathSeparator).last;
} }
String _defaultApplicationVersion(BuildContext context) { String _defaultApplicationVersion(BuildContext context) {

View file

@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
final TextEditingController _feedbackController = TextEditingController(); final TextEditingController _feedbackController = TextEditingController();
void feedback(BuildContext context) { void feedback(
BuildContext context) {
// flutter defined function // flutter defined function
showDialog( showDialog(
context: context, context: context,
@ -14,10 +15,12 @@ void feedback(BuildContext context) {
children: <Widget>[ children: <Widget>[
Text("We would love to hear your thoughts."), Text("We would love to hear your thoughts."),
Text("\nThis section needs to be fixed."), Text("\nThis section needs to be fixed."),
Container( Container(
margin: const EdgeInsets.fromLTRB(0, 20, 0, 0), margin: const EdgeInsets.fromLTRB(0, 20, 0, 0),
width: 200, width: 200,
height: 70, height: 70,
child : new TextField( child : new TextField(
controller: _feedbackController, controller: _feedbackController,
decoration: InputDecoration( decoration: InputDecoration(

View file

@ -1,7 +1,6 @@
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'dart:async';
Future<String> getToken() async { getToken() async {
SharedPreferences preferences = await SharedPreferences.getInstance(); SharedPreferences preferences = await SharedPreferences.getInstance();
String getToken = preferences.getString("LastToken"); String getToken = preferences.getString("LastToken");

View file

@ -2,16 +2,16 @@ import 'package:flutter/material.dart';
import 'package:local_spend/common/apifunctions/request_logout_api.dart'; import 'package:local_spend/common/apifunctions/request_logout_api.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
void logout(context) { logout(context) {
_clearLoginDetails().then((_) { requestLogoutAPI(context);
requestLogoutAPI();
Navigator.of(context).pushReplacementNamed('/LoginPage'); Navigator.of(context).pushReplacementNamed('/LoginPage');
}); _clearLoginDetails();
} }
Future<void> _clearLoginDetails() async { _clearLoginDetails() async {
SharedPreferences preferences = await SharedPreferences.getInstance(); SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString('username', ""); preferences.setString('username', "");
await preferences.setString('password', ""); preferences.setString('password', "");
print("details cleared");
} }

View file

@ -1,7 +1,7 @@
import 'package:local_spend/model/json/login_model.dart'; import 'package:local_spend/model/json/login_model.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
void saveCurrentLogin(Map responseJson, loginEmail) async { saveCurrentLogin(Map responseJson, loginEmail) async {
SharedPreferences preferences = await SharedPreferences.getInstance(); SharedPreferences preferences = await SharedPreferences.getInstance();
var user; var user;
@ -13,17 +13,14 @@ void saveCurrentLogin(Map responseJson, loginEmail) async {
var token = (responseJson != null && responseJson.isNotEmpty) var token = (responseJson != null && responseJson.isNotEmpty)
? LoginModel.fromJson(responseJson).token ? LoginModel.fromJson(responseJson).token
: ""; : "";
var email = (loginEmail != null) ? loginEmail : ""; var email = (loginEmail != null)
var userType = (responseJson != null && responseJson.isNotEmpty) ? loginEmail
? LoginModel.fromJson(responseJson).userType
: ""; : "";
await preferences.setString( await preferences.setString(
'LastUser', (user != null && user.length > 0) ? user : ""); 'LastUser', (user != null && user.length > 0) ? user : "");
await preferences.setString( await preferences.setString(
'LastToken', (token != null && token.isNotEmpty) ? token : ""); 'LastToken', (token != null && token.length > 0) ? token : "");
await preferences.setString( await preferences.setString(
'LastEmail', (email != null && email.length > 0) ? email : ""); 'LastEmail', (email != null && email.length > 0) ? email : "");
await preferences.setString('LastUserType',
(userType != null && userType.isNotEmpty) ? userType : "");
} }

View file

@ -1,6 +1,6 @@
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
void saveLogout() async { saveLogout() async {
SharedPreferences preferences = await SharedPreferences.getInstance(); SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString('LastUser', ""); await preferences.setString('LastUser', "");

View file

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
void showDialogTwoButtons(BuildContext context, String title, String message, void showDialogTwoButtons(
String buttonLabel1, String buttonLabel2, Function action) { BuildContext context, String title, String message, String buttonLabel1, String buttonLabel2, Function action) {
// flutter defined function // flutter defined function
showDialog( showDialog(
context: context, context: context,

View file

@ -4,24 +4,6 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class PlatformScaffold extends StatelessWidget { class PlatformScaffold extends StatelessWidget {
PlatformScaffold(
{this.key,
this.appBar,
this.body,
this.floatingActionButton,
this.floatingActionButtonLocation,
this.floatingActionButtonAnimator,
this.persistentFooterButtons,
this.drawer,
this.endDrawer,
this.bottomNavigationBar,
this.backgroundColor,
this.resizeToAvoidBottomPadding = true,
this.primary = true})
: assert(primary != null),
super(key: key);
@override
final Key key; final Key key;
final PreferredSizeWidget appBar; final PreferredSizeWidget appBar;
final Widget body; final Widget body;
@ -36,6 +18,23 @@ class PlatformScaffold extends StatelessWidget {
final bool resizeToAvoidBottomPadding; final bool resizeToAvoidBottomPadding;
final bool primary; final bool primary;
PlatformScaffold(
{this.key,
this.appBar,
this.body,
this.floatingActionButton,
this.floatingActionButtonLocation,
this.floatingActionButtonAnimator,
this.persistentFooterButtons,
this.drawer,
this.endDrawer,
this.bottomNavigationBar,
this.backgroundColor,
this.resizeToAvoidBottomPadding: true,
this.primary: true})
: assert(primary != null),
super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Platform.isIOS return Platform.isIOS

View file

@ -1,40 +0,0 @@
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
class AnimatedBackground extends StatelessWidget {
AnimatedBackground(
this.animateColors,
this.lastColor,
this.begin,
this.end,
this.duration,
);
final List<Color> animateColors;
final Color lastColor;
final Alignment begin, end;
final int duration;
@override
Widget build(BuildContext context) {
final tween = MultiTrackTween([
Track("color1").add(Duration(seconds: this.duration),
ColorTween(begin: this.animateColors[0], end: this.animateColors[1])),
]);
return ControlledAnimation(
playback: Playback.MIRROR,
tween: tween,
duration: tween.duration,
builder: (context, animation) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: this.begin,
end: this.end,
colors: [animation["color1"], this.lastColor])),
);
},
);
}
}

View file

@ -4,6 +4,9 @@ import 'package:charts_flutter/flutter.dart' as charts;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class DonutAutoLabelChart extends StatelessWidget { class DonutAutoLabelChart extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
DonutAutoLabelChart(this.seriesList, {this.animate}); DonutAutoLabelChart(this.seriesList, {this.animate});
/// Creates a [PieChart] with sample data and no transition. /// Creates a [PieChart] with sample data and no transition.
@ -15,8 +18,6 @@ class DonutAutoLabelChart extends StatelessWidget {
); );
} }
final List<charts.Series> seriesList;
final bool animate;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -66,8 +67,8 @@ class DonutAutoLabelChart extends StatelessWidget {
/// Sample linear data type. /// Sample linear data type.
class LinearSales { class LinearSales {
LinearSales(this.key, this.sales);
final String key; final String key;
final int sales; final int sales;
LinearSales(this.key, this.sales);
} }

View file

@ -2,12 +2,13 @@ import 'package:flutter/material.dart';
import 'package:local_spend/common/widgets/charts/time_series_simple.dart'; import 'package:local_spend/common/widgets/charts/time_series_simple.dart';
class TimeSeries extends StatelessWidget { class TimeSeries extends StatelessWidget {
final String chartDataName;
TimeSeries({ TimeSeries({
this.chartDataName, this.chartDataName,
}); });
final String chartDataName;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new Container( return new Container(

View file

@ -3,6 +3,9 @@ import 'package:charts_flutter/flutter.dart' as charts;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class DonutPieChart extends StatelessWidget { class DonutPieChart extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
DonutPieChart(this.seriesList, {this.animate}); DonutPieChart(this.seriesList, {this.animate});
/// Creates a [PieChart] with sample data and no transition. /// Creates a [PieChart] with sample data and no transition.
@ -13,8 +16,6 @@ class DonutPieChart extends StatelessWidget {
); );
} }
final List<charts.Series> seriesList;
final bool animate;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -45,10 +46,11 @@ class DonutPieChart extends StatelessWidget {
} }
} }
/// Sample linear data type. /// Sample linear data type.
class LinearSales { class LinearSales {
LinearSales(this.year, this.sales);
final int year; final int year;
final int sales; final int sales;
LinearSales(this.year, this.sales);
} }

View file

@ -3,6 +3,9 @@ import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart' as charts; import 'package:charts_flutter/flutter.dart' as charts;
class GroupedBarChart extends StatelessWidget { class GroupedBarChart extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
GroupedBarChart(this.seriesList, {this.animate}); GroupedBarChart(this.seriesList, {this.animate});
factory GroupedBarChart.withSampleData() { factory GroupedBarChart.withSampleData() {
@ -13,8 +16,6 @@ class GroupedBarChart extends StatelessWidget {
); );
} }
final List<charts.Series> seriesList;
final bool animate;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -73,8 +74,8 @@ class GroupedBarChart extends StatelessWidget {
/// Sample ordinal data type. /// Sample ordinal data type.
class OrdinalSales { class OrdinalSales {
OrdinalSales(this.year, this.sales);
final String year; final String year;
final int sales; final int sales;
OrdinalSales(this.year, this.sales);
} }

View file

@ -4,6 +4,9 @@ import 'package:charts_flutter/flutter.dart' as charts;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class NumericComboLineBarChart extends StatelessWidget { class NumericComboLineBarChart extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
NumericComboLineBarChart(this.seriesList, {this.animate}); NumericComboLineBarChart(this.seriesList, {this.animate});
/// Creates a [LineChart] with sample data and no transition. /// Creates a [LineChart] with sample data and no transition.
@ -15,8 +18,6 @@ class NumericComboLineBarChart extends StatelessWidget {
); );
} }
final List<charts.Series> seriesList;
final bool animate;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -87,8 +88,8 @@ class NumericComboLineBarChart extends StatelessWidget {
/// Sample linear data type. /// Sample linear data type.
class LinearSales { class LinearSales {
LinearSales(this.year, this.sales);
final int year; final int year;
final int sales; final int sales;
LinearSales(this.year, this.sales);
} }

View file

@ -3,6 +3,9 @@ import 'package:charts_flutter/flutter.dart' as charts;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class PieOutsideLabelChart extends StatelessWidget { class PieOutsideLabelChart extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
PieOutsideLabelChart(this.seriesList, {this.animate}); PieOutsideLabelChart(this.seriesList, {this.animate});
/// Creates a [PieChart] with sample data and no transition. /// Creates a [PieChart] with sample data and no transition.
@ -14,8 +17,6 @@ class PieOutsideLabelChart extends StatelessWidget {
); );
} }
final List<charts.Series> seriesList;
final bool animate;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -61,8 +62,8 @@ class PieOutsideLabelChart extends StatelessWidget {
/// Sample linear data type. /// Sample linear data type.
class LinearSales { class LinearSales {
LinearSales(this.year, this.sales);
final int year; final int year;
final int sales; final int sales;
LinearSales(this.year, this.sales);
} }

View file

@ -8,6 +8,9 @@ import 'package:charts_flutter/flutter.dart' as charts;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class BucketingAxisScatterPlotChart extends StatelessWidget { class BucketingAxisScatterPlotChart extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
BucketingAxisScatterPlotChart(this.seriesList, {this.animate}); BucketingAxisScatterPlotChart(this.seriesList, {this.animate});
/// Creates a [ScatterPlotChart] with sample data and no transition. /// Creates a [ScatterPlotChart] with sample data and no transition.
@ -19,8 +22,6 @@ class BucketingAxisScatterPlotChart extends StatelessWidget {
); );
} }
final List<charts.Series> seriesList;
final bool animate;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -127,9 +128,9 @@ class BucketingAxisScatterPlotChart extends StatelessWidget {
/// Sample linear data type. /// Sample linear data type.
class LinearSales { class LinearSales {
LinearSales(this.year, this.revenueShare, this.radius);
final int year; final int year;
final double revenueShare; final double revenueShare;
final double radius; final double radius;
LinearSales(this.year, this.revenueShare, this.radius);
} }

View file

@ -11,6 +11,9 @@ import 'package:charts_flutter/flutter.dart' as charts;
/// ///
/// Also shows the option to provide a custom measure formatter. /// Also shows the option to provide a custom measure formatter.
class LegendWithMeasures extends StatelessWidget { class LegendWithMeasures extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
LegendWithMeasures(this.seriesList, {this.animate}); LegendWithMeasures(this.seriesList, {this.animate});
factory LegendWithMeasures.withSampleData() { factory LegendWithMeasures.withSampleData() {
@ -21,8 +24,6 @@ class LegendWithMeasures extends StatelessWidget {
); );
} }
final List<charts.Series> seriesList;
final bool animate;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -121,8 +122,8 @@ class LegendWithMeasures extends StatelessWidget {
/// Sample ordinal data type. /// Sample ordinal data type.
class OrdinalSales { class OrdinalSales {
OrdinalSales(this.year, this.sales);
final String year; final String year;
final int sales; final int sales;
OrdinalSales(this.year, this.sales);
} }

View file

@ -3,6 +3,9 @@ import 'package:charts_flutter/flutter.dart' as charts;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class SimpleTimeSeriesChart extends StatelessWidget { class SimpleTimeSeriesChart extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
SimpleTimeSeriesChart(this.seriesList, {this.animate}); SimpleTimeSeriesChart(this.seriesList, {this.animate});
/// Creates a [TimeSeriesChart] with sample data and no transition. /// Creates a [TimeSeriesChart] with sample data and no transition.
@ -14,8 +17,6 @@ class SimpleTimeSeriesChart extends StatelessWidget {
); );
} }
final List<charts.Series> seriesList;
final bool animate;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -52,8 +53,8 @@ class SimpleTimeSeriesChart extends StatelessWidget {
/// Sample time series data type. /// Sample time series data type.
class TimeSeriesSales { class TimeSeriesSales {
TimeSeriesSales(this.time, this.sales);
final DateTime time; final DateTime time;
final int sales; final int sales;
TimeSeriesSales(this.time, this.sales);
} }

View file

@ -62,6 +62,8 @@ class CustomCheckbox extends StatefulWidget {
assert(tristate || value != null), assert(tristate || value != null),
super(key: key); super(key: key);
final bool useTapTarget; final bool useTapTarget;
/// Whether this checkbox is checked. /// Whether this checkbox is checked.
@ -136,8 +138,7 @@ class CustomCheckbox extends StatefulWidget {
_CustomCheckboxState createState() => _CustomCheckboxState(); _CustomCheckboxState createState() => _CustomCheckboxState();
} }
class _CustomCheckboxState extends State<CustomCheckbox> class _CustomCheckboxState extends State<CustomCheckbox> with TickerProviderStateMixin {
with TickerProviderStateMixin {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context)); assert(debugCheckHasMaterial(context));
@ -146,26 +147,25 @@ class _CustomCheckboxState extends State<CustomCheckbox>
Size size; Size size;
switch (widget.materialTapTargetSize ?? themeData.materialTapTargetSize) { switch (widget.materialTapTargetSize ?? themeData.materialTapTargetSize) {
case MaterialTapTargetSize.padded: case MaterialTapTargetSize.padded:
size = const Size( size = const Size(2 * kRadialReactionRadius + 8.0, 2 * kRadialReactionRadius + 8.0);
2 * kRadialReactionRadius + 8.0, 2 * kRadialReactionRadius + 8.0);
break; break;
case MaterialTapTargetSize.shrinkWrap: case MaterialTapTargetSize.shrinkWrap:
size = const Size(2 * kRadialReactionRadius, 2 * kRadialReactionRadius); size = const Size(2 * kRadialReactionRadius, 2 * kRadialReactionRadius);
break; break;
} }
Size noTapTargetSize = Size(CustomCheckbox.width, CustomCheckbox.width); Size noTapTargetSize = Size(CustomCheckbox.width,
CustomCheckbox.width);
final BoxConstraints additionalConstraints = final BoxConstraints additionalConstraints =
BoxConstraints.tight(widget.useTapTarget ? size : noTapTargetSize); BoxConstraints.tight(widget
.useTapTarget? size : noTapTargetSize);
return _CheckboxRenderObjectWidget( return _CheckboxRenderObjectWidget(
value: widget.value, value: widget.value,
tristate: widget.tristate, tristate: widget.tristate,
activeColor: widget.activeColor ?? themeData.toggleableActiveColor, activeColor: widget.activeColor ?? themeData.toggleableActiveColor,
checkColor: widget.checkColor ?? const Color(0xFFFFFFFF), checkColor: widget.checkColor ?? const Color(0xFFFFFFFF),
inactiveColor: widget.onChanged != null inactiveColor: widget.onChanged != null ? themeData.unselectedWidgetColor : themeData.disabledColor,
? themeData.unselectedWidgetColor
: themeData.disabledColor,
onChanged: widget.onChanged, onChanged: widget.onChanged,
additionalConstraints: additionalConstraints, additionalConstraints: additionalConstraints,
vsync: this, vsync: this,
@ -174,8 +174,8 @@ class _CustomCheckboxState extends State<CustomCheckbox>
} }
class _CheckboxRenderObjectWidget extends LeafRenderObjectWidget { class _CheckboxRenderObjectWidget extends LeafRenderObjectWidget {
const _CheckboxRenderObjectWidget( const _CheckboxRenderObjectWidget({
{Key key, Key key,
@required this.value, @required this.value,
@required this.tristate, @required this.tristate,
@required this.activeColor, @required this.activeColor,
@ -184,8 +184,9 @@ class _CheckboxRenderObjectWidget extends LeafRenderObjectWidget {
@required this.onChanged, @required this.onChanged,
@required this.vsync, @required this.vsync,
@required this.additionalConstraints, @required this.additionalConstraints,
this.useTapTarget = true}) this.useTapTarget = true
: assert(tristate != null),
}) : assert(tristate != null),
assert(tristate || value != null), assert(tristate || value != null),
assert(activeColor != null), assert(activeColor != null),
assert(inactiveColor != null), assert(inactiveColor != null),
@ -258,7 +259,8 @@ class _RenderCheckbox extends RenderToggleable {
@override @override
set value(bool newValue) { set value(bool newValue) {
if (newValue == value) return; if (newValue == value)
return;
_oldValue = value; _oldValue = value;
super.value = newValue; super.value = newValue;
} }
@ -276,8 +278,7 @@ class _RenderCheckbox extends RenderToggleable {
RRect _outerRectAt(Offset origin, double t) { RRect _outerRectAt(Offset origin, double t) {
final double inset = 1.0 - (t - 0.5).abs() * 2.0; final double inset = 1.0 - (t - 0.5).abs() * 2.0;
final double size = _kEdgeSize - inset * _kStrokeWidth; final double size = _kEdgeSize - inset * _kStrokeWidth;
final Rect rect = final Rect rect = Rect.fromLTWH(origin.dx + inset, origin.dy + inset, size, size);
Rect.fromLTWH(origin.dx + inset, origin.dy + inset, size, size);
return RRect.fromRectAndRadius(rect, _kEdgeRadius); return RRect.fromRectAndRadius(rect, _kEdgeRadius);
} }
@ -287,9 +288,7 @@ class _RenderCheckbox extends RenderToggleable {
// As t goes from 0.0 to 0.25, animate from the inactiveColor to activeColor. // As t goes from 0.0 to 0.25, animate from the inactiveColor to activeColor.
return onChanged == null return onChanged == null
? inactiveColor ? inactiveColor
: (t >= 0.25 : (t >= 0.25 ? activeColor : Color.lerp(inactiveColor, activeColor, t * 4.0));
? activeColor
: Color.lerp(inactiveColor, activeColor, t * 4.0));
} }
// White stroke used to paint the check and dash. // White stroke used to paint the check and dash.
@ -304,8 +303,7 @@ class _RenderCheckbox extends RenderToggleable {
assert(t >= 0.0 && t <= 0.5); assert(t >= 0.0 && t <= 0.5);
final double size = outer.width; final double size = outer.width;
// As t goes from 0.0 to 1.0, gradually fill the outer RRect. // As t goes from 0.0 to 1.0, gradually fill the outer RRect.
final RRect inner = final RRect inner = outer.deflate(math.min(size / 2.0, _kStrokeWidth + size * t));
outer.deflate(math.min(size / 2.0, _kStrokeWidth + size * t));
canvas.drawDRRect(outer, inner, paint); canvas.drawDRRect(outer, inner, paint);
} }
@ -349,11 +347,9 @@ class _RenderCheckbox extends RenderToggleable {
final Canvas canvas = context.canvas; final Canvas canvas = context.canvas;
paintRadialReaction(canvas, offset, size.center(Offset.zero)); paintRadialReaction(canvas, offset, size.center(Offset.zero));
final Offset origin = final Offset origin = offset + (size / 2.0 - const Size.square(_kEdgeSize) / 2.0);
offset + (size / 2.0 - const Size.square(_kEdgeSize) / 2.0);
final AnimationStatus status = position.status; final AnimationStatus status = position.status;
final double tNormalized = final double tNormalized = status == AnimationStatus.forward || status == AnimationStatus.completed
status == AnimationStatus.forward || status == AnimationStatus.completed
? position.value ? position.value
: 1.0 - position.value; : 1.0 - position.value;
@ -370,14 +366,12 @@ class _RenderCheckbox extends RenderToggleable {
_initStrokePaint(paint); _initStrokePaint(paint);
final double tShrink = (t - 0.5) * 2.0; final double tShrink = (t - 0.5) * 2.0;
if (_oldValue == null || value == null) { if (_oldValue == null || value == null)
_drawDash(canvas, origin, tShrink, paint); _drawDash(canvas, origin, tShrink, paint);
} else { else
_drawCheck(canvas, origin, tShrink, paint); _drawCheck(canvas, origin, tShrink, paint);
} }
} } else { // Two cases: null to true, true to null
} else {
// Two cases: null to true, true to null
final RRect outer = _outerRectAt(origin, 1.0); final RRect outer = _outerRectAt(origin, 1.0);
final Paint paint = Paint() ..color = _colorAt(1.0); final Paint paint = Paint() ..color = _colorAt(1.0);
canvas.drawRRect(outer, paint); canvas.drawRRect(outer, paint);
@ -385,19 +379,17 @@ class _RenderCheckbox extends RenderToggleable {
_initStrokePaint(paint); _initStrokePaint(paint);
if (tNormalized <= 0.5) { if (tNormalized <= 0.5) {
final double tShrink = 1.0 - tNormalized * 2.0; final double tShrink = 1.0 - tNormalized * 2.0;
if (_oldValue == true) { if (_oldValue == true)
_drawCheck(canvas, origin, tShrink, paint); _drawCheck(canvas, origin, tShrink, paint);
} else { else
_drawDash(canvas, origin, tShrink, paint); _drawDash(canvas, origin, tShrink, paint);
}
} else { } else {
final double tExpand = (tNormalized - 0.5) * 2.0; final double tExpand = (tNormalized - 0.5) * 2.0;
if (value == true) { if (value == true)
_drawCheck(canvas, origin, tExpand, paint); _drawCheck(canvas, origin, tExpand, paint);
} else { else
_drawDash(canvas, origin, tExpand, paint); _drawDash(canvas, origin, tExpand, paint);
} }
} }
} }
} }
}

View file

@ -28,8 +28,11 @@ class LabeledCheckboxWithIcon extends StatelessWidget {
onTap: () { onTap: () {
onChanged(!value); onChanged(!value);
}, },
child: Padding( child: Padding(
padding: padding, padding: padding,
child: Row( child: Row(
// crossAxisAlignment: CrossAxisAlignment.center, //doesn't do anything // crossAxisAlignment: CrossAxisAlignment.center, //doesn't do anything
@ -37,18 +40,16 @@ class LabeledCheckboxWithIcon extends StatelessWidget {
Container( Container(
padding: EdgeInsets.all(0), padding: EdgeInsets.all(0),
width: iconSize, width: iconSize,
child : Icon( child : Icon(
icon, icon,
// size: iconSize, // size: iconSize,
color: iconColor, color: iconColor,
), ),
), ),
Expanded(
child: Text( Expanded(child: Text(label, style: textStyle, textAlign: TextAlign.center,)),
label,
style: textStyle,
textAlign: TextAlign.center,
)),
CustomCheckbox( CustomCheckbox(
//custom checkbox removes padding so the form looks nice //custom checkbox removes padding so the form looks nice
@ -88,9 +89,11 @@ class LabeledCheckbox extends StatelessWidget {
}, },
child: Padding( child: Padding(
padding: padding, padding: padding,
child: Row( child: Row(
children: <Widget>[ children: <Widget>[
Expanded(child: Text(label, style: textStyle)), Expanded(child: Text(label, style: textStyle)),
CustomCheckbox( CustomCheckbox(
//custom checkbox removes padding so the form looks nice //custom checkbox removes padding so the form looks nice
@ -107,6 +110,7 @@ class LabeledCheckbox extends StatelessWidget {
} }
} }
/* /*
//USAGE: //USAGE:

View file

@ -1,9 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'dart:async'; import 'dart:async';
import 'package:flutter/services.dart';
import 'package:local_spend/common/apifunctions/find_organisations.dart'; import 'package:local_spend/common/apifunctions/find_organisations.dart';
class FindOrganisations { class FindOrganisations {
TextField getSearchBar(TextEditingController controller, String hintText) { TextField getSearchBar(TextEditingController controller, String hintText) {
return TextField( return TextField(
controller: controller, controller: controller,
@ -19,12 +20,12 @@ class FindOrganisations {
Future<dynamic> _moreInfoDialog(context, Organisation organisation) { Future<dynamic> _moreInfoDialog(context, Organisation organisation) {
TextStyle informationTitleStyle = new TextStyle(fontSize: 16); TextStyle informationTitleStyle = new TextStyle(fontSize: 16);
TextStyle informationStyle = TextStyle informationStyle = new TextStyle(fontSize: 16, fontWeight: FontWeight.bold);
new TextStyle(fontSize: 16, fontWeight: FontWeight.bold);
return showDialog<Organisation>( return showDialog<Organisation>(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (BuildContext context) { builder: (BuildContext context) {
return StatefulBuilder( return StatefulBuilder(
builder: (context, setState) { builder: (context, setState) {
@ -38,10 +39,12 @@ class FindOrganisations {
fontSize: 21, fontWeight: FontWeight.bold), fontSize: 21, fontWeight: FontWeight.bold),
), ),
), ),
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 10), padding: EdgeInsets.symmetric(horizontal: 10),
child: Divider(), child: Divider(),
), ),
Container( Container(
width: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width,
padding: EdgeInsets.symmetric(horizontal: 10), padding: EdgeInsets.symmetric(horizontal: 10),
@ -51,15 +54,13 @@ class FindOrganisations {
TableRow( TableRow(
children: [ children: [
Text("Street:", style: informationTitleStyle), Text("Street:", style: informationTitleStyle),
Text(organisation.streetName, Text(organisation.streetName, style: informationStyle),
style: informationStyle),
], ],
), ),
TableRow( TableRow(
children: [ children: [
Text("Postcode:", style: informationTitleStyle), Text("Postcode:", style: informationTitleStyle),
Text(organisation.postcode.toUpperCase(), Text(organisation.postcode.toUpperCase(), style: informationStyle),
style: informationStyle),
], ],
), ),
TableRow( TableRow(
@ -71,6 +72,7 @@ class FindOrganisations {
], ],
), ),
), ),
], ],
); );
}, },
@ -81,10 +83,10 @@ class FindOrganisations {
Future<Organisation> dialog(context) { Future<Organisation> dialog(context) {
bool _searchEnabled = false; bool _searchEnabled = false;
bool _orgsFetched = false;
TextEditingController searchBarText = new TextEditingController(); TextEditingController searchBarText = new TextEditingController();
var organisations = new Organisations(); var organisations = new Organisations();
var listTitle = "All Organisations"; var listTitle = "All Organisations";
// var organisationsList = organisations.getTestData();
var organisationsList = List<Organisation>(); var organisationsList = List<Organisation>();
Future<int> _submitSearch(String search) async { Future<int> _submitSearch(String search) async {
@ -92,86 +94,78 @@ class FindOrganisations {
listTitle = "Results for \'" + search + "\'"; listTitle = "Results for \'" + search + "\'";
var futureOrgs = await organisations.findOrganisations(search); var futureOrgs = await organisations.findOrganisations(search);
// futureOrgs.then((value) {
// debugPrint("There are " + futureOrgs.length.toString() +
// " payees matching the query \'" + search + "\'.");
organisationsList = futureOrgs; organisationsList = futureOrgs;
_searchEnabled = true; _searchEnabled = true;
return futureOrgs.length; return futureOrgs.length;
// });
} }
return showDialog<Organisation>( return showDialog<Organisation>(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (BuildContext context) { builder: (BuildContext context) {
return StatefulBuilder( return StatefulBuilder(
builder: (context, setState) { builder: (context, setState) {
return SimpleDialog( return SimpleDialog(
children: <Widget>[ children: <Widget>[
Column( Padding(
children: [ padding: EdgeInsets.fromLTRB(20, 0, 0, 0),
Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Container( Container(
padding: EdgeInsets.fromLTRB(20, 0, 0, 0), width: 140,
width: 150,
height: 50, height: 50,
child: TextField( child: TextField(
autofocus: true,
controller: searchBarText, controller: searchBarText,
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Payee Name", hintText: "Payee Name",
), ),
onChanged: (value) { onChanged: (value) {
if (value.isNotEmpty) { if (value.length > 0) {
_searchEnabled = true; _searchEnabled = true;
} else { } else {
_searchEnabled = false; _searchEnabled = false;
} }
setState(() => {_searchEnabled}); setState(() => {_searchEnabled});
}, },
onSubmitted: _searchEnabled onSubmitted: (_) {
? ((_) { if (_searchEnabled) {
SystemChannels.textInput var result = _submitSearch(searchBarText.text);
.invokeMethod('TextInput.hide');
var result =
_submitSearch(searchBarText.text);
result.then((_) { result.then((_) {
setState(() { setState(() {});
_orgsFetched = true;
}); });
}); }
}) },
: null,
), ),
), ),
Container( Container(
width: 80, width: 80,
padding: EdgeInsets.fromLTRB(20, 0, 0, 0), padding: EdgeInsets.fromLTRB(20, 0, 0, 0),
child: RaisedButton( child: RaisedButton(
onPressed: _searchEnabled onPressed: (() {
? (() { if (_searchEnabled) {
SystemChannels.textInput var result = _submitSearch(searchBarText.text);
.invokeMethod('TextInput.hide');
var result =
_submitSearch(searchBarText.text);
result.then((_) { result.then((_) {
setState(() { setState(() {});
_orgsFetched = true;
}); });
}); }
}) }),
: null,
child: Icon(Icons.search, color: Colors.white), child: Icon(Icons.search, color: Colors.white),
color: Colors.blue, color: _searchEnabled ? Colors.blue : Colors.blue[200],
// make inactive when search in progress as activity indicator
), ),
), ),
], ],
), ),
],
), ),
Column(
children: _orgsFetched
? [
Container( Container(
padding: EdgeInsets.fromLTRB(20, 20, 20, 0), padding: EdgeInsets.fromLTRB(20, 20, 20, 0),
child: Text( child: Text(
@ -180,10 +174,18 @@ class FindOrganisations {
fontSize: 23, fontWeight: FontWeight.bold), fontSize: 23, fontWeight: FontWeight.bold),
), ),
), ),
Container( Container(
padding: EdgeInsets.fromLTRB(10, 10, 10, 0), padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
width: MediaQuery.of(context).size.width, width: MediaQuery
height: MediaQuery.of(context).size.height * 0.67, .of(context)
.size
.width,
height: MediaQuery
.of(context)
.size
.height * 0.67,
child: Material( child: Material(
shadowColor: Colors.transparent, shadowColor: Colors.transparent,
color: Colors.transparent, color: Colors.transparent,
@ -193,21 +195,16 @@ class FindOrganisations {
return Card( return Card(
child: ListTile( child: ListTile(
leading: Icon(Icons.person), leading: Icon(Icons.person),
title: Text(organisationsList[index].name, title: Text(organisationsList[index].name, style: new TextStyle(fontSize: 18)),
style: new TextStyle(fontSize: 18)), subtitle: Text(organisationsList[index].postcode.toUpperCase()),
subtitle: Text(organisationsList[index]
.postcode
.toUpperCase()),
// trailing: Icon(Icons.arrow_forward_ios), // trailing: Icon(Icons.arrow_forward_ios),
// onTap: _chosenOrg(organisationsList[index]), // onTap: _chosenOrg(organisationsList[index]),
onTap: (){ onTap: (){
Navigator.of(context) Navigator.of(context).pop(organisationsList[index]);
.pop(organisationsList[index]);
}, },
onLongPress: (){ onLongPress: (){
// show more details about the organisation in a new dialog // show more details about the organisation in a new dialog
var moreInfo = _moreInfoDialog( var moreInfo = _moreInfoDialog(context, organisationsList[index]);
context, organisationsList[index]);
moreInfo.whenComplete(null); moreInfo.whenComplete(null);
}, },
), ),
@ -216,22 +213,12 @@ class FindOrganisations {
), ),
), ),
), ),
Center(
child: Container(
padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Text("Long press a payee for more info",
style:
TextStyle(fontStyle: FontStyle.italic)),
),
),
]
: [Container()],
),
// help button for if org not listed // help button for if org not listed
// cancel and ok buttons // cancel and ok buttons
], ],
// ),
); );
}, },
); );

View file

@ -6,6 +6,7 @@ class PopupListView {
return showDialog<dynamic>( return showDialog<dynamic>(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (BuildContext context) { builder: (BuildContext context) {
return SimpleDialog( return SimpleDialog(
title: Text(title), title: Text(title),
@ -15,8 +16,7 @@ class PopupListView {
); );
} }
List<Widget> getDialogOptions( List<Widget> getDialogOptions(context, List<String> options /*, Function onPressed*/) {
context, List<String> options /*, Function onPressed*/) {
var dialogOptionsList = new List<SimpleDialogOption>(); var dialogOptionsList = new List<SimpleDialogOption>();
for (var i = 0; i < options.length; i++) { for (var i = 0; i < options.length; i++) {

View file

@ -5,13 +5,13 @@ part 'config.g.dart';
@JsonSerializable(createToJson: false) @JsonSerializable(createToJson: false)
class Config { class Config {
Config({this.env, this.production, this.apiKey});
factory Config.fromJson(Map<String, dynamic> json) => _$ConfigFromJson(json);
final String env; final String env;
final bool production; final bool production;
final String apiKey; final String apiKey;
Config({this.env, this.production, this.apiKey});
factory Config.fromJson(Map<String, dynamic> json) => _$ConfigFromJson(json);
} }
class ConfigWrapper extends StatelessWidget { class ConfigWrapper extends StatelessWidget {

2
lib/env/dev.g.dart vendored
View file

@ -9,5 +9,5 @@ part of 'dev.dart';
const _$configJsonLiteral = { const _$configJsonLiteral = {
'env': 'DEV', 'env': 'DEV',
'production': false, 'production': false,
'apiUrl': 'https://dev.localspend.co.uk/api' 'apiUrl': 'https://dev.peartrade.org/api'
}; };

2
lib/env/dev.json vendored
View file

@ -1,5 +1,5 @@
{ {
"env": "DEV", "env": "DEV",
"production": false, "production": false,
"apiUrl": "https://dev.localspend.co.uk/api" "apiUrl": "https://dev.peartrade.org/api"
} }

2
lib/env/prod.g.dart vendored
View file

@ -9,5 +9,5 @@ part of 'prod.dart';
const _$configJsonLiteral = { const _$configJsonLiteral = {
'env': 'PROD', 'env': 'PROD',
'production': true, 'production': true,
'apiUrl': 'https://www.localspend.co.uk/api' 'apiUrl': 'https://www.peartrade.org/api'
}; };

2
lib/env/prod.json vendored
View file

@ -1,5 +1,5 @@
{ {
"env": "PROD", "env": "PROD",
"production": true, "production": true,
"apiUrl": "https://www.localspend.co.uk/api" "apiUrl": "https://www.peartrade.org/api"
} }

View file

@ -1,38 +1,34 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:local_spend/pages/home_page.dart'; import 'package:local_spend/pages/home_page.dart';
import 'package:local_spend/pages/login_page.dart'; import 'package:local_spend/pages/login_page.dart';
import 'package:local_spend/pages/map_page.dart';
import 'package:local_spend/pages/receipt_page_2.dart'; import 'package:local_spend/pages/receipt_page_2.dart';
import 'package:local_spend/pages/spash_screen.dart'; import 'package:local_spend/pages/spash_screen.dart';
import 'package:local_spend/pages/more_page.dart'; import 'package:local_spend/pages/more_page.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:local_spend/common/apifunctions/get_graph_data.dart'; import 'package:local_spend/common/apifunctions/get_graph_data.dart';
void main() { void main() {
runApp(MyApp()); runApp(MyApp());
} }
void loadGraphs() {}
class GraphWithTitle {
GraphWithTitle({this.graph, this.title});
GraphData graph;
String title;
}
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// TODO: load graphs on app login and send to graph widgets // GraphData gd = new GraphData();
// gd.getGraphData('total_last_week');
//var config = ConfigWrapper.of(context);
return new MaterialApp( return new MaterialApp(
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
localizationsDelegates: [ localizationsDelegates: [
GlobalMaterialLocalizations.delegate, GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate, GlobalWidgetsLocalizations.delegate,
], ],
supportedLocales: [Locale("en")], supportedLocales: [
Locale("en")
],
title: "Local Spend Tracker", title: "Local Spend Tracker",
theme: new ThemeData( theme: new ThemeData(
primarySwatch: Colors.blueGrey, primarySwatch: Colors.blueGrey,
@ -40,7 +36,6 @@ class MyApp extends StatelessWidget {
routes: <String, WidgetBuilder>{ routes: <String, WidgetBuilder>{
"/HomePage": (BuildContext context) => HomePage(), "/HomePage": (BuildContext context) => HomePage(),
"/LoginPage": (BuildContext context) => LoginPage(), "/LoginPage": (BuildContext context) => LoginPage(),
'/MapPage': (BuildContext context) => MapPage(),
"/ReceiptPage": (BuildContext context) => ReceiptPage2(), "/ReceiptPage": (BuildContext context) => ReceiptPage2(),
"/MorePage": (BuildContext context) => MorePage(), "/MorePage": (BuildContext context) => MorePage(),
}, },

View file

@ -1,18 +1,15 @@
class LoginModel { class LoginModel {
LoginModel(this.userName, this.token, this.userType); final String userName;
final String token;
LoginModel(this.userName, this.token);
LoginModel.fromJson(Map<String, dynamic> json) LoginModel.fromJson(Map<String, dynamic> json)
: userName = json['display_name'], : userName = json['display_name'],
userType = json['user_type'],
token = json['session_key']; token = json['session_key'];
final String userName;
final String token;
final String userType;
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
'name': userName, 'name': userName,
'user_type': userType,
'token': token, 'token': token,
}; };
} }

View file

@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart' as charts;
import 'package:local_spend/common/apifunctions/get_graph_data.dart';
class PresetChart extends StatefulWidget {
PresetChart({Key key}) : super(key: key);
@override
_PresetChartState createState() {
return _PresetChartState();
}
}
class _PresetChartState extends State<PresetChart> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return new Container(
);
}
}

View file

@ -1,163 +0,0 @@
import 'package:flutter/material.dart';
import 'package:local_spend/common/apifunctions/get_graph_data.dart';
import 'package:charts_flutter/flutter.dart' as charts;
class CustomerGraphs extends StatefulWidget {
CustomerGraphs({Key key}) : super(key: key);
@override
_CustomerGraphsState createState() {
return _CustomerGraphsState();
}
}
class _CustomerGraphsState extends State<CustomerGraphs> {
GraphData totalLastWeekGraph = new GraphData("total_last_week");
GraphData avgSpendLastWeekGraph = new GraphData("avg_spend_last_week");
GraphData totalLastMonthGraph = new GraphData("total_last_month");
GraphData avgSpendLastMonth = new GraphData("avg_spend_last_month");
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
// Initializing graphs:
if (!totalLastWeekGraph.loaded) {
totalLastWeekGraph.setGraphData().then((_) {
setState(() {});
});
}
if (!avgSpendLastWeekGraph.loaded) {
avgSpendLastWeekGraph.setGraphData().then((_) {
setState(() {});
});
}
if (!totalLastMonthGraph.loaded) {
totalLastMonthGraph.setGraphData().then((_) {
setState(() {});
});
}
if (!avgSpendLastMonth.loaded) {
avgSpendLastMonth.setGraphData().then((_) {
setState(() {});
});
}
return ListView(
children: <Widget>[
Container(
padding: EdgeInsets.fromLTRB(0.0, 17, 0.0, 0.0),
child: Text(
"Last Week's Total Spend",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
Tooltip(
message: "Graph of total spend last week",
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
height: 200,
child: totalLastWeekGraph.graph != null
? new charts.TimeSeriesChart(totalLastWeekGraph.graph)
: Center(
child: CircularProgressIndicator(
valueColor:
new AlwaysStoppedAnimation<Color>(Colors.orange),
)), //List<Series<dynamic, DateTime>>es<dynamic, DateTime>>
),
),
Container(
padding: EdgeInsets.fromLTRB(0.0, 17, 0.0, 0.0),
child: Text(
"Last Week's Average Spend",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
Tooltip(
message: "Graph of average spend last week",
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
height: 200,
child: avgSpendLastWeekGraph.graph != null
? new charts.TimeSeriesChart(avgSpendLastWeekGraph.graph)
: Center(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(Colors.blue),
)), //List<Series<dynamic, DateTime>>es<dynamic, DateTime>>
),
),
Container(
padding: EdgeInsets.fromLTRB(0.0, 17, 0.0, 0.0),
child: Text(
"Last Month's Total Spend",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
Tooltip(
message: "Graph of total spend last month",
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
height: 200,
child: totalLastMonthGraph.graph != null
? new charts.TimeSeriesChart(totalLastMonthGraph.graph)
: Center(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(Colors.green),
)), //List<Series<dynamic, DateTime>>es<dynamic, DateTime>>
),
),
Container(
padding: EdgeInsets.fromLTRB(0.0, 17, 0.0, 0.0),
child: Text(
"Last Month's Average Spend",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
Tooltip(
message: "Graph of average spend last month",
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
height: 200,
child: avgSpendLastMonth.graph != null
? new charts.TimeSeriesChart(avgSpendLastMonth.graph)
: Center(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(Colors.red),
)), //List<Series<dynamic, DateTime>>es<dynamic, DateTime>>
),
),
],
);
}
}

View file

@ -2,10 +2,9 @@ import 'package:flutter/material.dart';
import 'package:local_spend/pages/receipt_page_2.dart'; import 'package:local_spend/pages/receipt_page_2.dart';
import 'package:local_spend/pages/more_page.dart'; import 'package:local_spend/pages/more_page.dart';
import 'package:local_spend/pages/stats_page.dart'; import 'package:local_spend/pages/stats_page.dart';
import 'package:local_spend/pages/map_page.dart';
class HomePage extends StatelessWidget { class HomePage extends StatelessWidget {
static String _title = 'SpendTracker'; static String _title = 'Text here';
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -25,13 +24,11 @@ class HomePageWidget extends StatefulWidget {
class _HomePageState extends State<HomePageWidget> { class _HomePageState extends State<HomePageWidget> {
int _selectedIndex = 0; int _selectedIndex = 0;
static const TextStyle optionStyle = static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold); TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static List<Widget> _widgetOptions = <Widget>[ static List<Widget> _widgetOptions = <Widget>[
ReceiptPage2(), ReceiptPage2(),
StatsPage(), StatsPage(),
MapPage(),
MorePage() MorePage()
]; ];
@ -57,17 +54,12 @@ class _HomePageState extends State<HomePageWidget> {
icon: Icon(Icons.show_chart), icon: Icon(Icons.show_chart),
title: Text('Statistics'), title: Text('Statistics'),
), ),
BottomNavigationBarItem(
icon: Icon(Icons.map),
title: Text('Locations'),
),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: Icon(Icons.more_horiz), icon: Icon(Icons.more_horiz),
title: Text('More'), title: Text('More'),
), ),
], ],
currentIndex: _selectedIndex, currentIndex: _selectedIndex,
unselectedItemColor: Colors.grey[400],
selectedItemColor: Colors.blue[400], selectedItemColor: Colors.blue[400],
onTap: _onItemTapped, onTap: _onItemTapped,
), ),

View file

@ -7,9 +7,8 @@ import 'package:local_spend/common/platform/platform_scaffold.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:local_spend/common/widgets/labeled_checkbox.dart'; import 'package:local_spend/common/widgets/labeled_checkbox.dart';
import 'package:local_spend/common/widgets/animatedGradientButton.dart';
const url = "https://flutter.io/"; const URL = "https://flutter.io/";
class LoginPage extends StatefulWidget { class LoginPage extends StatefulWidget {
@override @override
@ -19,10 +18,10 @@ class LoginPage extends StatefulWidget {
} }
class LoginPageState extends State<LoginPage> { class LoginPageState extends State<LoginPage> {
bool _isLoggingIn = false; final TextEditingController _emailController = TextEditingController(/*text: 'test@example.com'*/); // remove
final TextEditingController _emailController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(/*text: 'abc123'*/); // remove
final TextEditingController _passwordController = TextEditingController(); bool _saveLoginDetails = true; // I am extremely sorry for the placement of this variable
bool _saveLoginDetails = true; // it will be fixed soon I promise
FocusNode focusNode; // added so focus can move automatically FocusNode focusNode; // added so focus can move automatically
@ -33,7 +32,7 @@ class LoginPageState extends State<LoginPage> {
showDialogSingleButton( showDialogSingleButton(
context, context,
"Unable to reach your website.", "Unable to reach your website.",
"Currently unable to reach the website $url. Please try again at a later time.", "Currently unable to reach the website $URL. Please try again at a later time.",
"OK"); "OK");
} }
} }
@ -54,7 +53,7 @@ class LoginPageState extends State<LoginPage> {
super.dispose(); super.dispose();
} }
void _fillLoginDetails() async { _fillLoginDetails() async {
SharedPreferences preferences = await SharedPreferences.getInstance(); SharedPreferences preferences = await SharedPreferences.getInstance();
var username = await preferences.get('username'); var username = await preferences.get('username');
@ -64,27 +63,27 @@ class LoginPageState extends State<LoginPage> {
_passwordController.text = await password; _passwordController.text = await password;
} }
void _saveCurrentRoute(String lastRoute) async { _saveCurrentRoute(String lastRoute) async {
SharedPreferences preferences = await SharedPreferences.getInstance(); SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString('LastPageRoute', lastRoute); await preferences.setString('LastPageRoute', lastRoute);
} }
void login(String username, String password) async { login(String username, String password) async {
_isLoggingIn = true; SystemChannels.textInput.invokeMethod('TextInput.hide');
await SystemChannels.textInput.invokeMethod('TextInput.hide');
SharedPreferences preferences = await SharedPreferences.getInstance(); SharedPreferences preferences = await SharedPreferences.getInstance();
if (_saveLoginDetails) { if (_saveLoginDetails) {
await preferences.setString('username', username); await preferences.setString('username', username);
await preferences.setString('password', password); await preferences.setString('password', password);
print("details saved");
} else { } else {
await preferences.setString('username', ""); await preferences.setString('username', "");
await preferences.setString('password', ""); await preferences.setString('password', "");
print("details cleared");
} }
await requestLoginAPI(context, username, password).then((value) { requestLoginAPI(context, username,
_isLoggingIn = false; password);
});
} }
@override @override
@ -97,25 +96,37 @@ class LoginPageState extends State<LoginPage> {
} else { } else {
Navigator.of(context).pushReplacementNamed('/HomePage'); Navigator.of(context).pushReplacementNamed('/HomePage');
} }
return null;
}, },
child: PlatformScaffold( child: PlatformScaffold(
body: Stack( // drawer: BasicDrawer(),
children: [ // body: Container(
AnimatedBackground([Colors.lightBlue[50], Colors.lightBlue[50]], // decoration: BoxDecoration(color: Colors.white),
Colors.white, Alignment.topRight, Alignment.bottomLeft, 3), // margin: const EdgeInsets.all(20),
Container( // child: Padding(
// padding: EdgeInsets.fromLTRB(30.0, 170.0, 30.0, 0.0),
// child: ListView(
// children: <Widget>[
body: Container(
decoration: new BoxDecoration(
gradient: new LinearGradient(
colors: [Colors.blue[50], Colors.white],
stops: [0,1],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: Container(
margin: EdgeInsets.fromLTRB(60,30,60,0), margin: EdgeInsets.fromLTRB(60,30,60,0),
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: AnimatedContainer( child: Container(
duration: Duration(seconds: 2),
margin: EdgeInsets.fromLTRB(15,0,15,0), margin: EdgeInsets.fromLTRB(15,0,15,0),
// alignment: FractionalOffset(0.5, 0.3), // not sure what this does ngl :/
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: image: AssetImage('assets/images/launch_image.png')
AssetImage('assets/images/launch_image.png')), ),
), ),
), ),
), ),
@ -157,68 +168,87 @@ class LoginPageState extends State<LoginPage> {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
onSubmitted: (_) { onSubmitted: (_) {
login(_emailController.text, _passwordController.text); login( _emailController.text,
_passwordController.text);
}, },
), ),
), ),
Container(
margin: EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 30.0), Padding(
padding: EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 30.0),
child : Material(
child: new Container(
decoration: new BoxDecoration(
border: new Border.all(color : Colors.transparent, width: 2),
borderRadius: BorderRadius.all(Radius.circular(2)),
gradient: new LinearGradient(
colors: [
Colors.blue[300],
Colors.blue[500],
],
stops: [0,1],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child : Material(
type: MaterialType.transparency,
child : InkWell(
onTap: () => login( _emailController.text, _passwordController.text),
child: new Container(
width: 100, width: 100,
height: 50, height: 50,
child: Opacity(
opacity: _isLoggingIn ? 0.5 : 1,
child: ClipRRect(
borderRadius: BorderRadius.circular(2),
child: Stack(
children: [
AnimatedBackground(
[Colors.blue, Colors.lightBlue[300]],
Colors.lightBlue,
Alignment.bottomRight,
Alignment.topLeft,
3),
Material(
type: MaterialType.transparency,
child: InkWell(
onTap: _isLoggingIn
? null
: () => login(_emailController.text,
_passwordController.text),
child: new Center( child: new Center(
child: new Text( child: new Text(
'GO', 'GO', style: new TextStyle(fontSize: 18, color: Colors.white),),
style: new TextStyle(
fontSize: 18, color: Colors.white),
), ),
), ),
), ),
), ),
],
),
), ),
), ),
), ),
Padding( Padding(
padding: EdgeInsets.fromLTRB(0, 10, 0, 50), padding: EdgeInsets.fromLTRB(0, 10, 0, 50),
child: LabeledCheckbox( child: LabeledCheckbox(
label : "SAVE LOGIN", label : "SAVE LOGIN",
textStyle: TextStyle( textStyle: TextStyle(fontSize: 18, color: Colors.black54, fontWeight: FontWeight.bold),
fontSize: 18,
color: Colors.black54,
fontWeight: FontWeight.bold),
padding: const EdgeInsets.fromLTRB(0,0,0,0), padding: const EdgeInsets.fromLTRB(0,0,0,0),
value : _saveLoginDetails, value : _saveLoginDetails,
onChanged: (bool newValue) { onChanged: (bool newValue) {
setState(() { setState(() {
_saveLoginDetails = newValue; _saveLoginDetails = newValue;
}); });
}, },
), ),
/*child: LabeledCheckboxWithIcon(
label : "SAVE LOGIN",
textStyle: TextStyle(fontSize: 18, color: Colors.black54, fontWeight: FontWeight.bold),
icon: Icons.account_box, // need to remove icon padding!!
iconSize: 18,
iconColor: Colors.black54,
padding: const EdgeInsets.fromLTRB(0,0,0,0),
value : _saveLoginDetails,
onChanged: (bool newValue) {
setState(() {
_saveLoginDetails = newValue;
});
},
),*/
), ),
], ],
), ),
), ),
],
), ),
), ),
); );

View file

@ -1,62 +0,0 @@
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:google_maps_flutter/google_maps_flutter.dart' as gmaps;
import 'package:local_spend/common/apifunctions/get_map_data.dart' as mapData;
import 'package:local_spend/common/platform/platform_scaffold.dart';
class MapPage extends StatefulWidget {
MapPage({Key key}) : super(key: key);
@override
_MapPageState createState() {
return _MapPageState();
}
}
class _MapPageState extends State<MapPage> {
final Map<String, gmaps.Marker> _markers = {};
Future<void> _onMapCreated(gmaps.GoogleMapController controller) async {
final region = await controller.getVisibleRegion();
final locations = await mapData.getLocations(region.northeast, region.southwest);
setState(() {
_markers.clear();
for (final location in locations.locations) {
final marker = gmaps.Marker(
markerId: gmaps.MarkerId(location.organisation.name),
position: gmaps.LatLng(location.lat, location.lng),
infoWindow: gmaps.InfoWindow(
title: location.organisation.name,
snippet: location.organisation.postcode,
),
);
_markers[location.organisation.name] = marker;
}
});
}
@override
Widget build(BuildContext context) {
return PlatformScaffold(
appBar: AppBar(
backgroundColor: Colors.blue[400],
title: Text(
"Map",
style: TextStyle(
fontSize: 20,
color: Colors.white,
),
),
centerTitle: true,
iconTheme: IconThemeData(color: Colors.black),
),
body: gmaps.GoogleMap(
myLocationButtonEnabled: false,
mapType: gmaps.MapType.hybrid,
onMapCreated: _onMapCreated,
initialCameraPosition: gmaps.CameraPosition(
target: gmaps.LatLng(54.0411301, -2.8104042),
zoom: 15,
),
),
);
}
}

View file

@ -1,3 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:local_spend/common/platform/platform_scaffold.dart'; import 'package:local_spend/common/platform/platform_scaffold.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@ -6,7 +7,7 @@ import 'package:url_launcher/url_launcher.dart';
import 'package:local_spend/common/functions/customAbout.dart' as custom; import 'package:local_spend/common/functions/customAbout.dart' as custom;
import 'package:local_spend/common/functions/showDialogTwoButtons.dart'; import 'package:local_spend/common/functions/showDialogTwoButtons.dart';
const url = "https://flutter.io/"; const URL = "https://flutter.io/";
const demonstration = false; const demonstration = false;
class MorePage extends StatefulWidget { class MorePage extends StatefulWidget {
@ -36,7 +37,7 @@ class MorePageState extends State<MorePage> {
focusNode.dispose(); //disposes focus node when form disposed focusNode.dispose(); //disposes focus node when form disposed
} }
void _saveCurrentRoute(String lastRoute) async { _saveCurrentRoute(String lastRoute) async {
SharedPreferences preferences = await SharedPreferences.getInstance(); SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString('LastPageRoute', lastRoute); await preferences.setString('LastPageRoute', lastRoute);
} }
@ -51,9 +52,9 @@ class MorePageState extends State<MorePage> {
} else { } else {
Navigator.of(context).pushReplacementNamed('/LoginPage'); Navigator.of(context).pushReplacementNamed('/LoginPage');
} }
return null;
}, },
child: PlatformScaffold( child: PlatformScaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Colors.blue[400], backgroundColor: Colors.blue[400],
title: Text( title: Text(
@ -67,11 +68,14 @@ class MorePageState extends State<MorePage> {
centerTitle: true, centerTitle: true,
iconTheme: IconThemeData(color: Colors.black), iconTheme: IconThemeData(color: Colors.black),
), ),
body: Container( body: Container(
padding: EdgeInsets.fromLTRB(30.0, 0.0, 30.0, 0.0),
child: ListView( child: ListView(
children: <Widget>[ children: <Widget>[
Container( Container(
padding: EdgeInsets.fromLTRB(30.0, 25, 30.0, 0.0), padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
child : Text( child : Text(
"Local Spend Tracker", "Local Spend Tracker",
textAlign: TextAlign.center, textAlign: TextAlign.center,
@ -84,70 +88,46 @@ class MorePageState extends State<MorePage> {
), ),
Padding( Padding(
padding: EdgeInsets.fromLTRB(30.0, 25.0, 30.0, 0.0), padding: EdgeInsets.fromLTRB(0.0, 25.0, 0.0, 0.0),
child: Container( child: Container(
height: 65.0, height: 65.0,
child: RaisedButton( child: RaisedButton(
onPressed: () { onPressed: () {
custom.showAboutDialog( custom.showAboutDialog(
context: context, context: context,
applicationIcon: new Icon(Icons.receipt), applicationIcon: new Icon(Icons.receipt),
applicationName: "Local Spend Tracker", applicationName: "Local Spend Tracker",
children: <Widget> [ children: <Widget> [
Text("Pear Trading is a commerce company designed to register and monitor money circulating in the local economy.\n"), Text("Pear Trading is a commerce company designed to register and monitor money circulating in the local economy."),
Container( Text("\nContact at test@example.com or +44 01524 64544"),
margin: EdgeInsets.symmetric(horizontal: 10),
height: 35,
child: RaisedButton(
onPressed: () => launch('http://www.peartrade.org'),
child: Text("Pear Trading",
style: TextStyle(
color: Colors.white, fontSize: 18.0)),
color: Colors.green,
),
),
Container( Padding(
margin: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0.0), padding: EdgeInsets.fromLTRB(0,20,0,0),
height: 40.0,
child: Material(
color: Colors.transparent,
child: InkWell( child: InkWell(
borderRadius: BorderRadius.circular(3), child: Text
onTap: () => launch('https://shadow.cat'), ('Developed by Shadowcat Systems',
child: Column(
children: [
Align(
child: Text("Developed by"),
alignment: Alignment.centerLeft),
Container(
margin: EdgeInsets.all(0),
child : Text(
"Shadowcat Systems",
style: TextStyle( style: TextStyle(
fontSize: 18, color: Colors.blue,
fontWeight: FontWeight.bold
), ),
), ),
), onTap: () => launch('https://shadow.cat/')
],
), ),
), ),
),
),
], ],
); );
}, },
child: Text("ABOUT", child: Text("ABOUT",
style: TextStyle(color: Colors.white, fontSize: 22.0)), style:
TextStyle(color: Colors.white, fontSize: 22.0)),
color: Colors.blue, color: Colors.blue,
), ),
), ),
), ),
Padding( Padding(
padding: EdgeInsets.fromLTRB(30.0, 20.0, 30.0, 0.0), padding: EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 0.0),
child: Container( child: Container(
height: 65.0, height: 65.0,
child: RaisedButton( child: RaisedButton(
@ -158,17 +138,19 @@ class MorePageState extends State<MorePage> {
"Are you sure you want to log out?", "Are you sure you want to log out?",
"Cancel", "Cancel",
"Logout", "Logout",
logout); logout
);
}, },
child: Text("LOGOUT", child: Text("LOGOUT",
style: TextStyle(color: Colors.white, fontSize: 22.0)), style:
TextStyle(color: Colors.white, fontSize: 22.0)),
color: Colors.red, color: Colors.red,
), ),
), ),
), ),
// Padding( // Padding(
// padding: EdgeInsets.fromLTRB(30.0, 20.0, 30.0, 0.0), // padding: EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 0.0),
// child: Container( // child: Container(
// height: 65.0, // height: 65.0,
// child: RaisedButton( // child: RaisedButton(
@ -182,6 +164,7 @@ class MorePageState extends State<MorePage> {
// ), // ),
// ), // ),
// ), // ),
], ],
), ),
), ),

View file

@ -1,8 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:local_spend/common/platform/platform_scaffold.dart'; import 'package:local_spend/common/platform/platform_scaffold.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.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;
class NewStatsPage extends StatefulWidget { class NewStatsPage extends StatefulWidget {
@ -13,6 +15,7 @@ class NewStatsPage extends StatefulWidget {
} }
class NewStatsPageState extends State<NewStatsPage> { class NewStatsPageState extends State<NewStatsPage> {
/// Graph types: /// Graph types:
/// - total_last_week /// - total_last_week
/// - avg_spend_last_week /// - avg_spend_last_week
@ -30,13 +33,14 @@ class NewStatsPageState extends State<NewStatsPage> {
super.dispose(); super.dispose();
} }
void _saveCurrentRoute(String lastRoute) async { _saveCurrentRoute(String lastRoute) async {
SharedPreferences preferences = await SharedPreferences.getInstance(); SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString('LastPageRoute', lastRoute); await preferences.setString('LastPageRoute', lastRoute);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return PlatformScaffold( return PlatformScaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Colors.blue[400], backgroundColor: Colors.blue[400],
@ -51,7 +55,11 @@ class NewStatsPageState extends State<NewStatsPage> {
centerTitle: true, centerTitle: true,
iconTheme: IconThemeData(color: Colors.black), iconTheme: IconThemeData(color: Colors.black),
), ),
body: Container(),
body : Container(
),
); );
} }
} }

View file

@ -1,178 +0,0 @@
import 'package:flutter/material.dart';
import 'package:local_spend/common/apifunctions/get_graph_data.dart';
import 'package:charts_flutter/flutter.dart' as charts;
class OrgGraphs extends StatefulWidget {
OrgGraphs({Key key}) : super(key: key);
@override
_OrgGraphsState createState() {
return _OrgGraphsState();
}
}
class _OrgGraphsState extends State<OrgGraphs> {
/// Organisations' graphs types: to fetch, POST to https://dev.localspend.co.uk/api/stats/[graph_type] as {"session_key":"[boop beep]"}
/// - organisations_all : organisation
/// - pies : organisation/pies
/// - snippets : organisation/snippets
/// - graphs : organisation/graphs
/// - {"graph":"customers_last_7_days","session_key":"[bleep]"}
/// - {"graph":"customers_last_30_days","session_key":"[blah]"}
/// - {"graph":"sales_last_7_days","session_key":"[bloop]"}
/// - {"graph":"sales_last_7_days","session_key":"[reee]"}
/// - {"graph":"purchases_last_7_days","session_key":"[yee]"}
/// - {"graph":"purchases_last_30_days","session_key":"[yah]"}
/// - {"graph":"purchases_all;","session_key":"[kappa]"} // I don't think this one works
///
/// HTTP POST request sample:
/// {"graph":"total_last_week","session_key":"blahblahblah"}
// OrganisationGraph customersLastWeek = new OrganisationGraph("graphs", graphsType: "customers_last_7_days");
OrganisationGraph customersLastMonth =
new OrganisationGraph("graphs", graphsType: "customers_last_30_days");
OrganisationGraph salesLastMonth =
new OrganisationGraph("graphs", graphsType: "sales_last_30_days");
OrganisationGraph purchasesLastMonth = new OrganisationGraph("graphs",
graphsType: "purchases_last_30_days"); //purchases_last_30_days
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
// if (!customersLastWeek.loaded) {
// customersLastWeek.getGraphData().then((_) {
// setState(() {});
// });
// }
if (!customersLastMonth.loaded) {
customersLastMonth.getGraphData().then((_) {
setState(() {});
});
}
if (!salesLastMonth.loaded) {
salesLastMonth.getGraphData().then((_) {
setState(() {});
});
}
if (!purchasesLastMonth.loaded) {
purchasesLastMonth.getGraphData().then((_) {
setState(() {});
});
}
return ListView(
children: <Widget>[
// Container(
// padding: EdgeInsets.fromLTRB(0.0, 17, 0.0, 0.0),
// child: Text(
// "Last Week's Customers",
// textAlign: TextAlign.center,
// style: TextStyle(
// fontSize: 22.0,
// color: Colors.black,
// fontWeight: FontWeight.bold,
// ),
// ),
// ),
// Tooltip(
// message: "Graph of customers last week",
// child: Container(
// padding: EdgeInsets.symmetric(horizontal: 10),
// height: 200,
// child: customersLastWeek.graph != null
// ? new charts.TimeSeriesChart(customersLastWeek.graph)
// : Center(
// child: Text(
// "Loading graph...")), //List<Series<dynamic, DateTime>>
// ),
// ),
Container(
padding: EdgeInsets.fromLTRB(0.0, 17, 0.0, 0.0),
child: Text(
"This Month's Customers",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
Tooltip(
message: "Customers this month", // this needs to be better
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
height: 200,
child: customersLastMonth.graph != null
? new charts.TimeSeriesChart(customersLastMonth.graph)
: Center(
child:
CircularProgressIndicator()), //List<Series<dynamic, DateTime>>
),
),
Container(
padding: EdgeInsets.fromLTRB(0.0, 17, 0.0, 0.0),
child: Text(
"This Month's Revenue",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
Tooltip(
message: "Revenue from sales this month",
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
height: 200,
child: salesLastMonth.graph != null
? new charts.TimeSeriesChart(salesLastMonth.graph)
: Center(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(Colors.green),
)), //List<Series<dynamic, DateTime>>
),
),
Container(
padding: EdgeInsets.fromLTRB(0.0, 17, 0.0, 0.0),
child: Text(
"This Month's Sales",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
Tooltip(
message: "Number of sales this month",
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
height: 200,
child: purchasesLastMonth.graph != null
? new charts.TimeSeriesChart(purchasesLastMonth.graph)
: Center(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(Colors.red),
)), //List<Series<dynamic, DateTime>>
),
),
],
);
}
}

View file

@ -1,15 +1,21 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:local_spend/common/platform/platform_scaffold.dart'; import 'package:local_spend/common/platform/platform_scaffold.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'dart:async'; import 'dart:core';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:local_spend/common/widgets/animatedGradientButton.dart';
import 'package:local_spend/common/apifunctions/find_organisations.dart'; import 'package:local_spend/common/apifunctions/find_organisations.dart';
import 'package:local_spend/common/widgets/organisations_dialog.dart'; import 'package:local_spend/common/widgets/organisations_dialog.dart';
import 'package:local_spend/common/apifunctions/submit_receipt_api.dart'; import 'package:local_spend/common/apifunctions/submit_receipt_api.dart';
import 'package:local_spend/common/apifunctions/categories.dart'; import 'package:local_spend/common/apifunctions/categories.dart';
class Transaction { class Transaction {
DateTime date;
TextEditingController amount;
Organisation organisation;
String recurring;
bool isEssential;
String category;
Transaction( Transaction(
this.date, this.date,
this.amount, this.amount,
@ -18,13 +24,6 @@ class Transaction {
this.isEssential, this.isEssential,
this.category, this.category,
); );
DateTime date;
TextEditingController amount;
Organisation organisation;
String recurring;
bool isEssential;
String category;
} }
class ReceiptPage2 extends StatefulWidget { class ReceiptPage2 extends StatefulWidget {
@ -44,7 +43,7 @@ class ReceiptPage2State extends State<ReceiptPage2> {
"Uncategorised", "Uncategorised",
); );
AlertDialog _invalidDialog(context) { _invalidDialog(context) {
return AlertDialog( return AlertDialog(
title: new Text("Invalid data"), title: new Text("Invalid data"),
content: new Text( content: new Text(
@ -64,7 +63,18 @@ class ReceiptPage2State extends State<ReceiptPage2> {
return await getCategories(); return await getCategories();
} }
void _submitReceipt(Transaction transaction) { _submitReceipt(Transaction transaction) {
DateTime dt = new DateTime.now();
// sample transaction:
// {
// "transaction_type":1,
// "transaction_value":33,
// "purchase_time":"2019-08-12T11:06:00.000+01:00",
// "organisation_id":59661,
// "essential":false,
// "session_key":"C438432A-B775-11E9-8EE8-147589E69626"
// }
Receipt receipt = new Receipt(); Receipt receipt = new Receipt();
receipt.organisationName = transaction.organisation.name; receipt.organisationName = transaction.organisation.name;
receipt.street = transaction.organisation.streetName; receipt.street = transaction.organisation.streetName;
@ -84,49 +94,36 @@ class ReceiptPage2State extends State<ReceiptPage2> {
} }
receipt.amount = transaction.amount.text.toString(); receipt.amount = transaction.amount.text.toString();
receipt.time = DateFormat("yyyy-MM-dd'T'hh:mm':00.000+01:00'") receipt.time = DateFormat("yyyy-MM-dd'T'hh:mm':00.000+01:00'").format(transaction.date).toString();
.format(transaction.date)
.toString();
receipt.essential = transaction.isEssential.toString(); receipt.essential = transaction.isEssential.toString();
submitReceiptAPI(context, receipt); submitReceiptAPI(context, receipt);
} }
List<String> _recurringOptions = new List<String>(7); List<String> _sampleRecurringOptions = new List<String>(7);
List<String> _categories = new List<String>(); List<String> _categories = new List<String>();
@override
void initState() {
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var _widgetHeight = MediaQuery.of(context).size.height * 0.06 < 40.0
? 40.0
: MediaQuery.of(context).size.height * 0.06;
var _fontSize = _widgetHeight * 0.45; if (_categories.length == 0) {
var _fontSizeButton = _widgetHeight * 0.5;
if (_categories.isEmpty) {
Future<List<String>> _futureCats = getCats(); Future<List<String>> _futureCats = getCats();
_categories.add("Fetching categories..."); _categories.add("Fetching categories...");
_futureCats.then((value) { _futureCats.then((value) {
_categories = null;
_categories = value; _categories = value;
_categories.insert(0, "Uncategorised");
setState(() {}); setState(() {});
}); });
} }
_recurringOptions[0] = "None"; _sampleRecurringOptions[0] = "None";
_recurringOptions[1] = "Daily"; _sampleRecurringOptions[1] = "Daily";
_recurringOptions[2] = "Weekly"; _sampleRecurringOptions[2] = "Weekly";
_recurringOptions[3] = "Fortnightly"; _sampleRecurringOptions[3] = "Fortnightly";
_recurringOptions[4] = "Monthly"; _sampleRecurringOptions[4] = "Monthly";
_recurringOptions[5] = "Quarterly"; _sampleRecurringOptions[5] = "Quarterly";
_recurringOptions[6] = "Yearly"; _sampleRecurringOptions[6] = "Yearly"; // these will be difficult to fetch from server as they are coded into the site's rather than fetched
// these will be difficult to fetch from server as they are coded into the site's HTML rather than fetched
return PlatformScaffold( return PlatformScaffold(
appBar: AppBar( appBar: AppBar(
@ -141,22 +138,18 @@ class ReceiptPage2State extends State<ReceiptPage2> {
centerTitle: true, centerTitle: true,
iconTheme: IconThemeData(color: Colors.black), iconTheme: IconThemeData(color: Colors.black),
), ),
body: ListView( body: ListView(
children: <Widget>[ children: <Widget>[
// each CHILD has its own horizontal padding because if the listView // each CHILD has its own horizontal padding because if the listView has padding, Android's end-of-scroll animation
// has padding, Android's end-of-scroll animation
// doesn't fit the screen properly and looks weird // doesn't fit the screen properly and looks weird
Container( Container(
padding: EdgeInsets.fromLTRB( padding: const EdgeInsets.fromLTRB(15, 17, 0, 0),
MediaQuery.of(context).size.width * 0.025,
MediaQuery.of(context).size.height * 0.025,
0,
0.0),
child : Text( child : Text(
"Receipt Details", "Receipt Details",
style: TextStyle( style: TextStyle(
fontSize: 26, fontSize: 24,
color: Colors.grey[700], color: Colors.grey[700],
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@ -164,51 +157,39 @@ class ReceiptPage2State extends State<ReceiptPage2> {
), // "Receipt Details" title ), // "Receipt Details" title
Container( Container(
padding: EdgeInsets.fromLTRB( padding: EdgeInsets.fromLTRB(25,15,15.0,0.0),
MediaQuery.of(context).size.width * 0.05,
15,
MediaQuery.of(context).size.width * 0.05,
0.0),
child: Tooltip(
message: "Date and time of transaction",
child: Row( child: Row(
children: <Widget> [ children: <Widget> [
Container( Container(
child : Text( child : Text(
"Date/Time", "Date/Time",
style: TextStyle( style: TextStyle(
fontSize: _fontSize, fontSize: 18,
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
width: MediaQuery.of(context).size.width * 0.3, width: 110,
), ),
Container( Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
height: _widgetHeight, height: 32.0,
width: MediaQuery.of(context).size.width * 0.6,
child: RaisedButton( child: RaisedButton(
onPressed: () { onPressed: () {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
builder: (BuildContext builder) { builder: (BuildContext builder) {
return Container( return Container(
height: MediaQuery.of(context) height: MediaQuery.of(context).copyWith().size.height / 3,
.copyWith()
.size
.height /
3,
child: CupertinoDatePicker( child: CupertinoDatePicker(
initialDateTime: initialDateTime: transaction.date.isAfter(DateTime.now())
transaction.date.isAfter(DateTime.now())
? DateTime.now() ? DateTime.now()
: transaction.date, : transaction.date,
onDateTimeChanged: (DateTime newDate) { onDateTimeChanged: (DateTime newDate) {
setState(() => { setState(() => {
newDate.isAfter(DateTime.now()) newDate.isAfter(DateTime.now())
? transaction.date = ? transaction.date = DateTime.now()
DateTime.now()
: transaction.date = newDate, : transaction.date = newDate,
}); });
}, },
@ -220,59 +201,43 @@ class ReceiptPage2State extends State<ReceiptPage2> {
}, },
child: Text( child: Text(
transaction.date == null transaction.date == null
? 'None set.' ? 'None set'
: transaction.date.year != DateTime.now().year : transaction.date.year == DateTime.now().year
? '${new DateFormat.MMMd().format(transaction.date)}' + ? '${new DateFormat.MMMd().format(transaction.date)}' + ", " + '${new DateFormat.Hm().format(transaction.date)}'
" " + : '${new DateFormat.MMMd().format(transaction.date)}' + " " + transaction.date.year.toString() + ", " + '${new DateFormat.Hm().format(transaction.date)}',
transaction.date.year.toString() + style:
" at " + TextStyle(color: Colors.white, fontSize: 18.0),
'${new DateFormat.Hm().format(transaction.date)}'
: transaction.date.day == DateTime.now().day &&
transaction.date.month ==
DateTime.now().month
? "Today at " +
'${new DateFormat.Hm().format(transaction.date)}'
: '${new DateFormat.MMMd().format(transaction.date)}' +
" at " +
'${new DateFormat.Hm().format(transaction.date)}',
style: TextStyle(
color: Colors.white, fontSize: _fontSizeButton),
), ),
color: Colors.blue, color: Colors.blue,
), ),
), ),
], ],
), ),
),
), // Date/Time picker ), // Date/Time picker
Container( Container(
padding: EdgeInsets.fromLTRB( padding: EdgeInsets.fromLTRB(25,15,15.0,0.0),
MediaQuery.of(context).size.width * 0.05,
15,
MediaQuery.of(context).size.width * 0.05,
0.0),
child: Tooltip(
message: "Transaction payee",
child: Row( child: Row(
children: <Widget> [ children: <Widget> [
Container( Container(
child : Text( child : Text(
"Payee", "Payee",
style: TextStyle( style: TextStyle(
fontSize: _fontSize, fontSize: 18,
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
width: MediaQuery.of(context).size.width * 0.3, width: 110,
), ),
Container( Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
height: _widgetHeight, height: 32.0,
width: MediaQuery.of(context).size.width * 0.6,
child: RaisedButton( child: RaisedButton(
onPressed: () { onPressed: () {
// var popupListView = new PopupListView();
// var dialog = popupListView.dialog(context, optionsList, "Choose Organization");
var organisations = new FindOrganisations(); var organisations = new FindOrganisations();
var orgDialog = organisations.dialog(context); var orgDialog = organisations.dialog(context);
orgDialog.then((organisation) { orgDialog.then((organisation) {
@ -290,73 +255,52 @@ class ReceiptPage2State extends State<ReceiptPage2> {
transaction.organisation.name == null transaction.organisation.name == null
? 'Find' ? 'Find'
: transaction.organisation.name.length > 14 : transaction.organisation.name.length > 14
? transaction.organisation.name ? transaction.organisation.name.substring(0,12) + "..."
.substring(0, 12) +
"..."
: transaction.organisation.name, : transaction.organisation.name,
style: TextStyle( style:
color: Colors.white, fontSize: _fontSizeButton), TextStyle(color: Colors.white, fontSize: 18.0),
), ),
color: Colors.blue, color: Colors.blue,
), ),
), ),
], ],
), ),
),
), // Organisation picker ), // Organisation picker
Container( Container(
padding: EdgeInsets.fromLTRB( padding: EdgeInsets.fromLTRB(25,15,15.0,0.0),
MediaQuery.of(context).size.width * 0.05,
15,
MediaQuery.of(context).size.width * 0.05,
0.0),
child: Tooltip(
message: "Repeating?",
child: Row( child: Row(
children: <Widget> [ children: <Widget> [
Container( Container(
child : Text( child : Text(
"Recurring", "Recurring",
style: TextStyle( style: TextStyle(
fontSize: _fontSize, fontSize: 18,
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
width: MediaQuery.of(context).size.width * 0.3, width: 110,
), ),
Container( Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
height: _widgetHeight, height: 32.0,
width: MediaQuery.of(context).size.width * 0.6,
child: RaisedButton( child: RaisedButton(
onPressed: () { onPressed: () {
transaction.recurring = _recurringOptions[0];
setState(() {});
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
builder: (BuildContext builder) { builder: (BuildContext builder) {
return Container( return Container(
height: MediaQuery.of(context) height: MediaQuery.of(context).copyWith().size.height / 3,
.copyWith()
.size
.height /
3,
child: CupertinoPicker( child: CupertinoPicker(
backgroundColor: Colors.white, backgroundColor: Colors.white,
children: _recurringOptions children: _sampleRecurringOptions.map((thisOption) => Text(thisOption)).toList(),
.map((thisOption) => Text(thisOption,
style: TextStyle(fontSize: 30)))
.toList(),
onSelectedItemChanged: ((newValue) { onSelectedItemChanged: ((newValue) {
transaction.recurring = transaction.recurring = _sampleRecurringOptions[newValue];
_recurringOptions[newValue];
setState(() {}); setState(() {});
}), }),
magnification: 1.1, itemExtent: 32,
useMagnifier: true,
itemExtent: 36,
), ),
); );
}); });
@ -365,71 +309,61 @@ class ReceiptPage2State extends State<ReceiptPage2> {
transaction.recurring == null transaction.recurring == null
? 'None' ? 'None'
: transaction.recurring, : transaction.recurring,
style: TextStyle( style:
color: Colors.white, fontSize: _fontSizeButton), TextStyle(color: Colors.white, fontSize: 18.0),
), ),
color: Colors.blue, color: Colors.blue,
), ),
), ),
], ],
), ),
),
), // Recurring picker ), // Recurring picker
Container( Container(
padding: EdgeInsets.fromLTRB( padding: EdgeInsets.fromLTRB(25,15,15.0,0.0),
MediaQuery.of(context).size.width * 0.05,
15,
MediaQuery.of(context).size.width * 0.05,
0.0),
child: Row( child: Row(
children: <Widget> [ children: <Widget> [
Container( Container(
child : Text( child : Text(
"Category", "Category",
style: TextStyle( style: TextStyle(
fontSize: _fontSize, fontSize: 18,
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
width: MediaQuery.of(context).size.width * 0.3, width: 110,
), ),
Container( Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
height: _widgetHeight, height: 32.0,
width: MediaQuery.of(context).size.width * 0.6, // child: DropdownButton(
child: Tooltip( // value: transaction.category,
message: "Category of transaction", // items: _categories.map((thisOption) {
// DropdownMenuItem(child: Text(thisOption));
// }).toList(),
//// items: _categories.map((thisOption) => DropdownMenuItem(child : Text(thisOption))).toList(),
// onChanged: ((newValue) {
// transaction.category = _categories[newValue];
// setState(() {});
// })
// ),
child: RaisedButton( child: RaisedButton(
onPressed: () { onPressed: () {
transaction.category = _categories[0];
setState(() {});
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
builder: (BuildContext builder) { builder: (BuildContext builder) {
return Container( return Container(
height: MediaQuery.of(context) height: MediaQuery.of(context).copyWith().size.height / 3,
.copyWith()
.size
.height /
3,
child: CupertinoPicker( child: CupertinoPicker(
backgroundColor: Colors.white, backgroundColor: Colors.white,
children: _categories children: _categories.map((thisOption) => Text(thisOption)).toList(),
.map((thisOption) => Text(
thisOption,
style: TextStyle(fontSize: 30),
))
.toList(),
onSelectedItemChanged: ((newValue) { onSelectedItemChanged: ((newValue) {
transaction.category = transaction.category = _categories[newValue];
_categories[newValue];
setState(() {}); setState(() {});
}), }),
magnification: 1.1, itemExtent: 32,
useMagnifier: true,
itemExtent: 36,
), ),
); );
}); });
@ -438,41 +372,34 @@ class ReceiptPage2State extends State<ReceiptPage2> {
transaction.category == null transaction.category == null
? 'None' ? 'None'
: transaction.category, : transaction.category,
style: TextStyle( style:
color: Colors.white, fontSize: _fontSizeButton), TextStyle(color: Colors.white, fontSize: 18.0),
), ),
color: Colors.blue, color: Colors.blue,
), ),
), ),
),
], ],
), ),
), // Category picker ), // Category picker
Container( Container(
padding: EdgeInsets.fromLTRB( padding: EdgeInsets.fromLTRB(25,15,15.0,0.0),
MediaQuery.of(context).size.width * 0.05,
15,
MediaQuery.of(context).size.width * 0.05,
0.0),
child: Tooltip(
message: "Essential or not",
child: Row( child: Row(
children: <Widget> [ children: <Widget> [
Container( Container(
child : Text( child : Text(
"Essential", "Essential",
style: TextStyle( style: TextStyle(
fontSize: _fontSize, fontSize: 18,
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
width: MediaQuery.of(context).size.width * 0.3, width: 95,
), ),
Container( Container(
height: _widgetHeight, height: 32.0,
width: MediaQuery.of(context).size.width * 0.6,
child: Checkbox( child: Checkbox(
value: transaction.isEssential, value: transaction.isEssential,
onChanged: ((value) { onChanged: ((value) {
@ -482,120 +409,81 @@ class ReceiptPage2State extends State<ReceiptPage2> {
), ),
], ],
), ),
),
), // Essential ), // Essential
Container( Container(
padding: EdgeInsets.fromLTRB( padding: EdgeInsets.fromLTRB(25,15,15.0,0.0),
MediaQuery.of(context).size.width * 0.05,
15,
MediaQuery.of(context).size.width * 0.05,
0.0),
child: Tooltip(
message: "Transaction amount",
child: Row( child: Row(
children: <Widget> [ children: <Widget> [
Container( Container(
child : Text( child : Text(
"Amount", "Amount",
style: TextStyle( style: TextStyle(
fontSize: _fontSize, fontSize: 18,
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
width: MediaQuery.of(context).size.width * 0.3, width: 110,
), ),
Container( Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
height: _widgetHeight, height: 32.0,
width: MediaQuery.of(context).size.width * 0.6, width: 100,
child: TextField( child: TextField(
style: TextStyle(
fontSize: _fontSize,
),
textAlign: TextAlign.center,
controller: transaction.amount, controller: transaction.amount,
decoration: InputDecoration(hintText: "0.00"), decoration: InputDecoration(
keyboardType: TextInputType.numberWithOptions( hintText: "0.00"
decimal: true, signed: true), ),
keyboardType: TextInputType.numberWithOptions(decimal: true, signed: true),
), ),
), ),
], ],
), ),
),
), // Amount picker ), // Amount picker
Padding( Padding(
padding: EdgeInsets.fromLTRB( padding: EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 0.0),
MediaQuery.of(context).size.width * 0.05,
MediaQuery.of(context).size.height * 0.03,
MediaQuery.of(context).size.width * 0.05,
15.0),
child: Tooltip(
message: "Submit receipt",
child: Container( child: Container(
height: _widgetHeight * 1.7, height: 65.0,
child: ClipRRect( child: RaisedButton(
borderRadius: BorderRadius.circular(2), onPressed: () {
child: Opacity(
opacity: 1,
child: Stack(
children: [
AnimatedBackground(
[Colors.blue, Colors.lightBlue[300]],
Colors.lightBlue,
Alignment.topLeft,
Alignment.bottomRight,
4),
Material(
type: MaterialType.transparency,
child: InkWell(
child: Center(
child: Text(
"GO",
style: TextStyle(
color: Colors.white, fontSize: 30.0),
),
),
onTap: () {
try { try {
if (transaction.amount.text == "" || if (transaction.amount.text == "" || transaction.organisation.name == null) {
transaction.organisation.name == null) {
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return _invalidDialog(context); return _invalidDialog(context);
}); }
);
} else { } else {
if (double.tryParse( if (double.tryParse(transaction.amount.text) != null && double.tryParse(transaction.amount.text) > 0) {
transaction.amount.text) !=
null &&
double.tryParse(transaction.amount.text) >
0) {
_submitReceipt(transaction); _submitReceipt(transaction);
} else { } else {
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return _invalidDialog(context); return _invalidDialog(context);
}); }
);
} }
} }
} catch (_) { }
catch (_) {
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return _invalidDialog(context); return _invalidDialog(context);
}); }
);
} }
}, },
), child: Text("GO",
), style:
], TextStyle(color: Colors.white, fontSize: 22.0)),
), color: Colors.blue,
),
),
), ),
), ),
), ),

View file

@ -12,7 +12,7 @@ class SplashScreen extends StatefulWidget {
class _SplashScreenState extends State<SplashScreen> { class _SplashScreenState extends State<SplashScreen> {
final int splashDuration = 1; final int splashDuration = 1;
Future<Timer> startTime() async { startTime() async {
return Timer(Duration(seconds: splashDuration), () { return Timer(Duration(seconds: splashDuration), () {
SystemChannels.textInput.invokeMethod('TextInput.hide'); SystemChannels.textInput.invokeMethod('TextInput.hide');
Navigator.of(context).pushReplacementNamed('/LoginPage'); Navigator.of(context).pushReplacementNamed('/LoginPage');
@ -27,7 +27,10 @@ class _SplashScreenState extends State<SplashScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var drawer = Drawer();
return PlatformScaffold( return PlatformScaffold(
drawer: drawer,
body: Container( body: Container(
decoration: BoxDecoration(color: Colors.white), decoration: BoxDecoration(color: Colors.white),
child: Column( child: Column(
@ -35,9 +38,10 @@ class _SplashScreenState extends State<SplashScreen> {
Expanded( Expanded(
child: Container( child: Container(
margin: EdgeInsets.all(15), margin: EdgeInsets.all(15),
alignment: FractionalOffset(0.5, 0.3),
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: AssetImage('assets/images/launch_image.png'), image: AssetImage('assets/images/launch_image.png')
), ),
), ),
), ),
@ -45,7 +49,7 @@ class _SplashScreenState extends State<SplashScreen> {
Container( Container(
margin: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 30.0), margin: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 30.0),
child: Text( child: Text(
"© Copyright Pear Trading 2019", "© Copyright Statement 2019",
style: TextStyle( style: TextStyle(
fontSize: 16.0, fontSize: 16.0,
color: Colors.black, color: Colors.black,

View file

@ -1,25 +1,44 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:local_spend/common/platform/platform_scaffold.dart'; import 'package:local_spend/common/platform/platform_scaffold.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:local_spend/pages/customerGraphs.dart'; import 'package:local_spend/common/functions/logout.dart';
import 'package:local_spend/pages/orgGraphs.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:local_spend/common/functions/customAbout.dart' as custom;
import 'package:local_spend/common/functions/showDialogTwoButtons.dart';
import 'package:local_spend/common/widgets/charts/donut_chart.dart';
import 'package:local_spend/common/widgets/charts/outside_label.dart';
import 'package:local_spend/common/widgets/charts/auto_label.dart';
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/numeric_line_bar_combo.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;
import 'package:local_spend/common/widgets/charts/chart_builder.dart';
const url = "https://flutter.io/"; const URL = "https://flutter.io/";
const demonstration = false; const demonstration = false;
class StatsPage extends StatefulWidget { class StatsPage extends StatefulWidget {
@override @override
State<StatefulWidget> createState() { State<StatefulWidget> createState() {
print(
"TODO: The 'stats' page should be loaded on login and cached rather than reloading on every opening of the page.");
print(
"Create new List<GraphData> in instantiated MyApp() and pass that or load it from this class' child with (graphs = super.graphList) or something.");
return new StatsPageState(); return new StatsPageState();
} }
} }
class StatsPageState extends State<StatsPage> { class StatsPageState extends State<StatsPage> {
String userType = "-";
/// Graph types:
/// - total_last_week
/// - avg_spend_last_week
/// - total_last_month
/// - avg_spend_last_month
GraphData totalLastWeekGraph = new GraphData("total_last_week");
GraphData avgSpendLastWeekGraph = new GraphData("avg_spend_last_week");
GraphData totalLastMonthGraph = new GraphData("total_last_month");
GraphData avgSpendLastMonth = new GraphData("avg_spend_last_month");
@override @override
void initState() { void initState() {
@ -32,23 +51,36 @@ class StatsPageState extends State<StatsPage> {
super.dispose(); super.dispose();
} }
void _saveCurrentRoute(String lastRoute) async { _saveCurrentRoute(String lastRoute) async {
SharedPreferences preferences = await SharedPreferences.getInstance(); SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString('LastPageRoute', lastRoute); await preferences.setString('LastPageRoute', lastRoute);
} }
Future<String> _getUserType() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
return await preferences.get('LastUserType');
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (userType == "-") {
_getUserType().then((value) { // Initializing graphs:
print(value);
userType = if (!totalLastWeekGraph.loaded) {
'${value[0].toUpperCase()}${value.substring(1)}'; // capitalises first letter totalLastWeekGraph.setGraphData().then((_) {
setState(() {});
});
}
if (!avgSpendLastWeekGraph.loaded) {
avgSpendLastWeekGraph.setGraphData().then((_) {
setState(() {});
});
}
if (!totalLastMonthGraph.loaded) {
totalLastMonthGraph.setGraphData().then((_) {
setState(() {});
});
}
if (!avgSpendLastMonth.loaded) {
avgSpendLastMonth.setGraphData().then((_) {
setState(() {}); setState(() {});
}); });
} }
@ -56,37 +88,102 @@ class StatsPageState extends State<StatsPage> {
return PlatformScaffold( return PlatformScaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Colors.blue[400], backgroundColor: Colors.blue[400],
title: Row( title: Text(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Statistics", "Statistics",
style: TextStyle( style: TextStyle(
fontSize: 20, fontSize: 20,
color: Colors.white, color: Colors.white,
), ),
), ),
Padding(padding: EdgeInsets.symmetric(horizontal: 4)), // leading: BackButton(),
Text(
userType,
style: TextStyle(
fontSize: 20,
color: Colors.white70,
),
),
],
),
centerTitle: true, centerTitle: true,
iconTheme: IconThemeData(color: Colors.black), iconTheme: IconThemeData(color: Colors.black),
), ),
body : Container( body : Container(
padding: EdgeInsets.fromLTRB(0, 0, 0, 0), padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
child: (userType == "-" child: ListView(
? null children: <Widget>[
: (userType.toLowerCase() == "customer"
? CustomerGraphs() Container(
: OrgGraphs())), padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
child : Text(
"Last Week's Total Spend",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 10),
height: 200,
child: totalLastWeekGraph.graph != null ? new charts.TimeSeriesChart(totalLastWeekGraph.graph) : Center(child: Text("Loading graph...")), //List<Series<dynamic, DateTime>>
),
Container(
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
child : Text(
"Last Week's Average Spend",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 10),
height: 200,
child: avgSpendLastWeekGraph.graph != null ? new charts.TimeSeriesChart(avgSpendLastWeekGraph.graph) : Center(child: Text("Loading graph...")), //List<Series<dynamic, DateTime>>
),
Container(
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
child : Text(
"Last Month's Spend",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 10),
height: 200,
child: totalLastMonthGraph.graph != null ? new charts.TimeSeriesChart(totalLastMonthGraph.graph) : Center(child: Text("Loading graph...")), //List<Series<dynamic, DateTime>>
),
Container(
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
child : Text(
"Last Month's Average Spend",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 10),
height: 200,
child: avgSpendLastMonth.graph != null ? new charts.TimeSeriesChart(avgSpendLastMonth.graph) : Center(child: Text("Loading graph...")), //List<Series<dynamic, DateTime>>
),
],
),
), ),
); );
} }

View file

@ -1,160 +1,125 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.0"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
name: analyzer name: analyzer
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.39.15" version: "0.36.4"
args: args:
dependency: transitive dependency: transitive
description: description:
name: args name: args
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.6.0" version: "1.5.2"
async: async:
dependency: transitive dependency: transitive
description: description:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.4.2" version: "2.2.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
name: boolean_selector name: boolean_selector
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.0" version: "1.0.4"
build: build:
dependency: transitive dependency: transitive
description: description:
name: build name: build
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0" version: "1.1.4"
build_config: build_config:
dependency: transitive dependency: transitive
description: description:
name: build_config name: build_config
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.2" version: "0.4.0"
build_daemon: build_daemon:
dependency: transitive dependency: transitive
description: description:
name: build_daemon name: build_daemon
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.4" version: "1.1.0"
build_resolvers: build_resolvers:
dependency: transitive dependency: transitive
description: description:
name: build_resolvers name: build_resolvers
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.10" version: "1.0.5"
build_runner: build_runner:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: build_runner name: build_runner
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.10.0" version: "1.6.1"
build_runner_core: build_runner_core:
dependency: transitive dependency: transitive
description: description:
name: build_runner_core name: build_runner_core
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.2.0" version: "3.0.6"
built_collection: built_collection:
dependency: transitive dependency: transitive
description: description:
name: built_collection name: built_collection
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.3.2" version: "4.2.2"
built_value: built_value:
dependency: transitive dependency: transitive
description: description:
name: built_value name: built_value
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "7.1.0" version: "6.6.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
charcode: charcode:
dependency: transitive dependency: transitive
description: description:
name: charcode name: charcode
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.3" version: "1.1.2"
charts_common: charts_common:
dependency: transitive dependency: transitive
description: description:
name: charts_common name: charts_common
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.0" version: "0.6.0"
charts_flutter: charts_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
name: charts_flutter name: charts_flutter
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.0" version: "0.6.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
cli_util:
dependency: transitive
description:
name: cli_util
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
code_builder: code_builder:
dependency: transitive dependency: transitive
description: description:
name: code_builder name: code_builder
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.4.0" version: "3.2.0"
collection: collection:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.14.13" version: "1.14.11"
convert: convert:
dependency: transitive dependency: transitive
description: description:
@ -168,56 +133,42 @@ packages:
name: crypto name: crypto
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.5" version: "2.0.6"
csslib: csslib:
dependency: transitive dependency: transitive
description: description:
name: csslib name: csslib
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.16.2" version: "0.16.0"
cupertino_icons: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
name: cupertino_icons name: cupertino_icons
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.3" version: "0.1.2"
dart_style: dart_style:
dependency: transitive dependency: transitive
description: description:
name: dart_style name: dart_style
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.6" version: "1.2.7"
datetime_picker_formfield: datetime_picker_formfield:
dependency: "direct main" dependency: "direct main"
description: description:
name: datetime_picker_formfield name: datetime_picker_formfield
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.0" version: "0.1.8"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.1"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
name: fixnum name: fixnum
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.10.11" version: "0.10.9"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -236,50 +187,31 @@ packages:
name: flutter_linkify name: flutter_linkify
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.3" version: "1.1.1"
flutter_localizations: flutter_localizations:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.8"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_web_plugins: front_end:
dependency: transitive dependency: transitive
description: flutter description:
source: sdk name: front_end
version: "0.0.0" url: "https://pub.dartlang.org"
source: hosted
version: "0.1.19"
glob: glob:
dependency: transitive dependency: transitive
description: description:
name: glob name: glob
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.1.7"
google_maps_flutter:
dependency: "direct main"
description:
name: google_maps_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.28+1"
google_maps_flutter_platform_interface:
dependency: transitive
description:
name: google_maps_flutter_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:
@ -293,91 +225,91 @@ packages:
name: html name: html
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.14.0+3" version: "0.14.0+2"
http: http:
dependency: "direct main" dependency: "direct main"
description: description:
name: http name: http
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.12.2" version: "0.12.0+2"
http_multi_server: http_multi_server:
dependency: transitive dependency: transitive
description: description:
name: http_multi_server name: http_multi_server
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.0" version: "2.1.0"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
name: http_parser name: http_parser
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.4" version: "3.1.3"
intl: intl:
dependency: transitive dependency: transitive
description: description:
name: intl name: intl
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.16.1" version: "0.15.8"
io: io:
dependency: transitive dependency: transitive
description: description:
name: io name: io
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.4" version: "0.3.3"
js: js:
dependency: transitive dependency: transitive
description: description:
name: js name: js
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.6.2" version: "0.6.1+1"
json_annotation: json_annotation:
dependency: "direct main" dependency: "direct main"
description: description:
name: json_annotation name: json_annotation
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.1" version: "2.3.0"
json_serializable: json_serializable:
dependency: "direct main" dependency: "direct dev"
description: description:
name: json_serializable name: json_serializable
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.3.0" version: "2.3.0"
linkify: kernel:
dependency: transitive dependency: transitive
description: description:
name: linkify name: kernel
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "0.3.19"
logging: logging:
dependency: transitive dependency: transitive
description: description:
name: logging name: logging
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.11.4" version: "0.11.3+2"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.12.8" version: "0.12.5"
meta: meta:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.8" version: "1.1.6"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@ -385,76 +317,34 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.6+3" version: "0.9.6+3"
node_interop:
dependency: transitive
description:
name: node_interop
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
node_io:
dependency: transitive
description:
name: node_io
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
package_config: package_config:
dependency: transitive dependency: transitive
description: description:
name: package_config name: package_config
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.9.3" version: "1.0.5"
package_resolver:
dependency: transitive
description:
name: package_resolver
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.10"
path: path:
dependency: transitive dependency: transitive
description: description:
name: path name: path
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.7.0" version: "1.6.2"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+2"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
pedantic: pedantic:
dependency: "direct dev" dependency: transitive
description: description:
name: pedantic name: pedantic
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.9.0" version: "1.7.0"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
platform_detect:
dependency: transitive
description:
name: platform_detect
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
pool: pool:
dependency: transitive dependency: transitive
description: description:
@ -462,104 +352,41 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
process:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.13"
pub_semver: pub_semver:
dependency: transitive dependency: transitive
description: description:
name: pub_semver name: pub_semver
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.4.4" version: "1.4.2"
pubspec_parse: pubspec_parse:
dependency: transitive dependency: transitive
description: description:
name: pubspec_parse name: pubspec_parse
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.5" version: "0.1.4"
quiver: quiver:
dependency: transitive dependency: transitive
description: description:
name: quiver name: quiver
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.3" version: "2.0.3"
sa_anicoto:
dependency: transitive
description:
name: sa_anicoto
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
sa_multi_tween:
dependency: transitive
description:
name: sa_multi_tween
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
sa_stateless_animation:
dependency: transitive
description:
name: sa_stateless_animation
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
sa_v1_migration:
dependency: transitive
description:
name: sa_v1_migration
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:
name: shared_preferences name: shared_preferences
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.5.8" version: "0.4.3"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.2+1"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+10"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2+7"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:
name: shelf name: shelf
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.7.7" version: "0.7.5"
shelf_web_socket: shelf_web_socket:
dependency: transitive dependency: transitive
description: description:
@ -567,13 +394,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.3" version: "0.2.3"
simple_animations:
dependency: "direct main"
description:
name: simple_animations
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -585,21 +405,21 @@ packages:
name: source_gen name: source_gen
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.6" version: "0.9.4+2"
source_span: source_span:
dependency: transitive dependency: transitive
description: description:
name: source_span name: source_span
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.7.0" version: "1.5.5"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.9.5" version: "1.9.3"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
@ -613,21 +433,14 @@ packages:
name: stream_transform name: stream_transform
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "0.0.19"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
name: string_scanner name: string_scanner
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.5" version: "1.0.4"
supercharged:
dependency: transitive
description:
name: supercharged
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
@ -641,56 +454,28 @@ packages:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.17" version: "0.2.5"
timing: timing:
dependency: transitive dependency: transitive
description: description:
name: timing name: timing
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.1+2" version: "0.1.1+1"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
name: typed_data name: typed_data
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.1.6"
url_launcher: url_launcher:
dependency: "direct main" dependency: "direct main"
description: description:
name: url_launcher name: url_launcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.5.0" version: "3.0.3"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+1"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+7"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.7"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@ -704,28 +489,21 @@ packages:
name: watcher name: watcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.7+15" version: "0.9.7+10"
web_socket_channel: web_socket_channel:
dependency: transitive dependency: transitive
description: description:
name: web_socket_channel name: web_socket_channel
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.0.13"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.0"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:
name: yaml name: yaml
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.1" version: "2.1.16"
sdks: sdks:
dart: ">=2.9.0-14.0.dev <3.0.0" dart: ">=2.3.0-dev.0.1 <3.0.0"
flutter: ">=1.16.3 <2.0.0" flutter: ">=0.1.4 <2.0.0"

View file

@ -10,24 +10,21 @@ description: Local Spend Tracker
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: ">=2.2.2 <3.0.0" sdk: ">=2.0.0-dev.68.0 <3.0.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
shared_preferences: ^0.5.8 shared_preferences: ^0.4.2
url_launcher: ^5.5.0 url_launcher: ^3.0.3
json_annotation : ^3.0.1 json_annotation : ^2.2.0
http: ^0.12.0+2 http: ^0.12.0+2
datetime_picker_formfield: ^1.0.0 datetime_picker_formfield: ^0.1.8
flutter_linkify: ^3.1.3 flutter_linkify: ^1.0.3
flutter_fadein: ^1.1.1 flutter_fadein: ^1.1.1
charts_flutter: ^0.9.0 charts_flutter: ^0.6.0
simple_animations: ^2.2.1
google_maps_flutter: ^0.5.20+5
json_serializable: ^3.3.0
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2 cupertino_icons: ^0.1.2
@ -35,8 +32,9 @@ dependencies:
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
pedantic: ^1.4.0 build_runner: ^1.1.3
build_runner: ^1.10.0 json_serializable: ^2.1.2
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec # following page: https://www.dartlang.org/tools/pub/pubspec
@ -57,11 +55,6 @@ flutter:
- assets/ - assets/
- assets/images/ - assets/images/
- assets/images/launch_image.png - assets/images/launch_image.png
fonts:
- family: Consolas
fonts:
- asset: assets/Consolas.ttf
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.io/assets-and-images/#resolution-aware. # https://flutter.io/assets-and-images/#resolution-aware.

View file

@ -11,16 +11,20 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:local_spend/main.dart'; import 'package:local_spend/main.dart';
void main() { void main() {
testWidgets('GO button repetition test', (WidgetTester tester) async { testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame. // Build our app and trigger a frame.
await tester.pumpWidget(MyApp()); await tester.pumpWidget(MyApp());
// Tap the GO button and trigger a frame. // Verify that our counter starts at 0.
await tester.tap(find.byKey(Key("goButton"))); expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump(); await tester.pump();
// Verify that the dialog has shown // Verify that our counter has incremented.
// expect(find.text('GO'), findsNothing); expect(find.text('0'), findsNothing);
// expect(find.text('Invalid data'), findsOneWidget); expect(find.text('1'), findsOneWidget);
}); });
} }