Compare commits
86 commits
testingSid
...
developmen
Author | SHA1 | Date | |
---|---|---|---|
|
ceafbb279f | ||
|
9381458d79 | ||
|
203d375aa4 | ||
|
5f53932995 | ||
96508a0228 | |||
b7e23081e2 | |||
2d462788b7 | |||
|
f2ecedd5c5 | ||
|
efa74e2207 | ||
|
2d741e61c7 | ||
|
a512549778 | ||
|
c6e69b037b | ||
|
96fdc01eea | ||
|
204cd74544 | ||
|
c6f6917226 | ||
|
7aeccb46cf | ||
|
ac82ec4d48 | ||
|
ece2c62762 | ||
|
f4a2e6ca7a | ||
|
030f482a54 | ||
|
62d50f86a8 | ||
|
d8ecd50acd | ||
|
7553ce25f8 | ||
|
55310068ea | ||
|
19021a9a09 | ||
|
cf6753363c | ||
|
68102ea628 | ||
|
ebf09413fd | ||
|
f5d2e991a8 | ||
|
28cd64aaee | ||
|
49038e9adc | ||
|
d7bcb29eda | ||
|
84c2fb1f2a | ||
|
231ed2c9df | ||
|
9a5bfbaaf0 | ||
|
cda3fc57e9 | ||
|
e44687fb6b | ||
|
b4c93ff80d | ||
|
6cd65b8ac6 | ||
|
05a57e8203 | ||
|
68a33c53e9 | ||
|
abbd0a7170 | ||
|
d0defec7d6 | ||
|
ff7cdf4907 | ||
|
2e1b74a222 | ||
|
aa40a5c926 | ||
|
1719a66e32 | ||
|
3c1191ec54 | ||
|
503cd3ca59 | ||
|
fdb6a5c69b | ||
|
94150ddd76 | ||
|
eebc8aeee3 | ||
|
41bc274965 | ||
|
9b34dd370d | ||
|
2734131f08 | ||
|
8bdc413eab | ||
|
3060a6d1f9 | ||
|
cb75f1ff87 | ||
|
7d30a8fc94 | ||
|
fc2add5636 | ||
|
a7db1c528b | ||
|
405a037a15 | ||
|
4e67bfbbec | ||
|
ee682eef82 | ||
|
9cba3e037c | ||
|
2218422df4 | ||
|
679fbd2f97 | ||
|
fa481f839d | ||
|
0996a1e252 | ||
|
3dba00b0b6 | ||
|
1e40d78dcd | ||
|
aa6de5559a | ||
|
60b1c5b10c | ||
|
eaf7a06f52 | ||
|
59f69b102f | ||
|
e742badcc8 | ||
|
fa7cf8ffa0 | ||
|
5adb7ae902 | ||
|
755893ee10 | ||
|
525a092f40 | ||
|
2e0802ac73 | ||
|
1b207c1ce6 | ||
|
d9abbbd0e7 | ||
|
9a09154332 | ||
|
d7625f7b88 | ||
|
bf1046c4e5 |
73 changed files with 3061 additions and 1543 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -234,4 +234,6 @@ fabric.properties
|
||||||
!**/ios/**/default.perspectivev3
|
!**/ios/**/default.perspectivev3
|
||||||
!/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
|
||||||
|
|
116
.idea/codeStyles/Project.xml
Normal file
116
.idea/codeStyles/Project.xml
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
<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
106
README.md
|
@ -1,31 +1,99 @@
|
||||||
# local_spend
|
# LocalSpend (Mobile App.)
|
||||||
|
|
||||||
Local Spend Tracker
|
Looking to discover if the value of spending local can be measured, understood and shown.
|
||||||
|
|
||||||
## Getting Started
|
This repository contains the mobile application for the LocalSpend system. See also:
|
||||||
|
|
||||||
This project is a starting point for a Flutter application.
|
* the [Web application](https://github.com/Pear-Trading/Foodloop-Web); and
|
||||||
|
* the [server](https://github.com/Pear-Trading/Foodloop-Server).
|
||||||
|
|
||||||
A few resources to get you started if this is your first Flutter project:
|
## Table of Contents
|
||||||
|
|
||||||
- [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab)
|
* [Tech Stack](#tech-stack)
|
||||||
- [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook)
|
* [Features](#features)
|
||||||
|
* [Installation](#installation)
|
||||||
|
* [Configuration](#configuration)
|
||||||
|
* [Usage](#usage)
|
||||||
|
* [Testing](#testing)
|
||||||
|
* [Code Formatting](#code-formatting)
|
||||||
|
* [Documentation](#documentation)
|
||||||
|
* [Acknowledgments](#acknowledgements)
|
||||||
|
* [License](#license)
|
||||||
|
* [Contact](#contact)
|
||||||
|
|
||||||
For help getting started with Flutter, view our
|
## Technology Stack
|
||||||
[online documentation](https://flutter.io/docs), which offers tutorials,
|
|
||||||
samples, guidance on mobile development, and a full API reference.
|
|
||||||
|
|
||||||
## Environments
|
The mobile app. is written in [Dart](https://dart.dev/).
|
||||||
|
|
||||||
To build an apk from dev, use:
|
| Technology | Description | Link |
|
||||||
flutter build apk -t lib/main_dev.dart
|
|-------------|---------------------------------|---------------------|
|
||||||
|
| Flutter | Cross-platform mobile framework | [Link][flutter] |
|
||||||
|
|
||||||
## Links and Things
|
[flutter]: https://flutter.dev/
|
||||||
|
|
||||||
https://github.com/putraxor/flutter-login-ui
|
## Features
|
||||||
https://github.com/pbirdsall/medium_splash_tokenauth
|
|
||||||
|
|
||||||
## How to debug code
|
This mobile app. provides:
|
||||||
// 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
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
TODO
|
|
||||||
⁃ Show username
|
|
||||||
⁃ add ‘confirm logout’
|
|
||||||
|
|
||||||
Splash Screen
|
|
||||||
⁃ Splash screen transition - fade
|
|
||||||
|
|
||||||
Login
|
|
||||||
⁃ save login details option (checkbox or something)
|
|
||||||
|
|
||||||
Navigation
|
|
||||||
⁃ Make it look good
|
|
||||||
|
|
||||||
Submit Receipt
|
|
||||||
⁃ Categories
|
|
||||||
⁃ Recurring
|
|
||||||
⁃ 'organisation name' dialog works
|
|
18
analysis_options.yaml
Normal file
18
analysis_options.yaml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
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
|
|
@ -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 27
|
compileSdkVersion 28
|
||||||
|
|
||||||
lintOptions {
|
lintOptions {
|
||||||
disable 'InvalidPackage'
|
disable 'InvalidPackage'
|
||||||
|
|
|
@ -13,11 +13,18 @@
|
||||||
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:name="io.flutter.app.FlutterApplication"
|
android:label="SpendTracker"
|
||||||
android:label="local_spend"
|
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<activity
|
|
||||||
android:name=".MainActivity"
|
<meta-data
|
||||||
|
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"
|
||||||
|
@ -27,9 +34,19 @@
|
||||||
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.app.android.SplashScreenUntilFirstFrame"
|
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||||
android:value="true" />
|
android:resource="@drawable/launch_background" />
|
||||||
|
|
||||||
|
<!-- 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"/>
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
package uk.co.localspend.localspend;
|
package uk.co.localspend.localspend;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import io.flutter.embedding.android.FlutterActivity;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
12
android/app/src/main/res/drawable/normal_background.xml
Normal file
12
android/app/src/main/res/drawable/normal_background.xml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?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>
|
|
@ -5,4 +5,8 @@
|
||||||
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>
|
||||||
|
|
|
@ -26,4 +26,4 @@ subprojects {
|
||||||
|
|
||||||
task clean(type: Delete) {
|
task clean(type: Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
|
@ -1 +1,4 @@
|
||||||
org.gradle.jvmargs=-Xmx1536M
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
||||||
|
android.enableR8=true
|
||||||
|
|
BIN
assets/Consolas.ttf
Normal file
BIN
assets/Consolas.ttf
Normal file
Binary file not shown.
BIN
assets/images/cat.png
Normal file
BIN
assets/images/cat.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 90 KiB |
BIN
assets/images/shadowcat-long.png
Normal file
BIN
assets/images/shadowcat-long.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
BIN
assets/images/text.png
Normal file
BIN
assets/images/text.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 169 KiB |
|
@ -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'
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
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):
|
||||||
|
@ -7,12 +15,19 @@ 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:
|
||||||
|
@ -20,9 +35,11 @@ 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: aff02bfeed411c636180d6812254b2daeea14d09
|
PODFILE CHECKSUM: 3389836f37640698630b8f0670315d626d5f1469
|
||||||
|
|
||||||
COCOAPODS: 1.7.3
|
COCOAPODS: 1.7.5
|
||||||
|
|
|
@ -163,6 +163,7 @@
|
||||||
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 = (
|
||||||
);
|
);
|
||||||
|
@ -292,6 +293,24 @@
|
||||||
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 */
|
||||||
|
@ -380,7 +399,7 @@
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = G78D5X4L92;
|
DEVELOPMENT_TEAM = G78D5X4L92;
|
||||||
|
@ -390,6 +409,7 @@
|
||||||
"$(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)",
|
||||||
|
@ -509,7 +529,7 @@
|
||||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = G78D5X4L92;
|
DEVELOPMENT_TEAM = G78D5X4L92;
|
||||||
|
@ -519,6 +539,7 @@
|
||||||
"$(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)",
|
||||||
|
@ -536,7 +557,7 @@
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = G78D5X4L92;
|
DEVELOPMENT_TEAM = G78D5X4L92;
|
||||||
|
@ -546,6 +567,7 @@
|
||||||
"$(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)",
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
#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];
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
<!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>
|
||||||
|
|
|
@ -3,18 +3,8 @@ 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';
|
||||||
|
|
||||||
class Category {
|
Future<List<String>> getCategories() async {
|
||||||
String name;
|
const url = "https://dev.localspend.co.uk/api/search/category";
|
||||||
String index;
|
|
||||||
|
|
||||||
Category({
|
|
||||||
this.name,
|
|
||||||
this.index,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<Category>> getCategories() async {
|
|
||||||
const url = "https://dev.peartrade.org/api/search/category";
|
|
||||||
var token;
|
var token;
|
||||||
|
|
||||||
await getToken().then((result) {
|
await getToken().then((result) {
|
||||||
|
@ -22,10 +12,10 @@ Future<List<Category>> getCategories() async {
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<String, String> body = {
|
Map<String, String> body = {
|
||||||
"session_key":token,
|
"session_key": token,
|
||||||
};
|
};
|
||||||
|
|
||||||
final response = await http.post (
|
final response = await http.post(
|
||||||
url,
|
url,
|
||||||
body: json.encode(body),
|
body: json.encode(body),
|
||||||
);
|
);
|
||||||
|
@ -34,7 +24,7 @@ Future<List<Category>> getCategories() async {
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
//request successful
|
//request successful
|
||||||
List<Category> categories = new List<Category>();
|
List<String> categories = new List<String>();
|
||||||
Map responseMap = json.decode(response.body);
|
Map responseMap = json.decode(response.body);
|
||||||
|
|
||||||
var categoriesJSON = responseMap['categories'];
|
var categoriesJSON = responseMap['categories'];
|
||||||
|
@ -45,14 +35,12 @@ Future<List<Category>> getCategories() async {
|
||||||
|
|
||||||
// print(categoriesJSON['11']); // prints "Banana"
|
// print(categoriesJSON['11']); // prints "Banana"
|
||||||
|
|
||||||
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()]);
|
||||||
categories.add(new Category(
|
categories.add(categoriesJSON[i.toString()]);
|
||||||
name: categoriesJSON[i.toString()], index: i.toString()));
|
|
||||||
// print(categories.last);
|
// print(categories.last);
|
||||||
i++;
|
i++;
|
||||||
} else {
|
} else {
|
||||||
|
@ -70,9 +58,11 @@ Future<List<Category>> getCategories() async {
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// print(categories[10].name.toString()); // prints "Banana"
|
// print(categories[10].name.toString()); // prints "Banana"
|
||||||
return categories;
|
|
||||||
|
return categories; // categories is List<Category>
|
||||||
|
// Category is defined at the top^^
|
||||||
} else {
|
} else {
|
||||||
// not successful
|
// not successful
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,109 +1,85 @@
|
||||||
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 {
|
||||||
|
Organisation(
|
||||||
|
this.id,
|
||||||
|
this.name,
|
||||||
|
this.postcode,
|
||||||
|
this.streetName,
|
||||||
|
this.town,
|
||||||
|
);
|
||||||
|
|
||||||
var id = 0;
|
var id = 0;
|
||||||
var name = "";
|
var name = "";
|
||||||
var postcode = "";
|
var postcode = "";
|
||||||
var streetName = ""; //street_name
|
var streetName = ""; //street_name
|
||||||
var town = "";
|
var town = "";
|
||||||
|
|
||||||
Organisation(int id, String name, String postcode, String streetName, String town) {
|
|
||||||
this.id = id;
|
|
||||||
this.name = name;
|
|
||||||
this.postcode = postcode;
|
|
||||||
this.streetName = streetName; //street_name
|
|
||||||
this.town = town;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Organisation> jsonToOrganisations(String json) {
|
class Organisations {
|
||||||
Map decoded = jsonDecode(json);
|
List<Organisation> getTestData() {
|
||||||
// print(decoded);
|
var numItems = 10;
|
||||||
|
var itemsList = new List<Organisation>();
|
||||||
|
|
||||||
List<dynamic> validated = decoded['validated'];
|
for (int i = 0; i < numItems; i++) {
|
||||||
// Map organisation = validated[0];
|
itemsList.add(new Organisation(i, "Payee " + (i + 1).toString(),
|
||||||
//
|
"tee hee hee", "yeet street", "Robloxia"));
|
||||||
// print("");
|
}
|
||||||
// print("Response:");
|
|
||||||
// for (var i = 0; i < validated.length; i++) {
|
|
||||||
// print(validated[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
List<Map> organisationsMaps = new List<Map>();
|
return itemsList;
|
||||||
|
}
|
||||||
|
|
||||||
validated.forEach((element) => organisationsMaps.add(element));
|
List<Organisation> _jsonToOrganisations(String json) {
|
||||||
|
Map decoded = jsonDecode(json);
|
||||||
|
List<dynamic> validated = decoded['validated'];
|
||||||
|
List<Map> organisationsMaps = new List<Map>();
|
||||||
|
validated.forEach((element) => organisationsMaps.add(element));
|
||||||
|
List<Organisation> organisations = new List<Organisation>();
|
||||||
|
|
||||||
// print("");
|
for (var i = 0; i < organisationsMaps.length; i++) {
|
||||||
// print("organisationsMaps:");
|
final params = organisationsMaps[i].values.toList();
|
||||||
// print(organisationsMaps);
|
|
||||||
|
|
||||||
List<Organisation> organisations = new List<Organisation>();
|
var newOrganisation = new Organisation(
|
||||||
|
params[0].toInt(),
|
||||||
// organisationsMaps[0].forEach((k,v) => print('${k}: ${v}'));
|
params[1].toString(),
|
||||||
|
params[2].toString(), // oof
|
||||||
|
params[3].toString(), // this could be improved...
|
||||||
|
params[4].toString(),
|
||||||
|
);
|
||||||
|
|
||||||
for (var i = 0; i < organisationsMaps.length; i++) {
|
organisations.add(newOrganisation);
|
||||||
final params = organisationsMaps[i].values.toList();
|
}
|
||||||
|
|
||||||
var newOrganisation = new Organisation(
|
return organisations;
|
||||||
params[0].toInt(),
|
}
|
||||||
params[1].toString(),
|
|
||||||
params[2].toString(), // oof
|
Future<List<Organisation>> findOrganisations(String search) async {
|
||||||
params[3].toString(), // this could be improved...
|
final url = "https://dev.localspend.co.uk/api/search";
|
||||||
params[4].toString(),
|
var token;
|
||||||
|
|
||||||
|
await getToken().then((result) {
|
||||||
|
token = result;
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, String> body = {
|
||||||
|
"search_name": search,
|
||||||
|
"session_key": token,
|
||||||
|
};
|
||||||
|
|
||||||
|
final response = await http.post(
|
||||||
|
url,
|
||||||
|
body: json.encode(body),
|
||||||
);
|
);
|
||||||
|
|
||||||
organisations.add(newOrganisation);
|
if (response.statusCode == 200) {
|
||||||
|
//request successful
|
||||||
|
return _jsonToOrganisations(response.body);
|
||||||
|
} else {
|
||||||
|
// not successful
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the reason some organizations do not show up is because they are not all validated
|
|
||||||
// option to 'show unvalidated' should be added along with maybe a settings section
|
|
||||||
//
|
|
||||||
// print("");
|
|
||||||
// print("Local:");
|
|
||||||
// for (var i = 0; i < organisations.length; i++)
|
|
||||||
// {
|
|
||||||
// print(organisations[i].name);
|
|
||||||
// }
|
|
||||||
// print("");
|
|
||||||
|
|
||||||
return organisations;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Organisation>> findOrganisations(String search) async {
|
|
||||||
final url = "https://dev.peartrade.org/api/search";
|
|
||||||
var token;
|
|
||||||
|
|
||||||
await getToken().then((result) {
|
|
||||||
token = result;
|
|
||||||
});
|
|
||||||
|
|
||||||
Map<String, String> body = {
|
|
||||||
"search_name":search,
|
|
||||||
"session_key":token,
|
|
||||||
};
|
|
||||||
|
|
||||||
final response = await http.post (
|
|
||||||
url,
|
|
||||||
body: json.encode(body),
|
|
||||||
);
|
|
||||||
|
|
||||||
// print(response.body);
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
//request successful
|
|
||||||
return jsonToOrganisations(response.body);
|
|
||||||
} else {
|
|
||||||
// not successful
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class OrganizationController extends TextEditingController {
|
|
||||||
Organisation organisation;
|
|
||||||
}
|
|
270
lib/common/apifunctions/get_graph_data.dart
Normal file
270
lib/common/apifunctions/get_graph_data.dart
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
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 {
|
||||||
|
GraphData(
|
||||||
|
this.chartType,
|
||||||
|
);
|
||||||
|
|
||||||
|
var chartType;
|
||||||
|
List<charts.Series<dynamic, DateTime>> graph;
|
||||||
|
|
||||||
|
List<TimeSeriesSpend> cachedData;
|
||||||
|
bool loaded = false;
|
||||||
|
|
||||||
|
Future<List<charts.Series<dynamic, DateTime>>> setGraphData() async {
|
||||||
|
if (loaded) {
|
||||||
|
this.graph = [
|
||||||
|
new charts.Series<TimeSeriesSpend, DateTime>(
|
||||||
|
id: 'Spend',
|
||||||
|
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
|
||||||
|
domainFn: (TimeSeriesSpend spend, _) => spend.time,
|
||||||
|
measureFn: (TimeSeriesSpend spend, _) => spend.spend,
|
||||||
|
data: cachedData,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final url = "https://dev.localspend.co.uk/api/v1/customer/graphs";
|
||||||
|
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
Map<String, String> body = {
|
||||||
|
'graph': this.chartType,
|
||||||
|
'session_key': preferences.get('LastToken'),
|
||||||
|
};
|
||||||
|
|
||||||
|
final response = await http.post(
|
||||||
|
url,
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final responseJson = jsonDecode(response.body);
|
||||||
|
final List<dynamic> labels = responseJson['graph']['labels'];
|
||||||
|
final List<dynamic> data = responseJson['graph']['data'];
|
||||||
|
|
||||||
|
List<TimeSeriesSpend> timeSeriesSpendList = new List<TimeSeriesSpend>();
|
||||||
|
|
||||||
|
for (int i = 0; i < labels.length; i++) {
|
||||||
|
timeSeriesSpendList.add(
|
||||||
|
new TimeSeriesSpend(data[i] * 1.00, DateTime.parse(labels[i])));
|
||||||
|
// print(timeSeriesSpendList[i].time.toString() + " : " + timeSeriesSpendList[i].spend.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedData = timeSeriesSpendList;
|
||||||
|
loaded = true;
|
||||||
|
|
||||||
|
this.graph = [
|
||||||
|
new charts.Series<TimeSeriesSpend, DateTime>(
|
||||||
|
id: 'Spend',
|
||||||
|
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
|
||||||
|
domainFn: (TimeSeriesSpend spend, _) => spend.time,
|
||||||
|
measureFn: (TimeSeriesSpend spend, _) => spend.spend,
|
||||||
|
data: timeSeriesSpendList,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
return this.graph;
|
||||||
|
} else {
|
||||||
|
final errorMessage = json.decode(response.body)['message'];
|
||||||
|
print("Error: " + errorMessage);
|
||||||
|
|
||||||
|
this.graph = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<charts.Series<dynamic, DateTime>>> getGraphData() async {
|
||||||
|
if (loaded == true) {
|
||||||
|
return [
|
||||||
|
new charts.Series<TimeSeriesSpend, DateTime>(
|
||||||
|
id: 'Spend',
|
||||||
|
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
|
||||||
|
domainFn: (TimeSeriesSpend spend, _) => spend.time,
|
||||||
|
measureFn: (TimeSeriesSpend spend, _) => spend.spend,
|
||||||
|
data: cachedData,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
final url = "https://dev.localspend.co.uk/api/v1/customer/graphs";
|
||||||
|
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
Map<String, String> body = {
|
||||||
|
'graph': this.chartType,
|
||||||
|
'session_key': preferences.get('LastToken'),
|
||||||
|
};
|
||||||
|
|
||||||
|
final response = await http.post(
|
||||||
|
url,
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final responseJson = jsonDecode(response.body);
|
||||||
|
final List<dynamic> labels = responseJson['graph']['labels'];
|
||||||
|
final List<dynamic> data = responseJson['graph']['data'];
|
||||||
|
|
||||||
|
List<TimeSeriesSpend> timeSeriesSpendList = new List<TimeSeriesSpend>();
|
||||||
|
|
||||||
|
for (int i = 0; i < labels.length; i++) {
|
||||||
|
timeSeriesSpendList.add(
|
||||||
|
new TimeSeriesSpend(data[i] * 1.00, DateTime.parse(labels[i])));
|
||||||
|
// print(timeSeriesSpendList[i].time.toString() + " : " + timeSeriesSpendList[i].spend.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedData = timeSeriesSpendList;
|
||||||
|
loaded = true;
|
||||||
|
|
||||||
|
return [
|
||||||
|
new charts.Series<TimeSeriesSpend, DateTime>(
|
||||||
|
id: 'Spend',
|
||||||
|
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
|
||||||
|
domainFn: (TimeSeriesSpend spend, _) => spend.time,
|
||||||
|
measureFn: (TimeSeriesSpend spend, _) => spend.spend,
|
||||||
|
data: timeSeriesSpendList,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
final errorMessage = json.decode(response.body)['message'];
|
||||||
|
print("Error: " + errorMessage);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TimeSeriesSpend {
|
||||||
|
TimeSeriesSpend(this.spend, this.time);
|
||||||
|
|
||||||
|
final DateTime time;
|
||||||
|
final double spend;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TimeSeriesCustomersOrSales {
|
||||||
|
TimeSeriesCustomersOrSales(this.numberOfStuff, this.time);
|
||||||
|
|
||||||
|
final DateTime time;
|
||||||
|
final double numberOfStuff;
|
||||||
|
}
|
87
lib/common/apifunctions/get_map_data.dart
Normal file
87
lib/common/apifunctions/get_map_data.dart
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,49 +4,72 @@ import 'dart:convert';
|
||||||
import 'package:flutter/material.dart';
|
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/save_current_login.dart';
|
import 'package:local_spend/common/functions/save_current_login.dart';
|
||||||
import 'package:local_spend/common/functions/show_dialog_single_button.dart';
|
|
||||||
import 'package:local_spend/model/json/login_model.dart';
|
import 'package:local_spend/model/json/login_model.dart';
|
||||||
|
|
||||||
|
Future<void> _incorrectDialog(BuildContext context, bool isLoginWrong) async {
|
||||||
|
return showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AnimatedContainer(
|
||||||
|
duration: Duration(seconds: 2),
|
||||||
|
child: AlertDialog(
|
||||||
|
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>[
|
||||||
|
FlatButton(
|
||||||
|
child: Text('OK'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<LoginModel> requestLoginAPI(
|
Future<LoginModel> requestLoginAPI(
|
||||||
BuildContext context, String email, String password) async {
|
BuildContext context, String email, String password) async {
|
||||||
//var apiUrl = ConfigWrapper.of(context).apiKey;
|
final url = "https://dev.localspend.co.uk/api/login";
|
||||||
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 {
|
||||||
|
final response = await http
|
||||||
|
.post(
|
||||||
|
url,
|
||||||
|
body: json.encode(body),
|
||||||
|
)
|
||||||
|
.timeout(Duration(seconds: 5));
|
||||||
|
|
||||||
final response = await http.post(
|
if (response.statusCode == 200) {
|
||||||
url,
|
final responseJson = json.decode(response.body);
|
||||||
body: json.encode(body),
|
|
||||||
);
|
|
||||||
|
|
||||||
// debugPrint(response.body);
|
saveCurrentLogin(responseJson, body["email"]);
|
||||||
|
await Navigator.of(context).pushReplacementNamed('/HomePage');
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
return LoginModel.fromJson(responseJson);
|
||||||
final responseJson = json.decode(response.body);
|
} else {
|
||||||
var user = new LoginModel.fromJson(responseJson);
|
final responseJson = json.decode(response.body);
|
||||||
|
|
||||||
saveCurrentLogin(responseJson, body["email"]);
|
saveCurrentLogin(responseJson, body["email"]);
|
||||||
Navigator.of(context).pushReplacementNamed('/HomePage');
|
|
||||||
|
|
||||||
return LoginModel.fromJson(responseJson);
|
await _incorrectDialog(context, true);
|
||||||
} else {
|
|
||||||
// debugPrint("Invalid, either credentials are wrong or server is down");
|
|
||||||
|
|
||||||
final responseJson = json.decode(response.body);
|
return null;
|
||||||
|
}
|
||||||
saveCurrentLogin(responseJson, body["email"]);
|
} on TimeoutException catch (_) {
|
||||||
|
await _incorrectDialog(context, false);
|
||||||
showDialogSingleButton(
|
} catch (error) {
|
||||||
context,
|
debugPrint(error.toString());
|
||||||
"Unable to Login",
|
await _incorrectDialog(context, false);
|
||||||
"You may have supplied an invalid 'Email' / 'Password' combination. Please try again or email an administrator.",
|
|
||||||
"OK");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<LoginModel> requestLogoutAPI(BuildContext context) async {
|
Future<bool> requestLogoutAPI() async {
|
||||||
final url = "https://dev.peartrade.org/api/logout";
|
saveLogout();
|
||||||
|
|
||||||
|
final url = "https://dev.localspend.co.uk/api/logout";
|
||||||
|
|
||||||
var token;
|
var token;
|
||||||
|
|
||||||
|
@ -17,23 +17,13 @@ Future<LoginModel> requestLogoutAPI(BuildContext context) async {
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<String, String> body = {
|
Map<String, String> body = {
|
||||||
"Token":token,
|
"Token": token,
|
||||||
};
|
};
|
||||||
|
|
||||||
final response = await http.post(
|
await http.post(
|
||||||
url,
|
url,
|
||||||
body: json.encode(body),
|
body: json.encode(body),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
return true;
|
||||||
// debugPrint("Logout successful: " + response.body);
|
|
||||||
|
|
||||||
saveLogout();
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
// debugPrint("Logout unsuccessful: " + response.body);
|
|
||||||
|
|
||||||
saveLogout();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,12 @@ 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.peartrade.org/api/upload";
|
final url = "https://dev.localspend.co.uk/api/upload";
|
||||||
|
|
||||||
SharedPreferences preferences = await SharedPreferences.getInstance();
|
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
Map<String, String> body = {
|
Map<String, String> body = {
|
||||||
'transaction_type' : "3",
|
'transaction_type': "3",
|
||||||
'transaction_value': receipt.amount,
|
'transaction_value': receipt.amount,
|
||||||
'purchase_time': receipt.time,
|
'purchase_time': receipt.time,
|
||||||
'category': receipt.category,
|
'category': receipt.category,
|
||||||
|
@ -37,14 +37,12 @@ Future<LoginModel> submitReceiptAPI(
|
||||||
'recurring': receipt.recurring,
|
'recurring': receipt.recurring,
|
||||||
'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'),
|
||||||
};
|
};
|
||||||
|
|
||||||
// debugPrint('$body');
|
// debugPrint('$body');
|
||||||
// debugPrint(json.encode(body));
|
debugPrint(json.encode(body));
|
||||||
|
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
url,
|
url,
|
||||||
|
@ -62,13 +60,11 @@ 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",
|
||||||
|
|
|
@ -103,7 +103,8 @@ class AboutListTile extends StatelessWidget {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: icon,
|
leading: icon,
|
||||||
title: child ??
|
title: child ??
|
||||||
Text(MaterialLocalizations.of(context).aboutListTileTitle(applicationName ?? _defaultApplicationName(context))),
|
Text(MaterialLocalizations.of(context).aboutListTileTitle(
|
||||||
|
applicationName ?? _defaultApplicationName(context))),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showAboutDialog(
|
showAboutDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -178,13 +179,14 @@ void showLicensePage({
|
||||||
String applicationLegalese,
|
String applicationLegalese,
|
||||||
}) {
|
}) {
|
||||||
assert(context != null);
|
assert(context != null);
|
||||||
Navigator.push(context, MaterialPageRoute<void>(
|
Navigator.push(
|
||||||
builder: (BuildContext context) => LicensePage(
|
context,
|
||||||
applicationName: applicationName,
|
MaterialPageRoute<void>(
|
||||||
applicationVersion: applicationVersion,
|
builder: (BuildContext context) => LicensePage(
|
||||||
applicationLegalese: applicationLegalese,
|
applicationName: applicationName,
|
||||||
)
|
applicationVersion: applicationVersion,
|
||||||
));
|
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,
|
||||||
|
@ -256,19 +258,27 @@ 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 = applicationVersion ?? _defaultApplicationVersion(context);
|
final String version =
|
||||||
|
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(IconTheme(data: const IconThemeData(size: 45.0), child: icon));
|
body.add(
|
||||||
|
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>[
|
||||||
Text(name, style: Theme.of(context).textTheme.title),
|
Container(
|
||||||
|
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 ?? '', style: Theme.of(context).textTheme.caption),
|
Text(applicationLegalese ?? '',
|
||||||
|
style: Theme.of(context).textTheme.caption),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -279,15 +289,15 @@ class AboutDialog extends StatelessWidget {
|
||||||
children: body,
|
children: body,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
if (children != null)
|
if (children != null) body.addAll(children);
|
||||||
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: Text(MaterialLocalizations.of(context).viewLicensesButtonLabel),
|
child:
|
||||||
|
Text(MaterialLocalizations.of(context).viewLicensesButtonLabel),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showLicensePage(
|
showLicensePage(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -372,7 +382,7 @@ class _LicensePageState extends State<LicensePage> {
|
||||||
int debugFlowId = -1;
|
int debugFlowId = -1;
|
||||||
assert(() {
|
assert(() {
|
||||||
final dev.Flow flow = dev.Flow.begin();
|
final dev.Flow flow = dev.Flow.begin();
|
||||||
dev.Timeline.timeSync('_initLicenses()', () { }, flow: flow);
|
dev.Timeline.timeSync('_initLicenses()', () {}, flow: flow);
|
||||||
debugFlowId = flow.id;
|
debugFlowId = flow.id;
|
||||||
return true;
|
return true;
|
||||||
}());
|
}());
|
||||||
|
@ -381,11 +391,12 @@ class _LicensePageState extends State<LicensePage> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert(() {
|
assert(() {
|
||||||
dev.Timeline.timeSync('_initLicenses()', () { }, flow: dev.Flow.step(debugFlowId));
|
dev.Timeline.timeSync('_initLicenses()', () {},
|
||||||
|
flow: dev.Flow.step(debugFlowId));
|
||||||
return true;
|
return true;
|
||||||
}());
|
}());
|
||||||
final List<LicenseParagraph> paragraphs =
|
final List<LicenseParagraph> paragraphs =
|
||||||
await SchedulerBinding.instance.scheduleTask<List<LicenseParagraph>>(
|
await SchedulerBinding.instance.scheduleTask<List<LicenseParagraph>>(
|
||||||
license.paragraphs.toList,
|
license.paragraphs.toList,
|
||||||
Priority.animation,
|
Priority.animation,
|
||||||
debugLabel: 'License',
|
debugLabel: 'License',
|
||||||
|
@ -400,8 +411,7 @@ 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),
|
||||||
|
@ -421,7 +431,8 @@ class _LicensePageState extends State<LicensePage> {
|
||||||
} else {
|
} else {
|
||||||
assert(paragraph.indent >= 0);
|
assert(paragraph.indent >= 0);
|
||||||
_licenses.add(Padding(
|
_licenses.add(Padding(
|
||||||
padding: EdgeInsetsDirectional.only(top: 8.0, start: 16.0 * paragraph.indent),
|
padding: EdgeInsetsDirectional.only(
|
||||||
|
top: 8.0, start: 16.0 * paragraph.indent),
|
||||||
child: Text(paragraph.text),
|
child: Text(paragraph.text),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -432,7 +443,8 @@ class _LicensePageState extends State<LicensePage> {
|
||||||
_loaded = true;
|
_loaded = true;
|
||||||
});
|
});
|
||||||
assert(() {
|
assert(() {
|
||||||
dev.Timeline.timeSync('Build scheduled', () { }, flow: dev.Flow.end(debugFlowId));
|
dev.Timeline.timeSync('Build scheduled', () {},
|
||||||
|
flow: dev.Flow.end(debugFlowId));
|
||||||
return true;
|
return true;
|
||||||
}());
|
}());
|
||||||
}
|
}
|
||||||
|
@ -440,16 +452,27 @@ class _LicensePageState extends State<LicensePage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
assert(debugCheckHasMaterialLocalizations(context));
|
assert(debugCheckHasMaterialLocalizations(context));
|
||||||
final String name = widget.applicationName ?? _defaultApplicationName(context);
|
final String name =
|
||||||
final String version = widget.applicationVersion ?? _defaultApplicationVersion(context);
|
widget.applicationName ?? _defaultApplicationName(context);
|
||||||
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
|
final String version =
|
||||||
|
widget.applicationVersion ?? _defaultApplicationVersion(context);
|
||||||
|
final MaterialLocalizations localizations =
|
||||||
|
MaterialLocalizations.of(context);
|
||||||
final List<Widget> contents = <Widget>[
|
final List<Widget> contents = <Widget>[
|
||||||
Text(name, style: Theme.of(context).textTheme.headline, textAlign: TextAlign.center),
|
Text(name,
|
||||||
Text(version, style: Theme.of(context).textTheme.body1, textAlign: TextAlign.center),
|
style: Theme.of(context).textTheme.headline,
|
||||||
|
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 ?? '', style: Theme.of(context).textTheme.caption, textAlign: TextAlign.center),
|
Text(widget.applicationLegalese ?? '',
|
||||||
|
style: Theme.of(context).textTheme.caption,
|
||||||
|
textAlign: TextAlign.center),
|
||||||
Container(height: 18.0),
|
Container(height: 18.0),
|
||||||
Text('Powered by Flutter', style: Theme.of(context).textTheme.body1, textAlign: TextAlign.center),
|
Text('Powered by Flutter',
|
||||||
|
style: Theme.of(context).textTheme.body1,
|
||||||
|
textAlign: TextAlign.center),
|
||||||
Container(height: 24.0),
|
Container(height: 24.0),
|
||||||
];
|
];
|
||||||
contents.addAll(_licenses);
|
contents.addAll(_licenses);
|
||||||
|
@ -476,7 +499,8 @@ class _LicensePageState extends State<LicensePage> {
|
||||||
bottom: false,
|
bottom: false,
|
||||||
child: Scrollbar(
|
child: Scrollbar(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 12.0),
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 8.0, vertical: 12.0),
|
||||||
children: contents,
|
children: contents,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -495,7 +519,8 @@ 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 ?? Platform.resolvedExecutable.split(Platform.pathSeparator).last;
|
return ancestorTitle?.title ??
|
||||||
|
Platform.resolvedExecutable.split(Platform.pathSeparator).last;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _defaultApplicationVersion(BuildContext context) {
|
String _defaultApplicationVersion(BuildContext context) {
|
||||||
|
|
|
@ -2,8 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
final TextEditingController _feedbackController = TextEditingController();
|
final TextEditingController _feedbackController = TextEditingController();
|
||||||
|
|
||||||
void feedback(
|
void feedback(BuildContext context) {
|
||||||
BuildContext context) {
|
|
||||||
// flutter defined function
|
// flutter defined function
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -15,13 +14,11 @@ void feedback(
|
||||||
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(
|
||||||
hintText: 'Enter your feedback here',
|
hintText: 'Enter your feedback here',
|
||||||
|
@ -41,7 +38,7 @@ void feedback(
|
||||||
),
|
),
|
||||||
new Container(
|
new Container(
|
||||||
padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
|
padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
|
||||||
child : new FlatButton(
|
child: new FlatButton(
|
||||||
child: new Text("Submit"),
|
child: new Text("Submit"),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
submit(_feedbackController.text);
|
submit(_feedbackController.text);
|
||||||
|
@ -58,4 +55,4 @@ void feedback(
|
||||||
|
|
||||||
void submit(String feedback) {
|
void submit(String feedback) {
|
||||||
debugPrint(feedback);
|
debugPrint(feedback);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
getToken() async {
|
Future<String> getToken() async {
|
||||||
SharedPreferences preferences = await SharedPreferences.getInstance();
|
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
String getToken = preferences.getString("LastToken");
|
String getToken = preferences.getString("LastToken");
|
||||||
|
|
|
@ -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';
|
||||||
|
|
||||||
logout(context) {
|
void logout(context) {
|
||||||
requestLogoutAPI(context);
|
_clearLoginDetails().then((_) {
|
||||||
Navigator.of(context).pushReplacementNamed('/LoginPage');
|
requestLogoutAPI();
|
||||||
_clearLoginDetails();
|
Navigator.of(context).pushReplacementNamed('/LoginPage');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_clearLoginDetails() async {
|
Future<void> _clearLoginDetails() async {
|
||||||
SharedPreferences preferences = await SharedPreferences.getInstance();
|
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
preferences.setString('username', "");
|
await preferences.setString('username', "");
|
||||||
preferences.setString('password', "");
|
await preferences.setString('password', "");
|
||||||
print("details cleared");
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -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';
|
||||||
|
|
||||||
saveCurrentLogin(Map responseJson, loginEmail) async {
|
void saveCurrentLogin(Map responseJson, loginEmail) async {
|
||||||
SharedPreferences preferences = await SharedPreferences.getInstance();
|
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
var user;
|
var user;
|
||||||
|
@ -13,14 +13,17 @@ 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)
|
var email = (loginEmail != null) ? loginEmail : "";
|
||||||
? loginEmail
|
var userType = (responseJson != null && responseJson.isNotEmpty)
|
||||||
|
? 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.length > 0) ? token : "");
|
'LastToken', (token != null && token.isNotEmpty) ? 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 : "");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
saveLogout() async {
|
void saveLogout() async {
|
||||||
SharedPreferences preferences = await SharedPreferences.getInstance();
|
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
await preferences.setString('LastUser', "");
|
await preferences.setString('LastUser', "");
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
void showDialogTwoButtons(
|
void showDialogTwoButtons(BuildContext context, String title, String message,
|
||||||
BuildContext context, String title, String message, String buttonLabel1, String buttonLabel2, Function action) {
|
String buttonLabel1, String buttonLabel2, Function action) {
|
||||||
// flutter defined function
|
// flutter defined function
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
@ -4,6 +4,24 @@ 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;
|
||||||
|
@ -18,23 +36,6 @@ 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
|
||||||
|
|
40
lib/common/widgets/animatedGradientButton.dart
Normal file
40
lib/common/widgets/animatedGradientButton.dart
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
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])),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,9 +4,6 @@ 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.
|
||||||
|
@ -18,6 +15,8 @@ class DonutAutoLabelChart extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final List<charts.Series> seriesList;
|
||||||
|
final bool animate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -67,8 +66,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);
|
|
||||||
}
|
|
||||||
|
|
18
lib/common/widgets/charts/chart_builder.dart
Normal file
18
lib/common/widgets/charts/chart_builder.dart
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:local_spend/common/widgets/charts/time_series_simple.dart';
|
||||||
|
|
||||||
|
class TimeSeries extends StatelessWidget {
|
||||||
|
TimeSeries({
|
||||||
|
this.chartDataName,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String chartDataName;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return new Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 7.5, vertical: 7.5),
|
||||||
|
child: SimpleTimeSeriesChart.withSampleData(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,9 +3,6 @@ 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.
|
||||||
|
@ -16,6 +13,8 @@ class DonutPieChart extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final List<charts.Series> seriesList;
|
||||||
|
final bool animate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -46,11 +45,10 @@ 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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,9 +3,6 @@ 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() {
|
||||||
|
@ -16,6 +13,8 @@ class GroupedBarChart extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final List<charts.Series> seriesList;
|
||||||
|
final bool animate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -74,8 +73,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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,9 +4,6 @@ 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.
|
||||||
|
@ -18,6 +15,8 @@ class NumericComboLineBarChart extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final List<charts.Series> seriesList;
|
||||||
|
final bool animate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -29,7 +28,7 @@ class NumericComboLineBarChart extends StatelessWidget {
|
||||||
// Custom renderer configuration for the bar series.
|
// Custom renderer configuration for the bar series.
|
||||||
customSeriesRenderers: [
|
customSeriesRenderers: [
|
||||||
new charts.BarRendererConfig(
|
new charts.BarRendererConfig(
|
||||||
// ID used to link series to this renderer.
|
// ID used to link series to this renderer.
|
||||||
customRendererId: 'customBar')
|
customRendererId: 'customBar')
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -65,7 +64,7 @@ class NumericComboLineBarChart extends StatelessWidget {
|
||||||
measureFn: (LinearSales sales, _) => sales.sales,
|
measureFn: (LinearSales sales, _) => sales.sales,
|
||||||
data: desktopSalesData,
|
data: desktopSalesData,
|
||||||
)
|
)
|
||||||
// Configure our custom bar renderer for this series.
|
// Configure our custom bar renderer for this series.
|
||||||
..setAttribute(charts.rendererIdKey, 'customBar'),
|
..setAttribute(charts.rendererIdKey, 'customBar'),
|
||||||
new charts.Series<LinearSales, int>(
|
new charts.Series<LinearSales, int>(
|
||||||
id: 'Tablet',
|
id: 'Tablet',
|
||||||
|
@ -74,7 +73,7 @@ class NumericComboLineBarChart extends StatelessWidget {
|
||||||
measureFn: (LinearSales sales, _) => sales.sales,
|
measureFn: (LinearSales sales, _) => sales.sales,
|
||||||
data: tableSalesData,
|
data: tableSalesData,
|
||||||
)
|
)
|
||||||
// Configure our custom bar renderer for this series.
|
// Configure our custom bar renderer for this series.
|
||||||
..setAttribute(charts.rendererIdKey, 'customBar'),
|
..setAttribute(charts.rendererIdKey, 'customBar'),
|
||||||
new charts.Series<LinearSales, int>(
|
new charts.Series<LinearSales, int>(
|
||||||
id: 'Mobile',
|
id: 'Mobile',
|
||||||
|
@ -88,8 +87,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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,9 +3,6 @@ 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.
|
||||||
|
@ -17,6 +14,8 @@ class PieOutsideLabelChart extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final List<charts.Series> seriesList;
|
||||||
|
final bool animate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -62,8 +61,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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,9 +8,6 @@ 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.
|
||||||
|
@ -22,6 +19,8 @@ class BucketingAxisScatterPlotChart extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final List<charts.Series> seriesList;
|
||||||
|
final bool animate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -77,7 +76,7 @@ class BucketingAxisScatterPlotChart extends StatelessWidget {
|
||||||
new charts.Series<LinearSales, int>(
|
new charts.Series<LinearSales, int>(
|
||||||
id: 'Cheese',
|
id: 'Cheese',
|
||||||
colorFn: (LinearSales sales, _) =>
|
colorFn: (LinearSales sales, _) =>
|
||||||
charts.MaterialPalette.blue.shadeDefault,
|
charts.MaterialPalette.blue.shadeDefault,
|
||||||
domainFn: (LinearSales sales, _) => sales.year,
|
domainFn: (LinearSales sales, _) => sales.year,
|
||||||
measureFn: (LinearSales sales, _) => sales.revenueShare,
|
measureFn: (LinearSales sales, _) => sales.revenueShare,
|
||||||
radiusPxFn: (LinearSales sales, _) => sales.radius,
|
radiusPxFn: (LinearSales sales, _) => sales.radius,
|
||||||
|
@ -85,7 +84,7 @@ class BucketingAxisScatterPlotChart extends StatelessWidget {
|
||||||
new charts.Series<LinearSales, int>(
|
new charts.Series<LinearSales, int>(
|
||||||
id: 'Carrots',
|
id: 'Carrots',
|
||||||
colorFn: (LinearSales sales, _) =>
|
colorFn: (LinearSales sales, _) =>
|
||||||
charts.MaterialPalette.red.shadeDefault,
|
charts.MaterialPalette.red.shadeDefault,
|
||||||
domainFn: (LinearSales sales, _) => sales.year,
|
domainFn: (LinearSales sales, _) => sales.year,
|
||||||
measureFn: (LinearSales sales, _) => sales.revenueShare,
|
measureFn: (LinearSales sales, _) => sales.revenueShare,
|
||||||
radiusPxFn: (LinearSales sales, _) => sales.radius,
|
radiusPxFn: (LinearSales sales, _) => sales.radius,
|
||||||
|
@ -93,7 +92,7 @@ class BucketingAxisScatterPlotChart extends StatelessWidget {
|
||||||
new charts.Series<LinearSales, int>(
|
new charts.Series<LinearSales, int>(
|
||||||
id: 'Cucumbers',
|
id: 'Cucumbers',
|
||||||
colorFn: (LinearSales sales, _) =>
|
colorFn: (LinearSales sales, _) =>
|
||||||
charts.MaterialPalette.green.shadeDefault,
|
charts.MaterialPalette.green.shadeDefault,
|
||||||
domainFn: (LinearSales sales, _) => sales.year,
|
domainFn: (LinearSales sales, _) => sales.year,
|
||||||
measureFn: (LinearSales sales, _) => sales.revenueShare,
|
measureFn: (LinearSales sales, _) => sales.revenueShare,
|
||||||
radiusPxFn: (LinearSales sales, _) => sales.radius,
|
radiusPxFn: (LinearSales sales, _) => sales.radius,
|
||||||
|
@ -101,7 +100,7 @@ class BucketingAxisScatterPlotChart extends StatelessWidget {
|
||||||
new charts.Series<LinearSales, int>(
|
new charts.Series<LinearSales, int>(
|
||||||
id: 'Crayons',
|
id: 'Crayons',
|
||||||
colorFn: (LinearSales sales, _) =>
|
colorFn: (LinearSales sales, _) =>
|
||||||
charts.MaterialPalette.purple.shadeDefault,
|
charts.MaterialPalette.purple.shadeDefault,
|
||||||
domainFn: (LinearSales sales, _) => sales.year,
|
domainFn: (LinearSales sales, _) => sales.year,
|
||||||
measureFn: (LinearSales sales, _) => sales.revenueShare,
|
measureFn: (LinearSales sales, _) => sales.revenueShare,
|
||||||
radiusPxFn: (LinearSales sales, _) => sales.radius,
|
radiusPxFn: (LinearSales sales, _) => sales.radius,
|
||||||
|
@ -109,7 +108,7 @@ class BucketingAxisScatterPlotChart extends StatelessWidget {
|
||||||
new charts.Series<LinearSales, int>(
|
new charts.Series<LinearSales, int>(
|
||||||
id: 'Celery',
|
id: 'Celery',
|
||||||
colorFn: (LinearSales sales, _) =>
|
colorFn: (LinearSales sales, _) =>
|
||||||
charts.MaterialPalette.indigo.shadeDefault,
|
charts.MaterialPalette.indigo.shadeDefault,
|
||||||
domainFn: (LinearSales sales, _) => sales.year,
|
domainFn: (LinearSales sales, _) => sales.year,
|
||||||
measureFn: (LinearSales sales, _) => sales.revenueShare,
|
measureFn: (LinearSales sales, _) => sales.revenueShare,
|
||||||
radiusPxFn: (LinearSales sales, _) => sales.radius,
|
radiusPxFn: (LinearSales sales, _) => sales.radius,
|
||||||
|
@ -117,7 +116,7 @@ class BucketingAxisScatterPlotChart extends StatelessWidget {
|
||||||
new charts.Series<LinearSales, int>(
|
new charts.Series<LinearSales, int>(
|
||||||
id: 'Cauliflower',
|
id: 'Cauliflower',
|
||||||
colorFn: (LinearSales sales, _) =>
|
colorFn: (LinearSales sales, _) =>
|
||||||
charts.MaterialPalette.gray.shadeDefault,
|
charts.MaterialPalette.gray.shadeDefault,
|
||||||
domainFn: (LinearSales sales, _) => sales.year,
|
domainFn: (LinearSales sales, _) => sales.year,
|
||||||
measureFn: (LinearSales sales, _) => sales.revenueShare,
|
measureFn: (LinearSales sales, _) => sales.revenueShare,
|
||||||
radiusPxFn: (LinearSales sales, _) => sales.radius,
|
radiusPxFn: (LinearSales sales, _) => sales.radius,
|
||||||
|
@ -128,9 +127,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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,9 +11,6 @@ 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() {
|
||||||
|
@ -24,6 +21,8 @@ class LegendWithMeasures extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final List<charts.Series> seriesList;
|
||||||
|
final bool animate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -122,8 +121,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);
|
|
||||||
}
|
|
||||||
|
|
59
lib/common/widgets/charts/time_series_simple.dart
Normal file
59
lib/common/widgets/charts/time_series_simple.dart
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/// Timeseries chart example
|
||||||
|
import 'package:charts_flutter/flutter.dart' as charts;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SimpleTimeSeriesChart extends StatelessWidget {
|
||||||
|
SimpleTimeSeriesChart(this.seriesList, {this.animate});
|
||||||
|
|
||||||
|
/// Creates a [TimeSeriesChart] with sample data and no transition.
|
||||||
|
factory SimpleTimeSeriesChart.withSampleData() {
|
||||||
|
return new SimpleTimeSeriesChart(
|
||||||
|
_createSampleData(),
|
||||||
|
// Disable animations for image tests.
|
||||||
|
animate: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<charts.Series> seriesList;
|
||||||
|
final bool animate;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return new charts.TimeSeriesChart(
|
||||||
|
seriesList,
|
||||||
|
animate: animate,
|
||||||
|
// Optionally pass in a [DateTimeFactory] used by the chart. The factory
|
||||||
|
// should create the same type of [DateTime] as the data provided. If none
|
||||||
|
// specified, the default creates local date time.
|
||||||
|
dateTimeFactory: const charts.LocalDateTimeFactory(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create one series with sample hard coded data.
|
||||||
|
static List<charts.Series<TimeSeriesSales, DateTime>> _createSampleData() {
|
||||||
|
final data = [
|
||||||
|
new TimeSeriesSales(new DateTime(2017, 9, 19), 5),
|
||||||
|
new TimeSeriesSales(new DateTime(2017, 9, 26), 25),
|
||||||
|
new TimeSeriesSales(new DateTime(2017, 10, 3), 100),
|
||||||
|
new TimeSeriesSales(new DateTime(2017, 10, 10), 75),
|
||||||
|
];
|
||||||
|
|
||||||
|
return [
|
||||||
|
new charts.Series<TimeSeriesSales, DateTime>(
|
||||||
|
id: 'Sales',
|
||||||
|
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
|
||||||
|
domainFn: (TimeSeriesSales sales, _) => sales.time,
|
||||||
|
measureFn: (TimeSeriesSales sales, _) => sales.sales,
|
||||||
|
data: data,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sample time series data type.
|
||||||
|
class TimeSeriesSales {
|
||||||
|
TimeSeriesSales(this.time, this.sales);
|
||||||
|
|
||||||
|
final DateTime time;
|
||||||
|
final int sales;
|
||||||
|
}
|
|
@ -58,12 +58,10 @@ class CustomCheckbox extends StatefulWidget {
|
||||||
this.checkColor,
|
this.checkColor,
|
||||||
this.materialTapTargetSize,
|
this.materialTapTargetSize,
|
||||||
this.useTapTarget = true,
|
this.useTapTarget = true,
|
||||||
}) : assert(tristate != null),
|
}) : assert(tristate != null),
|
||||||
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.
|
||||||
|
@ -138,7 +136,8 @@ class CustomCheckbox extends StatefulWidget {
|
||||||
_CustomCheckboxState createState() => _CustomCheckboxState();
|
_CustomCheckboxState createState() => _CustomCheckboxState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CustomCheckboxState extends State<CustomCheckbox> with TickerProviderStateMixin {
|
class _CustomCheckboxState extends State<CustomCheckbox>
|
||||||
|
with TickerProviderStateMixin {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
assert(debugCheckHasMaterial(context));
|
assert(debugCheckHasMaterial(context));
|
||||||
|
@ -147,25 +146,26 @@ class _CustomCheckboxState extends State<CustomCheckbox> with TickerProviderStat
|
||||||
Size size;
|
Size size;
|
||||||
switch (widget.materialTapTargetSize ?? themeData.materialTapTargetSize) {
|
switch (widget.materialTapTargetSize ?? themeData.materialTapTargetSize) {
|
||||||
case MaterialTapTargetSize.padded:
|
case MaterialTapTargetSize.padded:
|
||||||
size = const Size(2 * kRadialReactionRadius + 8.0, 2 * kRadialReactionRadius + 8.0);
|
size = const Size(
|
||||||
|
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,
|
Size noTapTargetSize = Size(CustomCheckbox.width, CustomCheckbox.width);
|
||||||
CustomCheckbox.width);
|
|
||||||
final BoxConstraints additionalConstraints =
|
final BoxConstraints additionalConstraints =
|
||||||
BoxConstraints.tight(widget
|
BoxConstraints.tight(widget.useTapTarget ? size : noTapTargetSize);
|
||||||
.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 ? themeData.unselectedWidgetColor : themeData.disabledColor,
|
inactiveColor: widget.onChanged != null
|
||||||
|
? themeData.unselectedWidgetColor
|
||||||
|
: themeData.disabledColor,
|
||||||
onChanged: widget.onChanged,
|
onChanged: widget.onChanged,
|
||||||
additionalConstraints: additionalConstraints,
|
additionalConstraints: additionalConstraints,
|
||||||
vsync: this,
|
vsync: this,
|
||||||
|
@ -174,19 +174,18 @@ class _CustomCheckboxState extends State<CustomCheckbox> with TickerProviderStat
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
@required this.checkColor,
|
@required this.checkColor,
|
||||||
@required this.inactiveColor,
|
@required this.inactiveColor,
|
||||||
@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),
|
||||||
|
@ -205,15 +204,15 @@ class _CheckboxRenderObjectWidget extends LeafRenderObjectWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_RenderCheckbox createRenderObject(BuildContext context) => _RenderCheckbox(
|
_RenderCheckbox createRenderObject(BuildContext context) => _RenderCheckbox(
|
||||||
value: value,
|
value: value,
|
||||||
tristate: tristate,
|
tristate: tristate,
|
||||||
activeColor: activeColor,
|
activeColor: activeColor,
|
||||||
checkColor: checkColor,
|
checkColor: checkColor,
|
||||||
inactiveColor: inactiveColor,
|
inactiveColor: inactiveColor,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
vsync: vsync,
|
vsync: vsync,
|
||||||
additionalConstraints: additionalConstraints,
|
additionalConstraints: additionalConstraints,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void updateRenderObject(BuildContext context, _RenderCheckbox renderObject) {
|
void updateRenderObject(BuildContext context, _RenderCheckbox renderObject) {
|
||||||
|
@ -243,24 +242,23 @@ class _RenderCheckbox extends RenderToggleable {
|
||||||
BoxConstraints additionalConstraints,
|
BoxConstraints additionalConstraints,
|
||||||
ValueChanged<bool> onChanged,
|
ValueChanged<bool> onChanged,
|
||||||
@required TickerProvider vsync,
|
@required TickerProvider vsync,
|
||||||
}) : _oldValue = value,
|
}) : _oldValue = value,
|
||||||
super(
|
super(
|
||||||
value: value,
|
value: value,
|
||||||
tristate: tristate,
|
tristate: tristate,
|
||||||
activeColor: activeColor,
|
activeColor: activeColor,
|
||||||
inactiveColor: inactiveColor,
|
inactiveColor: inactiveColor,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
additionalConstraints: additionalConstraints,
|
additionalConstraints: additionalConstraints,
|
||||||
vsync: vsync,
|
vsync: vsync,
|
||||||
);
|
);
|
||||||
|
|
||||||
bool _oldValue;
|
bool _oldValue;
|
||||||
Color checkColor;
|
Color checkColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
set value(bool newValue) {
|
set value(bool newValue) {
|
||||||
if (newValue == value)
|
if (newValue == value) return;
|
||||||
return;
|
|
||||||
_oldValue = value;
|
_oldValue = value;
|
||||||
super.value = newValue;
|
super.value = newValue;
|
||||||
}
|
}
|
||||||
|
@ -278,7 +276,8 @@ 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 = Rect.fromLTWH(origin.dx + inset, origin.dy + inset, size, size);
|
final Rect rect =
|
||||||
|
Rect.fromLTWH(origin.dx + inset, origin.dy + inset, size, size);
|
||||||
return RRect.fromRectAndRadius(rect, _kEdgeRadius);
|
return RRect.fromRectAndRadius(rect, _kEdgeRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +287,9 @@ 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 ? activeColor : Color.lerp(inactiveColor, activeColor, t * 4.0));
|
: (t >= 0.25
|
||||||
|
? 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.
|
||||||
|
@ -303,7 +304,8 @@ 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 = outer.deflate(math.min(size / 2.0, _kStrokeWidth + size * t));
|
final RRect inner =
|
||||||
|
outer.deflate(math.min(size / 2.0, _kStrokeWidth + size * t));
|
||||||
canvas.drawDRRect(outer, inner, paint);
|
canvas.drawDRRect(outer, inner, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,11 +349,13 @@ 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 = offset + (size / 2.0 - const Size.square(_kEdgeSize) / 2.0);
|
final Offset origin =
|
||||||
|
offset + (size / 2.0 - const Size.square(_kEdgeSize) / 2.0);
|
||||||
final AnimationStatus status = position.status;
|
final AnimationStatus status = position.status;
|
||||||
final double tNormalized = status == AnimationStatus.forward || status == AnimationStatus.completed
|
final double tNormalized =
|
||||||
? position.value
|
status == AnimationStatus.forward || status == AnimationStatus.completed
|
||||||
: 1.0 - position.value;
|
? position.value
|
||||||
|
: 1.0 - position.value;
|
||||||
|
|
||||||
// Four cases: false to null, false to true, null to false, true to false
|
// Four cases: false to null, false to true, null to false, true to false
|
||||||
if (_oldValue == false || value == false) {
|
if (_oldValue == false || value == false) {
|
||||||
|
@ -366,29 +370,33 @@ 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);
|
||||||
|
|
||||||
_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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,11 +28,8 @@ 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
|
||||||
|
|
||||||
|
@ -40,16 +37,18 @@ 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(
|
||||||
Expanded(child: Text(label, style: textStyle, textAlign: TextAlign.center,)),
|
child: Text(
|
||||||
|
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
|
||||||
|
|
||||||
|
@ -89,11 +88,9 @@ 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
|
||||||
|
|
||||||
|
@ -110,7 +107,6 @@ class LabeledCheckbox extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
//USAGE:
|
//USAGE:
|
||||||
|
|
||||||
|
@ -134,4 +130,4 @@ Widget build(BuildContext context) {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
241
lib/common/widgets/organisations_dialog.dart
Normal file
241
lib/common/widgets/organisations_dialog.dart
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:local_spend/common/apifunctions/find_organisations.dart';
|
||||||
|
|
||||||
|
class FindOrganisations {
|
||||||
|
TextField getSearchBar(TextEditingController controller, String hintText) {
|
||||||
|
return TextField(
|
||||||
|
controller: controller,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: hintText,
|
||||||
|
icon: Icon(Icons.search),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: get all organisations, favourites and all data from one 'organisations' class or similar
|
||||||
|
// eg items: organisations.getFavourites().orderBy(name),
|
||||||
|
|
||||||
|
Future<dynamic> _moreInfoDialog(context, Organisation organisation) {
|
||||||
|
TextStyle informationTitleStyle = new TextStyle(fontSize: 16);
|
||||||
|
TextStyle informationStyle =
|
||||||
|
new TextStyle(fontSize: 16, fontWeight: FontWeight.bold);
|
||||||
|
|
||||||
|
return showDialog<Organisation>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (context, setState) {
|
||||||
|
return SimpleDialog(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
|
||||||
|
child: Text(
|
||||||
|
organisation.name,
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: 21, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||||
|
child: Divider(),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||||
|
child: Table(
|
||||||
|
// defaultColumnWidth: FixedColumnWidth(100),
|
||||||
|
children: [
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
Text("Street:", style: informationTitleStyle),
|
||||||
|
Text(organisation.streetName,
|
||||||
|
style: informationStyle),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
Text("Postcode:", style: informationTitleStyle),
|
||||||
|
Text(organisation.postcode.toUpperCase(),
|
||||||
|
style: informationStyle),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
Text("Town:", style: informationTitleStyle),
|
||||||
|
Text(organisation.town, style: informationStyle),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Organisation> dialog(context) {
|
||||||
|
bool _searchEnabled = false;
|
||||||
|
bool _orgsFetched = false;
|
||||||
|
TextEditingController searchBarText = new TextEditingController();
|
||||||
|
var organisations = new Organisations();
|
||||||
|
var listTitle = "All Organisations";
|
||||||
|
var organisationsList = List<Organisation>();
|
||||||
|
|
||||||
|
Future<int> _submitSearch(String search) async {
|
||||||
|
_searchEnabled = false;
|
||||||
|
listTitle = "Results for \'" + search + "\'";
|
||||||
|
|
||||||
|
var futureOrgs = await organisations.findOrganisations(search);
|
||||||
|
organisationsList = futureOrgs;
|
||||||
|
_searchEnabled = true;
|
||||||
|
return futureOrgs.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return showDialog<Organisation>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (context, setState) {
|
||||||
|
return SimpleDialog(
|
||||||
|
children: <Widget>[
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(20, 0, 0, 0),
|
||||||
|
width: 150,
|
||||||
|
height: 50,
|
||||||
|
child: TextField(
|
||||||
|
autofocus: true,
|
||||||
|
controller: searchBarText,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: "Payee Name",
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value.isNotEmpty) {
|
||||||
|
_searchEnabled = true;
|
||||||
|
} else {
|
||||||
|
_searchEnabled = false;
|
||||||
|
}
|
||||||
|
setState(() => {_searchEnabled});
|
||||||
|
},
|
||||||
|
onSubmitted: _searchEnabled
|
||||||
|
? ((_) {
|
||||||
|
SystemChannels.textInput
|
||||||
|
.invokeMethod('TextInput.hide');
|
||||||
|
var result =
|
||||||
|
_submitSearch(searchBarText.text);
|
||||||
|
result.then((_) {
|
||||||
|
setState(() {
|
||||||
|
_orgsFetched = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 80,
|
||||||
|
padding: EdgeInsets.fromLTRB(20, 0, 0, 0),
|
||||||
|
child: RaisedButton(
|
||||||
|
onPressed: _searchEnabled
|
||||||
|
? (() {
|
||||||
|
SystemChannels.textInput
|
||||||
|
.invokeMethod('TextInput.hide');
|
||||||
|
var result =
|
||||||
|
_submitSearch(searchBarText.text);
|
||||||
|
result.then((_) {
|
||||||
|
setState(() {
|
||||||
|
_orgsFetched = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
: null,
|
||||||
|
child: Icon(Icons.search, color: Colors.white),
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
Column(
|
||||||
|
children: _orgsFetched
|
||||||
|
? [
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(20, 20, 20, 0),
|
||||||
|
child: Text(
|
||||||
|
listTitle,
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: 23, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: MediaQuery.of(context).size.height * 0.67,
|
||||||
|
child: Material(
|
||||||
|
shadowColor: Colors.transparent,
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: organisationsList.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return Card(
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(Icons.person),
|
||||||
|
title: Text(organisationsList[index].name,
|
||||||
|
style: new TextStyle(fontSize: 18)),
|
||||||
|
subtitle: Text(organisationsList[index]
|
||||||
|
.postcode
|
||||||
|
.toUpperCase()),
|
||||||
|
// trailing: Icon(Icons.arrow_forward_ios),
|
||||||
|
// onTap: _chosenOrg(organisationsList[index]),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context)
|
||||||
|
.pop(organisationsList[index]);
|
||||||
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
// show more details about the organisation in a new dialog
|
||||||
|
var moreInfo = _moreInfoDialog(
|
||||||
|
context, organisationsList[index]);
|
||||||
|
moreInfo.whenComplete(null);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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
|
||||||
|
// cancel and ok buttons
|
||||||
|
],
|
||||||
|
// ),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,6 @@ 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),
|
||||||
|
@ -16,7 +15,8 @@ class PopupListView {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> getDialogOptions(context, List<String> options /*, Function onPressed*/) {
|
List<Widget> getDialogOptions(
|
||||||
|
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++) {
|
||||||
|
@ -33,4 +33,4 @@ class PopupListView {
|
||||||
|
|
||||||
return dialogOptionsList;
|
return dialogOptionsList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,13 @@ part 'config.g.dart';
|
||||||
|
|
||||||
@JsonSerializable(createToJson: false)
|
@JsonSerializable(createToJson: false)
|
||||||
class Config {
|
class Config {
|
||||||
final String env;
|
|
||||||
final bool production;
|
|
||||||
final String apiKey;
|
|
||||||
|
|
||||||
Config({this.env, this.production, this.apiKey});
|
Config({this.env, this.production, this.apiKey});
|
||||||
|
|
||||||
factory Config.fromJson(Map<String, dynamic> json) => _$ConfigFromJson(json);
|
factory Config.fromJson(Map<String, dynamic> json) => _$ConfigFromJson(json);
|
||||||
|
|
||||||
|
final String env;
|
||||||
|
final bool production;
|
||||||
|
final String apiKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConfigWrapper extends StatelessWidget {
|
class ConfigWrapper extends StatelessWidget {
|
||||||
|
@ -24,7 +24,7 @@ class ConfigWrapper extends StatelessWidget {
|
||||||
|
|
||||||
static Config of(BuildContext context) {
|
static Config of(BuildContext context) {
|
||||||
final _InheritedConfig inheritedConfig =
|
final _InheritedConfig inheritedConfig =
|
||||||
context.inheritFromWidgetOfExactType(_InheritedConfig);
|
context.inheritFromWidgetOfExactType(_InheritedConfig);
|
||||||
return inheritedConfig.config;
|
return inheritedConfig.config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,4 +43,4 @@ class _InheritedConfig extends InheritedWidget {
|
||||||
@override
|
@override
|
||||||
bool updateShouldNotify(_InheritedConfig oldWidget) =>
|
bool updateShouldNotify(_InheritedConfig oldWidget) =>
|
||||||
config != oldWidget.config;
|
config != oldWidget.config;
|
||||||
}
|
}
|
||||||
|
|
2
lib/env/dev.dart
vendored
2
lib/env/dev.dart
vendored
|
@ -3,4 +3,4 @@ import 'package:json_annotation/json_annotation.dart';
|
||||||
part 'dev.g.dart';
|
part 'dev.g.dart';
|
||||||
|
|
||||||
@JsonLiteral('dev.json', asConst: true)
|
@JsonLiteral('dev.json', asConst: true)
|
||||||
Map<String, dynamic> get config => _$configJsonLiteral;
|
Map<String, dynamic> get config => _$configJsonLiteral;
|
||||||
|
|
2
lib/env/dev.g.dart
vendored
2
lib/env/dev.g.dart
vendored
|
@ -9,5 +9,5 @@ part of 'dev.dart';
|
||||||
const _$configJsonLiteral = {
|
const _$configJsonLiteral = {
|
||||||
'env': 'DEV',
|
'env': 'DEV',
|
||||||
'production': false,
|
'production': false,
|
||||||
'apiUrl': 'https://dev.peartrade.org/api'
|
'apiUrl': 'https://dev.localspend.co.uk/api'
|
||||||
};
|
};
|
||||||
|
|
2
lib/env/dev.json
vendored
2
lib/env/dev.json
vendored
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"env": "DEV",
|
"env": "DEV",
|
||||||
"production": false,
|
"production": false,
|
||||||
"apiUrl": "https://dev.peartrade.org/api"
|
"apiUrl": "https://dev.localspend.co.uk/api"
|
||||||
}
|
}
|
2
lib/env/prod.dart
vendored
2
lib/env/prod.dart
vendored
|
@ -3,4 +3,4 @@ import 'package:json_annotation/json_annotation.dart';
|
||||||
part 'prod.g.dart';
|
part 'prod.g.dart';
|
||||||
|
|
||||||
@JsonLiteral('prod.json', asConst: true)
|
@JsonLiteral('prod.json', asConst: true)
|
||||||
Map<String, dynamic> get config => _$configJsonLiteral;
|
Map<String, dynamic> get config => _$configJsonLiteral;
|
||||||
|
|
2
lib/env/prod.g.dart
vendored
2
lib/env/prod.g.dart
vendored
|
@ -9,5 +9,5 @@ part of 'prod.dart';
|
||||||
const _$configJsonLiteral = {
|
const _$configJsonLiteral = {
|
||||||
'env': 'PROD',
|
'env': 'PROD',
|
||||||
'production': true,
|
'production': true,
|
||||||
'apiUrl': 'https://www.peartrade.org/api'
|
'apiUrl': 'https://www.localspend.co.uk/api'
|
||||||
};
|
};
|
||||||
|
|
2
lib/env/prod.json
vendored
2
lib/env/prod.json
vendored
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"env": "PROD",
|
"env": "PROD",
|
||||||
"production": true,
|
"production": true,
|
||||||
"apiUrl": "https://www.peartrade.org/api"
|
"apiUrl": "https://www.localspend.co.uk/api"
|
||||||
}
|
}
|
|
@ -1,29 +1,38 @@
|
||||||
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/receipt_page.dart';
|
import 'package:local_spend/pages/map_page.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';
|
||||||
|
|
||||||
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) {
|
||||||
//var config = ConfigWrapper.of(context);
|
// TODO: load graphs on app login and send to graph widgets
|
||||||
return new MaterialApp(
|
|
||||||
|
|
||||||
|
return new MaterialApp(
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
localizationsDelegates: [
|
localizationsDelegates: [
|
||||||
GlobalMaterialLocalizations.delegate,
|
GlobalMaterialLocalizations.delegate,
|
||||||
GlobalWidgetsLocalizations.delegate,
|
GlobalWidgetsLocalizations.delegate,
|
||||||
],
|
],
|
||||||
supportedLocales: [
|
supportedLocales: [Locale("en")],
|
||||||
Locale("en")
|
|
||||||
],
|
|
||||||
|
|
||||||
title: "Local Spend Tracker",
|
title: "Local Spend Tracker",
|
||||||
theme: new ThemeData(
|
theme: new ThemeData(
|
||||||
primarySwatch: Colors.blueGrey,
|
primarySwatch: Colors.blueGrey,
|
||||||
|
@ -31,7 +40,8 @@ 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(),
|
||||||
"/ReceiptPage": (BuildContext context) => ReceiptPage(),
|
'/MapPage': (BuildContext context) => MapPage(),
|
||||||
|
"/ReceiptPage": (BuildContext context) => ReceiptPage2(),
|
||||||
"/MorePage": (BuildContext context) => MorePage(),
|
"/MorePage": (BuildContext context) => MorePage(),
|
||||||
},
|
},
|
||||||
home: SplashScreen(),
|
home: SplashScreen(),
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
class LoginModel {
|
class LoginModel {
|
||||||
final String userName;
|
LoginModel(this.userName, this.token, this.userType);
|
||||||
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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
163
lib/pages/customerGraphs.dart
Normal file
163
lib/pages/customerGraphs.dart
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
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>>
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:local_spend/pages/receipt_page.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 = 'Text here';
|
static String _title = 'SpendTracker';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -24,11 +25,13 @@ 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>[
|
||||||
ReceiptPage(),
|
ReceiptPage2(),
|
||||||
StatsPage(),
|
StatsPage(),
|
||||||
|
MapPage(),
|
||||||
MorePage()
|
MorePage()
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -54,15 +57,20 @@ 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,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,9 @@ 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
|
||||||
|
@ -18,12 +19,12 @@ class LoginPage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class LoginPageState extends State<LoginPage> {
|
class LoginPageState extends State<LoginPage> {
|
||||||
final TextEditingController _emailController = TextEditingController(/*text: 'test@example.com'*/); // remove
|
bool _isLoggingIn = false;
|
||||||
final TextEditingController _passwordController = TextEditingController(/*text: 'abc123'*/); // remove
|
final TextEditingController _emailController = TextEditingController();
|
||||||
bool _saveLoginDetails = true; // I am extremely sorry for the placement of this variable
|
final TextEditingController _passwordController = TextEditingController();
|
||||||
// it will be fixed soon I promise
|
bool _saveLoginDetails = true;
|
||||||
|
|
||||||
FocusNode focusNode; // added so focus can move automatically
|
FocusNode focusNode; // added so focus can move automatically
|
||||||
|
|
||||||
Future launchURL(String url) async {
|
Future launchURL(String url) async {
|
||||||
if (await canLaunch(url)) {
|
if (await canLaunch(url)) {
|
||||||
|
@ -32,7 +33,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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +54,7 @@ class LoginPageState extends State<LoginPage> {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
_fillLoginDetails() async {
|
void _fillLoginDetails() async {
|
||||||
SharedPreferences preferences = await SharedPreferences.getInstance();
|
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
var username = await preferences.get('username');
|
var username = await preferences.get('username');
|
||||||
|
@ -63,27 +64,27 @@ class LoginPageState extends State<LoginPage> {
|
||||||
_passwordController.text = await password;
|
_passwordController.text = await password;
|
||||||
}
|
}
|
||||||
|
|
||||||
_saveCurrentRoute(String lastRoute) async {
|
void _saveCurrentRoute(String lastRoute) async {
|
||||||
SharedPreferences preferences = await SharedPreferences.getInstance();
|
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||||
await preferences.setString('LastPageRoute', lastRoute);
|
await preferences.setString('LastPageRoute', lastRoute);
|
||||||
}
|
}
|
||||||
|
|
||||||
login(String username, String password) async {
|
void login(String username, String password) async {
|
||||||
SystemChannels.textInput.invokeMethod('TextInput.hide');
|
_isLoggingIn = true;
|
||||||
|
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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requestLoginAPI(context, username,
|
await requestLoginAPI(context, username, password).then((value) {
|
||||||
password);
|
_isLoggingIn = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -96,153 +97,128 @@ class LoginPageState extends State<LoginPage> {
|
||||||
} else {
|
} else {
|
||||||
Navigator.of(context).pushReplacementNamed('/HomePage');
|
Navigator.of(context).pushReplacementNamed('/HomePage');
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
child: PlatformScaffold(
|
child: PlatformScaffold(
|
||||||
// drawer: BasicDrawer(),
|
body: Stack(
|
||||||
// body: Container(
|
children: [
|
||||||
// decoration: BoxDecoration(color: Colors.white),
|
AnimatedBackground([Colors.lightBlue[50], Colors.lightBlue[50]],
|
||||||
// margin: const EdgeInsets.all(20),
|
Colors.white, Alignment.topRight, Alignment.bottomLeft, 3),
|
||||||
// child: Padding(
|
Container(
|
||||||
// padding: EdgeInsets.fromLTRB(30.0, 170.0, 30.0, 0.0),
|
margin: EdgeInsets.fromLTRB(60, 30, 60, 0),
|
||||||
// child: ListView(
|
child: Column(
|
||||||
// children: <Widget>[
|
children: <Widget>[
|
||||||
body: Container(
|
Expanded(
|
||||||
decoration: new BoxDecoration(
|
child: AnimatedContainer(
|
||||||
gradient: new LinearGradient(
|
duration: Duration(seconds: 2),
|
||||||
colors: [Colors.white, Colors.blue[50]],
|
margin: EdgeInsets.fromLTRB(15, 0, 15, 0),
|
||||||
stops: [0,1],
|
decoration: BoxDecoration(
|
||||||
begin: Alignment.topCenter,
|
image: DecorationImage(
|
||||||
end: Alignment.bottomCenter,
|
image:
|
||||||
),
|
AssetImage('assets/images/launch_image.png')),
|
||||||
), child: Container(
|
|
||||||
margin: EdgeInsets.fromLTRB(60,30,60,0),
|
|
||||||
child: Column(
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
margin: EdgeInsets.fromLTRB(15,0,15,0),
|
|
||||||
// alignment: FractionalOffset(0.5, 0.3), // not sure what this does ngl :/
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(
|
|
||||||
image: AssetImage('assets/images/launch_image.png')
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
Padding(
|
padding: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
|
||||||
padding: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
|
child: TextField(
|
||||||
child: TextField(
|
autocorrect: false,
|
||||||
autocorrect: false,
|
textAlign: TextAlign.center,
|
||||||
textAlign: TextAlign.center,
|
controller: _emailController,
|
||||||
controller: _emailController,
|
decoration: InputDecoration(
|
||||||
decoration: InputDecoration(
|
hintText: "EMAIL",
|
||||||
hintText: "EMAIL",
|
hintStyle: TextStyle(fontSize: 15),
|
||||||
hintStyle: TextStyle(fontSize: 15),
|
),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18.0,
|
||||||
|
color: Colors.grey[800],
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
onSubmitted: (_) {
|
||||||
|
FocusScope.of(context).requestFocus(focusNode);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18.0,
|
|
||||||
color: Colors.grey[800],
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
onSubmitted: (_) {
|
|
||||||
FocusScope.of(context).requestFocus(focusNode);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
Padding(
|
padding: EdgeInsets.fromLTRB(0.0, 15.0, 0.0, 0.0),
|
||||||
padding: EdgeInsets.fromLTRB(0.0, 15.0, 0.0, 0.0),
|
child: TextField(
|
||||||
child: TextField(
|
autocorrect: false,
|
||||||
autocorrect: false,
|
textAlign: TextAlign.center,
|
||||||
textAlign: TextAlign.center,
|
controller: _passwordController,
|
||||||
controller: _passwordController,
|
focusNode: focusNode,
|
||||||
focusNode: focusNode,
|
decoration: InputDecoration(
|
||||||
decoration: InputDecoration(
|
hintText: 'PASSWORD',
|
||||||
hintText: 'PASSWORD',
|
hintStyle: TextStyle(fontSize: 15),
|
||||||
hintStyle: TextStyle(fontSize: 15),
|
),
|
||||||
|
obscureText: true,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18.0,
|
||||||
|
color: Colors.grey[800],
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
onSubmitted: (_) {
|
||||||
|
login(_emailController.text, _passwordController.text);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
obscureText: true,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18.0,
|
|
||||||
color: Colors.grey[800],
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
onSubmitted: (_) {
|
|
||||||
login( _emailController.text,
|
|
||||||
_passwordController.text);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 30.0),
|
||||||
Padding(
|
width: 100,
|
||||||
padding: EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 30.0),
|
height: 50,
|
||||||
|
child: Opacity(
|
||||||
child : Material(
|
opacity: _isLoggingIn ? 0.5 : 1,
|
||||||
child: new Container(
|
child: ClipRRect(
|
||||||
child : InkWell(
|
borderRadius: BorderRadius.circular(2),
|
||||||
onTap: () => login( _emailController.text, _passwordController.text),
|
child: Stack(
|
||||||
child: new Container(
|
children: [
|
||||||
width: 100,
|
AnimatedBackground(
|
||||||
height: 50,
|
[Colors.blue, Colors.lightBlue[300]],
|
||||||
child: new Center(
|
Colors.lightBlue,
|
||||||
child: new Text(
|
Alignment.bottomRight,
|
||||||
'GO', style: new TextStyle(fontSize: 18, color: Colors.white),),
|
Alignment.topLeft,
|
||||||
|
3),
|
||||||
|
Material(
|
||||||
|
type: MaterialType.transparency,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: _isLoggingIn
|
||||||
|
? null
|
||||||
|
: () => login(_emailController.text,
|
||||||
|
_passwordController.text),
|
||||||
|
child: new Center(
|
||||||
|
child: new Text(
|
||||||
|
'GO',
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: 18, color: Colors.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
splashColor: Colors.lightBlueAccent,
|
|
||||||
),
|
|
||||||
|
|
||||||
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,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(0, 10, 0, 50),
|
||||||
|
child: LabeledCheckbox(
|
||||||
|
label: "SAVE LOGIN",
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: Colors.black54,
|
||||||
|
fontWeight: FontWeight.bold),
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
|
||||||
|
value: _saveLoginDetails,
|
||||||
|
onChanged: (bool newValue) {
|
||||||
|
setState(() {
|
||||||
|
_saveLoginDetails = newValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(0, 10, 0, 50),
|
|
||||||
|
|
||||||
child: LabeledCheckbox(
|
|
||||||
label : "SAVE LOGIN",
|
|
||||||
textStyle: TextStyle(fontSize: 18, color: Colors.black54, fontWeight: FontWeight.bold),
|
|
||||||
padding: const EdgeInsets.fromLTRB(0,0,0,0),
|
|
||||||
value : _saveLoginDetails,
|
|
||||||
|
|
||||||
onChanged: (bool newValue) {
|
|
||||||
setState(() {
|
|
||||||
_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;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),*/
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
62
lib/pages/map_page.dart
Normal file
62
lib/pages/map_page.dart
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
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';
|
||||||
|
@ -7,7 +6,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 {
|
||||||
|
@ -18,7 +17,7 @@ class MorePage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MorePageState extends State<MorePage> {
|
class MorePageState extends State<MorePage> {
|
||||||
FocusNode focusNode; // added so focus can move automatically
|
FocusNode focusNode; // added so focus can move automatically
|
||||||
|
|
||||||
DateTime date;
|
DateTime date;
|
||||||
|
|
||||||
|
@ -37,7 +36,7 @@ class MorePageState extends State<MorePage> {
|
||||||
focusNode.dispose(); //disposes focus node when form disposed
|
focusNode.dispose(); //disposes focus node when form disposed
|
||||||
}
|
}
|
||||||
|
|
||||||
_saveCurrentRoute(String lastRoute) async {
|
void _saveCurrentRoute(String lastRoute) async {
|
||||||
SharedPreferences preferences = await SharedPreferences.getInstance();
|
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||||
await preferences.setString('LastPageRoute', lastRoute);
|
await preferences.setString('LastPageRoute', lastRoute);
|
||||||
}
|
}
|
||||||
|
@ -52,9 +51,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(
|
||||||
|
@ -68,15 +67,12 @@ 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(0.0,25,0.0,0.0),
|
padding: EdgeInsets.fromLTRB(30.0, 25, 30.0, 0.0),
|
||||||
child : Text(
|
child: Text(
|
||||||
"Local Spend Tracker",
|
"Local Spend Tracker",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
@ -86,76 +82,93 @@ class MorePageState extends State<MorePage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
//
|
|
||||||
// Padding(
|
|
||||||
// padding: EdgeInsets.fromLTRB(10, 15, 0, 0),
|
|
||||||
// child: Text("helloooo"),
|
|
||||||
// ),
|
|
||||||
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.fromLTRB(0.0, 25.0, 0.0, 0.0),
|
padding: EdgeInsets.fromLTRB(30.0, 25.0, 30.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."),
|
Text("Pear Trading is a commerce company designed to register and monitor money circulating in the local economy.\n"),
|
||||||
Text("\nContact at test@example.com or +44(0)1524 64544"),
|
Container(
|
||||||
|
margin: EdgeInsets.symmetric(horizontal: 10),
|
||||||
Padding(
|
height: 35,
|
||||||
padding: EdgeInsets.fromLTRB(0,20,0,0),
|
child: RaisedButton(
|
||||||
child: InkWell(
|
onPressed: () => launch('http://www.peartrade.org'),
|
||||||
child: Text
|
child: Text("Pear Trading",
|
||||||
('Developed by Shadowcat Systems',
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.blue,
|
color: Colors.white, fontSize: 18.0)),
|
||||||
),
|
color: Colors.green,
|
||||||
),
|
|
||||||
onTap: () => launch('https://shadow.cat/')
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0.0),
|
||||||
|
height: 40.0,
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(3),
|
||||||
|
onTap: () => launch('https://shadow.cat'),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Align(
|
||||||
|
child: Text("Developed by"),
|
||||||
|
alignment: Alignment.centerLeft),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.all(0),
|
||||||
|
child : Text(
|
||||||
|
"Shadowcat Systems",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
},
|
},
|
||||||
child: Text("ABOUT",
|
child: Text("ABOUT",
|
||||||
style:
|
style: TextStyle(color: Colors.white, fontSize: 22.0)),
|
||||||
TextStyle(color: Colors.white, fontSize: 22.0)),
|
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 0.0),
|
padding: EdgeInsets.fromLTRB(30.0, 20.0, 30.0, 0.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 65.0,
|
height: 65.0,
|
||||||
child: RaisedButton(
|
child: RaisedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialogTwoButtons(
|
showDialogTwoButtons(
|
||||||
context,
|
context,
|
||||||
"Logout",
|
"Logout",
|
||||||
"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:
|
style: TextStyle(color: Colors.white, fontSize: 22.0)),
|
||||||
TextStyle(color: Colors.white, fontSize: 22.0)),
|
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Padding(
|
// Padding(
|
||||||
// padding: EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 0.0),
|
// padding: EdgeInsets.fromLTRB(30.0, 20.0, 30.0, 0.0),
|
||||||
// child: Container(
|
// child: Container(
|
||||||
// height: 65.0,
|
// height: 65.0,
|
||||||
// child: RaisedButton(
|
// child: RaisedButton(
|
||||||
|
@ -169,7 +182,6 @@ class MorePageState extends State<MorePage> {
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
57
lib/pages/new_stats_page.dart
Normal file
57
lib/pages/new_stats_page.dart
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:local_spend/common/platform/platform_scaffold.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
const url = "https://flutter.io/";
|
||||||
|
const demonstration = false;
|
||||||
|
|
||||||
|
class NewStatsPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() {
|
||||||
|
return new NewStatsPageState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NewStatsPageState extends State<NewStatsPage> {
|
||||||
|
/// Graph types:
|
||||||
|
/// - total_last_week
|
||||||
|
/// - avg_spend_last_week
|
||||||
|
/// - total_last_month
|
||||||
|
/// - avg_spend_last_month
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_saveCurrentRoute("/StatsPageState");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _saveCurrentRoute(String lastRoute) async {
|
||||||
|
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||||
|
await preferences.setString('LastPageRoute', lastRoute);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return PlatformScaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
backgroundColor: Colors.blue[400],
|
||||||
|
title: Text(
|
||||||
|
"Statistics",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// leading: BackButton(),
|
||||||
|
centerTitle: true,
|
||||||
|
iconTheme: IconThemeData(color: Colors.black),
|
||||||
|
),
|
||||||
|
body: Container(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
178
lib/pages/orgGraphs.dart
Normal file
178
lib/pages/orgGraphs.dart
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
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>>
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,612 +0,0 @@
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:local_spend/common/apifunctions/submit_receipt_api.dart';
|
|
||||||
import 'package:local_spend/common/functions/show_dialog_single_button.dart';
|
|
||||||
import 'package:local_spend/common/platform/platform_scaffold.dart';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
|
|
||||||
import 'package:local_spend/common/apifunctions/find_organisations.dart';
|
|
||||||
import 'package:local_spend/common/widgets/popupListView.dart';
|
|
||||||
import 'package:local_spend/common/apifunctions/categories.dart';
|
|
||||||
|
|
||||||
const URL = "https://flutter.io/";
|
|
||||||
const demonstration = false;
|
|
||||||
|
|
||||||
class ReceiptPage extends StatefulWidget {
|
|
||||||
@override
|
|
||||||
State<StatefulWidget> createState() {
|
|
||||||
return new ReceiptPageState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReceiptPageState extends State<ReceiptPage> {
|
|
||||||
final TextEditingController _timeController = TextEditingController();
|
|
||||||
final TextEditingController _amountController = TextEditingController();
|
|
||||||
final TextEditingController _essentialController = TextEditingController();
|
|
||||||
final TextEditingController _recurringController = TextEditingController();
|
|
||||||
final TextEditingController _categoryController = TextEditingController();
|
|
||||||
final TextEditingController _orgController = TextEditingController();
|
|
||||||
final OrganizationController _organizationController = OrganizationController();
|
|
||||||
List<String> _categoryDropDownItems = List<String>();
|
|
||||||
|
|
||||||
FocusNode focusNode;
|
|
||||||
|
|
||||||
DateTime date;
|
|
||||||
|
|
||||||
Future launchURL(String url) async {
|
|
||||||
if (await canLaunch(url)) {
|
|
||||||
await launch(url, forceSafariVC: true, forceWebView: true);
|
|
||||||
} else {
|
|
||||||
showDialogSingleButton(
|
|
||||||
context,
|
|
||||||
"Unable to reach your website.",
|
|
||||||
"Currently unable to reach the website $URL. Please try again at a later time.",
|
|
||||||
"OK");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
getCategoriesStrings().then((value) {
|
|
||||||
_categoryDropDownItems = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
super.initState();
|
|
||||||
_saveCurrentRoute("/ReceiptPageState");
|
|
||||||
|
|
||||||
focusNode = FocusNode();
|
|
||||||
|
|
||||||
_recurringController.text = "None";
|
|
||||||
_categoryController.text = "";
|
|
||||||
// getCategoriesStrings().then((value) {
|
|
||||||
// _categoryDropDownItems = value;
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
super.dispose();
|
|
||||||
|
|
||||||
focusNode.dispose(); //disposes focus node when form disposed
|
|
||||||
}
|
|
||||||
|
|
||||||
_saveCurrentRoute(String lastRoute) async {
|
|
||||||
SharedPreferences preferences = await SharedPreferences.getInstance();
|
|
||||||
await preferences.setString('LastPageRoute', lastRoute);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this file is getting really messy sorry everyone
|
|
||||||
|
|
||||||
void submitReceipt(String amount, String time, Organisation organisation, String recurring, String category, String essential) async {
|
|
||||||
SystemChannels.textInput.invokeMethod('TextInput.hide');
|
|
||||||
|
|
||||||
if (organisation == null) {
|
|
||||||
_findOrganizationsDialog(context);
|
|
||||||
/* await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
// return object of type Dialog
|
|
||||||
return AlertDialog(
|
|
||||||
title: new Text("Missing organisation"),
|
|
||||||
content: new Text(
|
|
||||||
"Please press 'Find' to select your desired organization."),
|
|
||||||
actions: <Widget>[
|
|
||||||
new FlatButton(
|
|
||||||
child: new Text("OK"),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);*/
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (amount == "" || time == "") {
|
|
||||||
await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
// return object of type Dialog
|
|
||||||
return AlertDialog(
|
|
||||||
title: new Text("Missing required data"),
|
|
||||||
content: new Text(
|
|
||||||
"We couldn't process your request because one or more required fields are missing."),
|
|
||||||
actions: <Widget>[
|
|
||||||
new FlatButton(
|
|
||||||
child: new Text("OK"),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (demonstration) {
|
|
||||||
await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
// return object of type Dialog
|
|
||||||
return AlertDialog(
|
|
||||||
title: new Text("Success"),
|
|
||||||
content: new Text("Receipt successfully submitted."),
|
|
||||||
actions: <Widget>[
|
|
||||||
// usually buttons at the bottom of the dialog
|
|
||||||
new FlatButton(
|
|
||||||
child: new Text("OK"),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
Navigator.of(context).pushReplacementNamed("/HomePage");
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
).then((_) {});
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
Receipt receipt = new Receipt();
|
|
||||||
|
|
||||||
// setting up 'receipt'
|
|
||||||
receipt.amount = amount;
|
|
||||||
receipt.time = formatDate(time);
|
|
||||||
// debugPrint(organisation.name + ", " + organisation.streetName + ", " + organisation.town + ", " + organisation.postcode);
|
|
||||||
receipt.organisationName = organisation.name;
|
|
||||||
receipt.street = organisation.streetName;
|
|
||||||
receipt.town = organisation.town;
|
|
||||||
receipt.postcode = organisation.postcode;
|
|
||||||
receipt.recurring = recurring;
|
|
||||||
receipt.category = category;
|
|
||||||
|
|
||||||
receipt.essential = essential;
|
|
||||||
|
|
||||||
submitReceiptAPI(context, receipt);
|
|
||||||
Navigator.of(context).pushReplacementNamed("/HomePage");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String convertBoolToString(bool toConvert) {
|
|
||||||
if (toConvert)
|
|
||||||
{
|
|
||||||
return "true";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "false";
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<String>> getCategoriesStrings() async {
|
|
||||||
var categories = getCategories(); //future<list<cat>>
|
|
||||||
var categoriesStrings = List<String>();
|
|
||||||
|
|
||||||
categories.then((val) {
|
|
||||||
val.forEach((thisCategory) {
|
|
||||||
// print(thisCategory.name);
|
|
||||||
categoriesStrings.add(thisCategory.name);
|
|
||||||
});
|
|
||||||
|
|
||||||
// print(categoriesStrings[10]); // prints 'Banana'
|
|
||||||
// print(categoriesStrings.toString());
|
|
||||||
|
|
||||||
_categoryDropDownItems = categoriesStrings;
|
|
||||||
return categoriesStrings;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> getRecurringOptions() {
|
|
||||||
var options = new List<String>(7);
|
|
||||||
options[0] = "None";
|
|
||||||
options[1] = "Daily";
|
|
||||||
options[2] = "Weekly";
|
|
||||||
options[3] = "Fortnightly";
|
|
||||||
options[4] = "Monthly";
|
|
||||||
options[5] = "Quarterly";
|
|
||||||
options[6] = "Yearly";
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
String formatDate(String date) {
|
|
||||||
// return "";
|
|
||||||
// should be in format:
|
|
||||||
// yyyy-MM-ddThh:mm:00.000+01:00
|
|
||||||
// eg 2019-07-05T10:24:00.000+01.00 (real life example, works)
|
|
||||||
|
|
||||||
// current format = "dd/MM/yyyy 'at' hh:mm"
|
|
||||||
// 0123456789ABCDEFGHIJK
|
|
||||||
|
|
||||||
var components = new List(5);
|
|
||||||
|
|
||||||
components[0] = (date.substring(0,2)); // dd
|
|
||||||
components[1] = (date.substring(3,5)); // MM
|
|
||||||
components[2] = (date.substring(6,10)); // yyyy
|
|
||||||
components[3] = (date.substring(14,16)); // hh
|
|
||||||
components[4] = (date.substring(17,19)); // mm
|
|
||||||
|
|
||||||
//print(components);
|
|
||||||
|
|
||||||
return (components[2] + "-" + components[1] + "-" + components[0]
|
|
||||||
+ "T" + components[3] + ":" + components[4] + ":00.000+01:00");
|
|
||||||
|
|
||||||
// Yes, there is probably a function to convert dates, but I didn't
|
|
||||||
// know that before writing this and it's done now so I'm keeping it.
|
|
||||||
}
|
|
||||||
|
|
||||||
Organisation listOrganisations(List<Organisation> organisations, context) {
|
|
||||||
if (organisations.length == 0) {
|
|
||||||
showDialogSingleButton(
|
|
||||||
context,
|
|
||||||
"No matching organizations",
|
|
||||||
"We were unable to find any organizations matching this text.",
|
|
||||||
"OK"
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var optionsList = new List<String>();
|
|
||||||
|
|
||||||
for (var i = 0; i < organisations.length; i++) {
|
|
||||||
optionsList.add(organisations[i].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// var popupListView = new PopupListView(context, optionsList, "Choose Organization");
|
|
||||||
|
|
||||||
var popupListView = new PopupListView();
|
|
||||||
var dialog = popupListView.dialog(context, optionsList, "Choose Organization");
|
|
||||||
|
|
||||||
// dialog.then((value) => debugPrint(value));
|
|
||||||
|
|
||||||
|
|
||||||
dialog.then((value) {
|
|
||||||
_orgController.text = value;
|
|
||||||
_organizationController.organisation = organisations.where((thisOrg) => thisOrg.name == value).elementAt(0);
|
|
||||||
// this may not work when two organisations have the same name,
|
|
||||||
// then again the popupListView can't display two of the same names properly either
|
|
||||||
});
|
|
||||||
//can't return value as it is <future> and thus would block
|
|
||||||
}
|
|
||||||
|
|
||||||
_findOrganizationsDialog(context) {
|
|
||||||
if (_orgController.text != "") {
|
|
||||||
var organisations = findOrganisations(
|
|
||||||
_orgController.text); // returns Future<List<Organisation>>
|
|
||||||
|
|
||||||
var choice = organisations.then((data) =>
|
|
||||||
listOrganisations(data, context));
|
|
||||||
|
|
||||||
choice.then((value) => _orgController.text = value.name);
|
|
||||||
choice.then((value) => _organizationController.organisation = value);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// no data entered
|
|
||||||
|
|
||||||
showDialogSingleButton(
|
|
||||||
context,
|
|
||||||
"No data",
|
|
||||||
"We were unable to service your request because no data was entered.",
|
|
||||||
"OK"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return PlatformScaffold(
|
|
||||||
|
|
||||||
appBar: AppBar(
|
|
||||||
backgroundColor: Colors.blue[400],
|
|
||||||
title: Text(
|
|
||||||
"Submit Receipt",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// leading: BackButton(),
|
|
||||||
centerTitle: true,
|
|
||||||
iconTheme: IconThemeData(color: Colors.black),
|
|
||||||
),
|
|
||||||
|
|
||||||
body: Container(
|
|
||||||
padding: EdgeInsets.fromLTRB(30.0, 0.0, 30.0, 0.0),
|
|
||||||
child: ListView(
|
|
||||||
children: <Widget>[
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
|
|
||||||
child : Text(
|
|
||||||
"Time of Transaction",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DateTimePickerFormField(
|
|
||||||
inputType: InputType.both,
|
|
||||||
format: DateFormat("dd/MM/yyyy 'at' hh:mm"),
|
|
||||||
editable: true,
|
|
||||||
controller: _timeController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: 'Date/Time of Transaction', hasFloatingPlaceholder: false),
|
|
||||||
onChanged: (dt) => setState(() => date = dt),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
|
|
||||||
child: Text(
|
|
||||||
"Amount",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0, 5.0, 0.0, 0.0),
|
|
||||||
child: TextField(
|
|
||||||
controller: _amountController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Value in £',
|
|
||||||
),
|
|
||||||
// obscureText: true,
|
|
||||||
autocorrect: false,
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18.0,
|
|
||||||
color: Colors.grey[800],
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
onSubmitted: (_) {
|
|
||||||
FocusScope.of(context).requestFocus(focusNode);
|
|
||||||
// submitReceipt(_amountController.text, _timeController.text);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
|
|
||||||
|
|
||||||
child : Container (
|
|
||||||
height: 22, // this should be the same height as text
|
|
||||||
|
|
||||||
child : ListView(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
children: <Widget>[
|
|
||||||
|
|
||||||
Container(
|
|
||||||
child: Text(
|
|
||||||
"Organization Name",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
child : Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(5,0,0,4), // sorry about hardcoded constraints
|
|
||||||
child: FlatButton(
|
|
||||||
onPressed: () {
|
|
||||||
_findOrganizationsDialog(context);
|
|
||||||
},
|
|
||||||
child: Text("Find",
|
|
||||||
style: TextStyle(color: Colors.blue, fontSize: 18.0)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0, 5.0, 0.0, 0.0),
|
|
||||||
child: TextField(
|
|
||||||
controller: _orgController,
|
|
||||||
focusNode: focusNode,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Eg. Pear Trading',
|
|
||||||
),
|
|
||||||
// obscureText: true,
|
|
||||||
autocorrect: true,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18.0,
|
|
||||||
color: Colors.grey[800],
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
onSubmitted: (_) {
|
|
||||||
submitReceipt(_amountController.text,
|
|
||||||
_timeController.text, _organizationController.organisation, _recurringController.text, _categoryController.text, _essentialController.text);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
|
|
||||||
|
|
||||||
child : Container (
|
|
||||||
height: 18,
|
|
||||||
|
|
||||||
child : ListView(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
|
|
||||||
children: <Widget>[
|
|
||||||
Container(
|
|
||||||
child: Text(
|
|
||||||
"Essential",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18.0,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
child : Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(20.0, 0.0, 0, 0),
|
|
||||||
|
|
||||||
child: Checkbox(value:
|
|
||||||
_essentialController.text.toLowerCase() == 'true',
|
|
||||||
onChanged: (bool newValue) {
|
|
||||||
setState(() {
|
|
||||||
_essentialController.text =
|
|
||||||
convertBoolToString(newValue);
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,18,0.0,0.0),
|
|
||||||
|
|
||||||
child : Container (
|
|
||||||
height: 35,
|
|
||||||
// width: 400,
|
|
||||||
|
|
||||||
child : ListView(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
|
|
||||||
children: <Widget>[
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.fromLTRB(0, 7, 0, 8),
|
|
||||||
child: Text(
|
|
||||||
"Recurring",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18.0,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.fromLTRB(29, 0, 0, 0),
|
|
||||||
child: DropdownButton<String>(
|
|
||||||
value: _recurringController.text,
|
|
||||||
onChanged: (String newValue) {
|
|
||||||
setState(() {
|
|
||||||
_recurringController.text = newValue;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
items: getRecurringOptions().map<DropdownMenuItem<String>>((String value) {
|
|
||||||
return DropdownMenuItem<String>(
|
|
||||||
value: value,
|
|
||||||
child: Text(value),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.toList(),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,18,0.0,0.0),
|
|
||||||
|
|
||||||
child : Container (
|
|
||||||
height: 35,
|
|
||||||
// width: 400,
|
|
||||||
|
|
||||||
child : ListView(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
|
|
||||||
children: <Widget>[
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.fromLTRB(0, 7, 0, 8),
|
|
||||||
child: Text(
|
|
||||||
"Category",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18.0,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.fromLTRB(29, 0, 0, 0),
|
|
||||||
child: DropdownButton<String>(
|
|
||||||
value: _categoryController.text,
|
|
||||||
onChanged: (String newValue) {
|
|
||||||
setState(() {
|
|
||||||
_categoryController.text = newValue;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
items: _categoryDropDownItems.map<DropdownMenuItem<String>>((String value) {
|
|
||||||
return DropdownMenuItem<String>(
|
|
||||||
value: value,
|
|
||||||
child: Text(value),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 0.0),
|
|
||||||
child: Container(
|
|
||||||
height: 65.0,
|
|
||||||
child: RaisedButton(
|
|
||||||
onPressed: () {
|
|
||||||
try {
|
|
||||||
submitReceipt(
|
|
||||||
_amountController.text, _timeController.text,
|
|
||||||
_organizationController.organisation, _recurringController.text, _categoryController.text, _essentialController.text);
|
|
||||||
}
|
|
||||||
catch (_) {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
// return object of type Dialog
|
|
||||||
return AlertDialog(
|
|
||||||
title: new Text("Invalid data"),
|
|
||||||
content: new Text(
|
|
||||||
"We couldn't process your request because some of the data entered is invalid."),
|
|
||||||
actions: <Widget>[
|
|
||||||
new FlatButton(
|
|
||||||
child: new Text("OK"),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text("GO",
|
|
||||||
style:
|
|
||||||
TextStyle(color: Colors.white, fontSize: 22.0)),
|
|
||||||
color: Colors.blue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
606
lib/pages/receipt_page_2.dart
Normal file
606
lib/pages/receipt_page_2.dart
Normal file
|
@ -0,0 +1,606 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:local_spend/common/platform/platform_scaffold.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
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/widgets/organisations_dialog.dart';
|
||||||
|
import 'package:local_spend/common/apifunctions/submit_receipt_api.dart';
|
||||||
|
import 'package:local_spend/common/apifunctions/categories.dart';
|
||||||
|
|
||||||
|
class Transaction {
|
||||||
|
Transaction(
|
||||||
|
this.date,
|
||||||
|
this.amount,
|
||||||
|
this.organisation,
|
||||||
|
this.recurring,
|
||||||
|
this.isEssential,
|
||||||
|
this.category,
|
||||||
|
);
|
||||||
|
|
||||||
|
DateTime date;
|
||||||
|
TextEditingController amount;
|
||||||
|
Organisation organisation;
|
||||||
|
String recurring;
|
||||||
|
bool isEssential;
|
||||||
|
String category;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReceiptPage2 extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() {
|
||||||
|
return new ReceiptPage2State();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReceiptPage2State extends State<ReceiptPage2> {
|
||||||
|
Transaction transaction = new Transaction(
|
||||||
|
DateTime.now(),
|
||||||
|
new TextEditingController(),
|
||||||
|
new Organisation(null, null, null, null, null),
|
||||||
|
"None",
|
||||||
|
false,
|
||||||
|
"Uncategorised",
|
||||||
|
);
|
||||||
|
|
||||||
|
AlertDialog _invalidDialog(context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: new Text("Invalid data"),
|
||||||
|
content: new Text(
|
||||||
|
"We couldn't process your request because some of the data entered is invalid."),
|
||||||
|
actions: <Widget>[
|
||||||
|
new FlatButton(
|
||||||
|
child: new Text("OK"),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<String>> getCats() async {
|
||||||
|
return await getCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _submitReceipt(Transaction transaction) {
|
||||||
|
Receipt receipt = new Receipt();
|
||||||
|
receipt.organisationName = transaction.organisation.name;
|
||||||
|
receipt.street = transaction.organisation.streetName;
|
||||||
|
receipt.postcode = transaction.organisation.postcode;
|
||||||
|
receipt.town = transaction.organisation.town;
|
||||||
|
|
||||||
|
receipt.recurring = transaction.recurring;
|
||||||
|
|
||||||
|
if (transaction.recurring == "None") {
|
||||||
|
receipt.recurring = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
receipt.category = transaction.category;
|
||||||
|
|
||||||
|
if (transaction.category == "Uncategorised") {
|
||||||
|
receipt.category = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
receipt.amount = transaction.amount.text.toString();
|
||||||
|
receipt.time = DateFormat("yyyy-MM-dd'T'hh:mm':00.000+01:00'")
|
||||||
|
.format(transaction.date)
|
||||||
|
.toString();
|
||||||
|
receipt.essential = transaction.isEssential.toString();
|
||||||
|
|
||||||
|
submitReceiptAPI(context, receipt);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> _recurringOptions = new List<String>(7);
|
||||||
|
List<String> _categories = new List<String>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
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;
|
||||||
|
var _fontSizeButton = _widgetHeight * 0.5;
|
||||||
|
|
||||||
|
if (_categories.isEmpty) {
|
||||||
|
Future<List<String>> _futureCats = getCats();
|
||||||
|
_categories.add("Fetching categories...");
|
||||||
|
_futureCats.then((value) {
|
||||||
|
_categories = value;
|
||||||
|
_categories.insert(0, "Uncategorised");
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_recurringOptions[0] = "None";
|
||||||
|
_recurringOptions[1] = "Daily";
|
||||||
|
_recurringOptions[2] = "Weekly";
|
||||||
|
_recurringOptions[3] = "Fortnightly";
|
||||||
|
_recurringOptions[4] = "Monthly";
|
||||||
|
_recurringOptions[5] = "Quarterly";
|
||||||
|
_recurringOptions[6] = "Yearly";
|
||||||
|
// these will be difficult to fetch from server as they are coded into the site's HTML rather than fetched
|
||||||
|
|
||||||
|
return PlatformScaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
backgroundColor: Colors.blue[400],
|
||||||
|
title: Text(
|
||||||
|
"Submit Receipt",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
centerTitle: true,
|
||||||
|
iconTheme: IconThemeData(color: Colors.black),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: <Widget>[
|
||||||
|
// each CHILD has its own horizontal padding because if the listView
|
||||||
|
// has padding, Android's end-of-scroll animation
|
||||||
|
// doesn't fit the screen properly and looks weird
|
||||||
|
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
MediaQuery.of(context).size.width * 0.025,
|
||||||
|
MediaQuery.of(context).size.height * 0.025,
|
||||||
|
0,
|
||||||
|
0.0),
|
||||||
|
child: Text(
|
||||||
|
"Receipt Details",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 26,
|
||||||
|
color: Colors.grey[700],
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
), // "Receipt Details" title
|
||||||
|
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
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(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
child: Text(
|
||||||
|
"Date/Time",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: _fontSize,
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
width: MediaQuery.of(context).size.width * 0.3,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
|
||||||
|
height: _widgetHeight,
|
||||||
|
width: MediaQuery.of(context).size.width * 0.6,
|
||||||
|
child: RaisedButton(
|
||||||
|
onPressed: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext builder) {
|
||||||
|
return Container(
|
||||||
|
height: MediaQuery.of(context)
|
||||||
|
.copyWith()
|
||||||
|
.size
|
||||||
|
.height /
|
||||||
|
3,
|
||||||
|
child: CupertinoDatePicker(
|
||||||
|
initialDateTime:
|
||||||
|
transaction.date.isAfter(DateTime.now())
|
||||||
|
? DateTime.now()
|
||||||
|
: transaction.date,
|
||||||
|
onDateTimeChanged: (DateTime newDate) {
|
||||||
|
setState(() => {
|
||||||
|
newDate.isAfter(DateTime.now())
|
||||||
|
? transaction.date =
|
||||||
|
DateTime.now()
|
||||||
|
: transaction.date = newDate,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
use24hFormat: true,
|
||||||
|
maximumDate: DateTime.now(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
transaction.date == null
|
||||||
|
? 'None set.'
|
||||||
|
: transaction.date.year != DateTime.now().year
|
||||||
|
? '${new DateFormat.MMMd().format(transaction.date)}' +
|
||||||
|
" " +
|
||||||
|
transaction.date.year.toString() +
|
||||||
|
" at " +
|
||||||
|
'${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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
), // Date/Time picker
|
||||||
|
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
MediaQuery.of(context).size.width * 0.05,
|
||||||
|
15,
|
||||||
|
MediaQuery.of(context).size.width * 0.05,
|
||||||
|
0.0),
|
||||||
|
child: Tooltip(
|
||||||
|
message: "Transaction payee",
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
child: Text(
|
||||||
|
"Payee",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: _fontSize,
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
width: MediaQuery.of(context).size.width * 0.3,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
|
||||||
|
height: _widgetHeight,
|
||||||
|
width: MediaQuery.of(context).size.width * 0.6,
|
||||||
|
child: RaisedButton(
|
||||||
|
onPressed: () {
|
||||||
|
var organisations = new FindOrganisations();
|
||||||
|
var orgDialog = organisations.dialog(context);
|
||||||
|
orgDialog.then((organisation) {
|
||||||
|
try {
|
||||||
|
organisation.name.length;
|
||||||
|
transaction.organisation = organisation;
|
||||||
|
// debugPrint(organisation.name);
|
||||||
|
setState(() {});
|
||||||
|
} catch (_) {
|
||||||
|
debugPrint("No organisation chosen.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
transaction.organisation.name == null
|
||||||
|
? 'Find'
|
||||||
|
: transaction.organisation.name.length > 14
|
||||||
|
? transaction.organisation.name
|
||||||
|
.substring(0, 12) +
|
||||||
|
"..."
|
||||||
|
: transaction.organisation.name,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white, fontSize: _fontSizeButton),
|
||||||
|
),
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
), // Organisation picker
|
||||||
|
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
MediaQuery.of(context).size.width * 0.05,
|
||||||
|
15,
|
||||||
|
MediaQuery.of(context).size.width * 0.05,
|
||||||
|
0.0),
|
||||||
|
child: Tooltip(
|
||||||
|
message: "Repeating?",
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
child: Text(
|
||||||
|
"Recurring",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: _fontSize,
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
width: MediaQuery.of(context).size.width * 0.3,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
|
||||||
|
height: _widgetHeight,
|
||||||
|
width: MediaQuery.of(context).size.width * 0.6,
|
||||||
|
child: RaisedButton(
|
||||||
|
onPressed: () {
|
||||||
|
transaction.recurring = _recurringOptions[0];
|
||||||
|
setState(() {});
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext builder) {
|
||||||
|
return Container(
|
||||||
|
height: MediaQuery.of(context)
|
||||||
|
.copyWith()
|
||||||
|
.size
|
||||||
|
.height /
|
||||||
|
3,
|
||||||
|
child: CupertinoPicker(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
children: _recurringOptions
|
||||||
|
.map((thisOption) => Text(thisOption,
|
||||||
|
style: TextStyle(fontSize: 30)))
|
||||||
|
.toList(),
|
||||||
|
onSelectedItemChanged: ((newValue) {
|
||||||
|
transaction.recurring =
|
||||||
|
_recurringOptions[newValue];
|
||||||
|
setState(() {});
|
||||||
|
}),
|
||||||
|
magnification: 1.1,
|
||||||
|
useMagnifier: true,
|
||||||
|
itemExtent: 36,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
transaction.recurring == null
|
||||||
|
? 'None'
|
||||||
|
: transaction.recurring,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white, fontSize: _fontSizeButton),
|
||||||
|
),
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
), // Recurring picker
|
||||||
|
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
MediaQuery.of(context).size.width * 0.05,
|
||||||
|
15,
|
||||||
|
MediaQuery.of(context).size.width * 0.05,
|
||||||
|
0.0),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
child: Text(
|
||||||
|
"Category",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: _fontSize,
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
width: MediaQuery.of(context).size.width * 0.3,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
|
||||||
|
height: _widgetHeight,
|
||||||
|
width: MediaQuery.of(context).size.width * 0.6,
|
||||||
|
child: Tooltip(
|
||||||
|
message: "Category of transaction",
|
||||||
|
child: RaisedButton(
|
||||||
|
onPressed: () {
|
||||||
|
transaction.category = _categories[0];
|
||||||
|
setState(() {});
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext builder) {
|
||||||
|
return Container(
|
||||||
|
height: MediaQuery.of(context)
|
||||||
|
.copyWith()
|
||||||
|
.size
|
||||||
|
.height /
|
||||||
|
3,
|
||||||
|
child: CupertinoPicker(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
children: _categories
|
||||||
|
.map((thisOption) => Text(
|
||||||
|
thisOption,
|
||||||
|
style: TextStyle(fontSize: 30),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
onSelectedItemChanged: ((newValue) {
|
||||||
|
transaction.category =
|
||||||
|
_categories[newValue];
|
||||||
|
setState(() {});
|
||||||
|
}),
|
||||||
|
magnification: 1.1,
|
||||||
|
useMagnifier: true,
|
||||||
|
itemExtent: 36,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
transaction.category == null
|
||||||
|
? 'None'
|
||||||
|
: transaction.category,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white, fontSize: _fontSizeButton),
|
||||||
|
),
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
), // Category picker
|
||||||
|
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
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(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
child: Text(
|
||||||
|
"Essential",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: _fontSize,
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
width: MediaQuery.of(context).size.width * 0.3,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
height: _widgetHeight,
|
||||||
|
width: MediaQuery.of(context).size.width * 0.6,
|
||||||
|
child: Checkbox(
|
||||||
|
value: transaction.isEssential,
|
||||||
|
onChanged: ((value) {
|
||||||
|
setState(() => transaction.isEssential = value);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
), // Essential
|
||||||
|
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
MediaQuery.of(context).size.width * 0.05,
|
||||||
|
15,
|
||||||
|
MediaQuery.of(context).size.width * 0.05,
|
||||||
|
0.0),
|
||||||
|
child: Tooltip(
|
||||||
|
message: "Transaction amount",
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
child: Text(
|
||||||
|
"Amount",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: _fontSize,
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
width: MediaQuery.of(context).size.width * 0.3,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
|
||||||
|
height: _widgetHeight,
|
||||||
|
width: MediaQuery.of(context).size.width * 0.6,
|
||||||
|
child: TextField(
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: _fontSize,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
controller: transaction.amount,
|
||||||
|
decoration: InputDecoration(hintText: "0.00"),
|
||||||
|
keyboardType: TextInputType.numberWithOptions(
|
||||||
|
decimal: true, signed: true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
), // Amount picker
|
||||||
|
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
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(
|
||||||
|
height: _widgetHeight * 1.7,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(2),
|
||||||
|
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 {
|
||||||
|
if (transaction.amount.text == "" ||
|
||||||
|
transaction.organisation.name == null) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return _invalidDialog(context);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (double.tryParse(
|
||||||
|
transaction.amount.text) !=
|
||||||
|
null &&
|
||||||
|
double.tryParse(transaction.amount.text) >
|
||||||
|
0) {
|
||||||
|
_submitReceipt(transaction);
|
||||||
|
} else {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return _invalidDialog(context);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return _invalidDialog(context);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,9 +10,9 @@ class SplashScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SplashScreenState extends State<SplashScreen> {
|
class _SplashScreenState extends State<SplashScreen> {
|
||||||
final int splashDuration = 3;
|
final int splashDuration = 1;
|
||||||
|
|
||||||
startTime() async {
|
Future<Timer> 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,10 +27,7 @@ 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(
|
||||||
|
@ -38,10 +35,9 @@ 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'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -49,7 +45,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 Statement 2019",
|
"© Copyright Pear Trading 2019",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16.0,
|
fontSize: 16.0,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
|
|
|
@ -1,29 +1,25 @@
|
||||||
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/functions/logout.dart';
|
import 'package:local_spend/pages/customerGraphs.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:local_spend/pages/orgGraphs.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';
|
|
||||||
|
|
||||||
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 = "-";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -36,195 +32,61 @@ class StatsPageState extends State<StatsPage> {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
_saveCurrentRoute(String lastRoute) async {
|
void _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) {
|
||||||
|
print(value);
|
||||||
|
userType =
|
||||||
|
'${value[0].toUpperCase()}${value.substring(1)}'; // capitalises first letter
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return PlatformScaffold(
|
return PlatformScaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.blue[400],
|
backgroundColor: Colors.blue[400],
|
||||||
title: Text(
|
title: Row(
|
||||||
"Statistics",
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
style: TextStyle(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
fontSize: 20,
|
children: [
|
||||||
color: Colors.white,
|
Text(
|
||||||
),
|
"Statistics",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(padding: EdgeInsets.symmetric(horizontal: 4)),
|
||||||
|
Text(
|
||||||
|
userType,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
color: Colors.white70,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
// leading: BackButton(),
|
|
||||||
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: ListView(
|
child: (userType == "-"
|
||||||
children: <Widget>[
|
? null
|
||||||
// some graphs and charts here etc
|
: (userType.toLowerCase() == "customer"
|
||||||
// Container(
|
? CustomerGraphs()
|
||||||
// padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
: OrgGraphs())),
|
||||||
// child : Text(
|
|
||||||
// "Really Cool Chart",
|
|
||||||
// textAlign: TextAlign.center,
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: 22.0,
|
|
||||||
// color: Colors.black,
|
|
||||||
// fontWeight: FontWeight.bold,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
//
|
|
||||||
// Container(
|
|
||||||
// height: 250,
|
|
||||||
//// width: 250,
|
|
||||||
// child: new DonutPieChart.withSampleData()
|
|
||||||
// ),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
|
||||||
child : Text(
|
|
||||||
"GroupedBarChart",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
height: 200,
|
|
||||||
// width: 250,
|
|
||||||
child: new GroupedBarChart.withSampleData()
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
|
||||||
child : Text(
|
|
||||||
"BucketingAxisScatterPlotChart",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
height: 200,
|
|
||||||
// width: 250,
|
|
||||||
child: new BucketingAxisScatterPlotChart.withSampleData()
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,20,0.0,0.0),
|
|
||||||
child : Text(
|
|
||||||
"PieOutsideLabelChart",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
height: 200,
|
|
||||||
// width: 250,
|
|
||||||
child: new PieOutsideLabelChart.withSampleData()
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
|
||||||
child : Text(
|
|
||||||
"DonutAutoLabelChart",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
height: 200,
|
|
||||||
// width: 250,
|
|
||||||
child: new DonutAutoLabelChart.withSampleData()
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
|
||||||
child : Text(
|
|
||||||
"DonutPieChart",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
height: 200,
|
|
||||||
// width: 250,
|
|
||||||
child: new DonutPieChart.withSampleData()
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
|
||||||
child : Text(
|
|
||||||
"NumericComboLineBarChart",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
height: 200,
|
|
||||||
// width: 250,
|
|
||||||
child: new NumericComboLineBarChart.withSampleData()
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0,17,0.0,0.0),
|
|
||||||
child : Text(
|
|
||||||
"LegendWithMeasures",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
height: 200,
|
|
||||||
// width: 250,
|
|
||||||
child: new LegendWithMeasures.withSampleData()
|
|
||||||
),
|
|
||||||
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
376
pubspec.lock
376
pubspec.lock
|
@ -1,125 +1,160 @@
|
||||||
# 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.36.4"
|
version: "0.39.15"
|
||||||
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.5.2"
|
version: "1.6.0"
|
||||||
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.2.0"
|
version: "2.4.2"
|
||||||
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: "1.0.4"
|
version: "2.0.0"
|
||||||
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.1.4"
|
version: "1.3.0"
|
||||||
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.0"
|
version: "0.4.2"
|
||||||
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: "1.1.0"
|
version: "2.1.4"
|
||||||
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.0.5"
|
version: "1.3.10"
|
||||||
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.6.1"
|
version: "1.10.0"
|
||||||
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: "3.0.6"
|
version: "5.2.0"
|
||||||
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.2.2"
|
version: "4.3.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: "6.6.0"
|
version: "7.1.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.2"
|
version: "1.1.3"
|
||||||
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.6.0"
|
version: "0.9.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.6.0"
|
version: "0.9.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.2.0"
|
version: "3.4.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.11"
|
version: "1.14.13"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -133,42 +168,56 @@ packages:
|
||||||
name: crypto
|
name: crypto
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.6"
|
version: "2.1.5"
|
||||||
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.0"
|
version: "0.16.2"
|
||||||
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.2"
|
version: "0.1.3"
|
||||||
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.2.7"
|
version: "1.3.6"
|
||||||
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: "0.1.8"
|
version: "1.0.0"
|
||||||
|
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.9"
|
version: "0.10.11"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -187,31 +236,50 @@ packages:
|
||||||
name: flutter_linkify
|
name: flutter_linkify
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "3.1.3"
|
||||||
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"
|
||||||
front_end:
|
flutter_web_plugins:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description: flutter
|
||||||
name: front_end
|
source: sdk
|
||||||
url: "https://pub.dartlang.org"
|
version: "0.0.0"
|
||||||
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.1.7"
|
version: "1.2.0"
|
||||||
|
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:
|
||||||
|
@ -225,91 +293,91 @@ packages:
|
||||||
name: html
|
name: html
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.14.0+2"
|
version: "0.14.0+3"
|
||||||
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.0+2"
|
version: "0.12.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.1.0"
|
version: "2.2.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.3"
|
version: "3.1.4"
|
||||||
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.15.8"
|
version: "0.16.1"
|
||||||
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.3"
|
version: "0.3.4"
|
||||||
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.1+1"
|
version: "0.6.2"
|
||||||
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: "2.3.0"
|
version: "3.0.1"
|
||||||
json_serializable:
|
json_serializable:
|
||||||
dependency: "direct dev"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: json_serializable
|
name: json_serializable
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "3.3.0"
|
||||||
kernel:
|
linkify:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: kernel
|
name: linkify
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.19"
|
version: "2.1.0"
|
||||||
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.3+2"
|
version: "0.11.4"
|
||||||
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.5"
|
version: "0.12.8"
|
||||||
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.6"
|
version: "1.1.8"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -317,34 +385,76 @@ 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.0.5"
|
version: "1.9.3"
|
||||||
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.6.2"
|
version: "1.7.0"
|
||||||
pedantic:
|
path_provider_linux:
|
||||||
dependency: transitive
|
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:
|
||||||
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: pedantic
|
name: pedantic
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
version: "1.9.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:
|
||||||
|
@ -352,41 +462,104 @@ 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.2"
|
version: "1.4.4"
|
||||||
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.4"
|
version: "0.1.5"
|
||||||
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.0.3"
|
version: "2.1.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.4.3"
|
version: "0.5.8"
|
||||||
|
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.5"
|
version: "0.7.7"
|
||||||
shelf_web_socket:
|
shelf_web_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -394,6 +567,13 @@ 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
|
||||||
|
@ -405,21 +585,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.4+2"
|
version: "0.9.6"
|
||||||
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.5.5"
|
version: "1.7.0"
|
||||||
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.3"
|
version: "1.9.5"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -433,14 +613,21 @@ packages:
|
||||||
name: stream_transform
|
name: stream_transform
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.19"
|
version: "1.2.0"
|
||||||
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.4"
|
version: "1.0.5"
|
||||||
|
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:
|
||||||
|
@ -454,28 +641,56 @@ packages:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.5"
|
version: "0.2.17"
|
||||||
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+1"
|
version: "0.1.1+2"
|
||||||
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.1.6"
|
version: "1.2.0"
|
||||||
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: "3.0.3"
|
version: "5.5.0"
|
||||||
|
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:
|
||||||
|
@ -489,21 +704,28 @@ packages:
|
||||||
name: watcher
|
name: watcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.7+10"
|
version: "0.9.7+15"
|
||||||
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.0.13"
|
version: "1.1.0"
|
||||||
|
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.1.16"
|
version: "2.2.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.3.0-dev.0.1 <3.0.0"
|
dart: ">=2.9.0-14.0.dev <3.0.0"
|
||||||
flutter: ">=0.1.4 <2.0.0"
|
flutter: ">=1.16.3 <2.0.0"
|
||||||
|
|
27
pubspec.yaml
27
pubspec.yaml
|
@ -10,21 +10,24 @@ description: Local Spend Tracker
|
||||||
version: 1.0.0+1
|
version: 1.0.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.0.0-dev.68.0 <3.0.0"
|
sdk: ">=2.2.2 <3.0.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
shared_preferences: ^0.4.2
|
shared_preferences: ^0.5.8
|
||||||
url_launcher: ^3.0.3
|
url_launcher: ^5.5.0
|
||||||
json_annotation : ^2.2.0
|
json_annotation : ^3.0.1
|
||||||
http: ^0.12.0+2
|
http: ^0.12.0+2
|
||||||
datetime_picker_formfield: ^0.1.8
|
datetime_picker_formfield: ^1.0.0
|
||||||
flutter_linkify: ^1.0.3
|
flutter_linkify: ^3.1.3
|
||||||
flutter_fadein: ^1.1.1
|
flutter_fadein: ^1.1.1
|
||||||
charts_flutter: ^0.6.0
|
charts_flutter: ^0.9.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
|
||||||
|
@ -32,9 +35,8 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^1.1.3
|
pedantic: ^1.4.0
|
||||||
json_serializable: ^2.1.2
|
build_runner: ^1.10.0
|
||||||
|
|
||||||
|
|
||||||
# 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
|
||||||
|
@ -55,6 +57,11 @@ 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.
|
||||||
|
|
||||||
|
|
|
@ -11,20 +11,16 @@ import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:local_spend/main.dart';
|
import 'package:local_spend/main.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
testWidgets('GO button repetition 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());
|
||||||
|
|
||||||
// Verify that our counter starts at 0.
|
// Tap the GO button and trigger a frame.
|
||||||
expect(find.text('0'), findsOneWidget);
|
await tester.tap(find.byKey(Key("goButton")));
|
||||||
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 our counter has incremented.
|
// Verify that the dialog has shown
|
||||||
expect(find.text('0'), findsNothing);
|
// expect(find.text('GO'), findsNothing);
|
||||||
expect(find.text('1'), findsOneWidget);
|
// expect(find.text('Invalid data'), findsOneWidget);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue