many UI improvements and subtle tweaks

This commit is contained in:
Felix 2019-08-20 13:54:45 +01:00
parent 9a5bfbaaf0
commit 231ed2c9df
15 changed files with 191 additions and 153 deletions

View File

@ -5,7 +5,7 @@ import 'package:local_spend/common/functions/get_token.dart';
import 'package:flutter/material.dart';
Future<List<String>> getCategories() async {
const url = "https://dev.peartrade.org/api/search/category";
const url = "https://dev.localspend.co.uk/api/search/category";
var token;
await getToken().then((result) {

View File

@ -64,7 +64,7 @@ class Organisations {
}
Future<List<Organisation>> findOrganisations(String search) async {
final url = "https://dev.peartrade.org/api/search";
final url = "https://dev.localspend.co.uk/api/search";
var token;
await getToken().then((result) {

View File

@ -28,7 +28,7 @@ class GraphData {
];
}
final url = "https://dev.peartrade.org/api/v1/customer/graphs";
final url = "https://dev.localspend.co.uk/api/v1/customer/graphs";
SharedPreferences preferences = await SharedPreferences.getInstance();
Map<String, String> body = {
@ -88,13 +88,13 @@ class GraphData {
// TODO: Show available graph types based on whether or not the user is an Organisation or User
/// Customer graph types: https://dev.peartrade.org/api/v1/customer/graphs
/// 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.peartrade.org/api/stats/[name] as {"session_key":"[boop beep]"}
/// 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
@ -110,7 +110,7 @@ class GraphData {
/// HTTP POST request sample:
/// {"graph":"total_last_week","session_key":"blahblahblah"}
final url = "https://dev.peartrade.org/api/v1/customer/graphs";
final url = "https://dev.localspend.co.uk/api/v1/customer/graphs";
SharedPreferences preferences = await SharedPreferences.getInstance();
Map<String, String> body = {
@ -182,7 +182,7 @@ class OrgGraphData {
];
}
final url = "https://dev.peartrade.org/api/v1/customer/graphs";
final url = "https://dev.localspend.co.uk/api/v1/customer/graphs";
SharedPreferences preferences = await SharedPreferences.getInstance();
Map<String, String> body = {
@ -242,13 +242,13 @@ class OrgGraphData {
// TODO: Show available graph types based on whether or not the user is an Organisation or User
/// Customer graph types: https://dev.peartrade.org/api/v1/customer/graphs
/// 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.peartrade.org/api/stats/[name] as {"session_key":"[boop beep]"}
/// 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
@ -264,7 +264,7 @@ class OrgGraphData {
/// HTTP POST request sample:
/// {"graph":"total_last_week","session_key":"blahblahblah"}
final url = "https://dev.peartrade.org/api/v1/customer/graphs";
final url = "https://dev.localspend.co.uk/api/v1/customer/graphs";
SharedPreferences preferences = await SharedPreferences.getInstance();
Map<String, String> body = {

View File

@ -11,26 +11,27 @@ Future<void> _incorrectDialog(BuildContext context, bool isLoginWrong) async {
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Unable to Login"),
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();
},
),
],
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(
BuildContext context, String email, String password) async {
//var apiUrl = ConfigWrapper.of(context).apiKey;
final url = "https://dev.peartrade.org/api/login";
Future<LoginModel> requestLoginAPI(BuildContext context, String email, String password) async {
final url = "https://dev.localspend.co.uk/api/login";
Map<String, String> body = {
'email': email,
@ -43,7 +44,7 @@ Future<LoginModel> requestLoginAPI(
final response = await http.post(
url,
body: json.encode(body),
);
).timeout(Duration(seconds: 5));
if (response.statusCode == 200) {
final responseJson = json.decode(response.body);
@ -61,6 +62,8 @@ Future<LoginModel> requestLoginAPI(
return null;
}
} on TimeoutException catch (_) {
_incorrectDialog(context, false);
} catch (error) {
debugPrint(error.toString());
_incorrectDialog(context, false);

View File

@ -8,7 +8,7 @@ import 'package:local_spend/common/functions/save_logout.dart';
import 'package:local_spend/model/json/login_model.dart';
Future<LoginModel> requestLogoutAPI(BuildContext context) async {
final url = "https://dev.peartrade.org/api/logout";
final url = "https://dev.localspend.co.uk/api/logout";
var token;

View File

@ -23,7 +23,7 @@ class Receipt {
Future<LoginModel> submitReceiptAPI(
BuildContext context, Receipt receipt) async {
//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();

View File

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
class AnimatedBackground extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tween = MultiTrackTween([
Track("color1").add(Duration(seconds: 4),
ColorTween(begin: Colors.lightBlue, end: Colors.lightBlue[300])),
]);
return ControlledAnimation(
playback: Playback.MIRROR,
tween: tween,
duration: tween.duration,
builder: (context, animation) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [animation["color1"], Colors.lightBlue])),
);
},
);
}
}

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

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

2
lib/env/dev.json vendored
View File

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

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

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

2
lib/env/prod.json vendored
View File

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

View File

@ -18,6 +18,7 @@ class LoginPage extends StatefulWidget {
}
class LoginPageState extends State<LoginPage> {
bool _isLoggingIn = false;
final TextEditingController _emailController = TextEditingController(/*text: 'test@example.com'*/); // remove
final TextEditingController _passwordController = TextEditingController(/*text: 'abc123'*/); // remove
bool _saveLoginDetails = true; // I am extremely sorry for the placement of this variable
@ -69,6 +70,7 @@ class LoginPageState extends State<LoginPage> {
}
login(String username, String password) async {
_isLoggingIn = true;
SystemChannels.textInput.invokeMethod('TextInput.hide');
SharedPreferences preferences = await SharedPreferences.getInstance();
@ -82,8 +84,9 @@ class LoginPageState extends State<LoginPage> {
print("details cleared");
}
requestLoginAPI(context, username,
password);
requestLoginAPI(context, username, password).then((value) {
_isLoggingIn = false;
});
}
@override
@ -98,14 +101,6 @@ class LoginPageState extends State<LoginPage> {
}
},
child: PlatformScaffold(
// drawer: BasicDrawer(),
// body: Container(
// decoration: BoxDecoration(color: Colors.white),
// margin: const EdgeInsets.all(20),
// child: Padding(
// padding: EdgeInsets.fromLTRB(30.0, 170.0, 30.0, 0.0),
// child: ListView(
// children: <Widget>[
body: Container(
decoration: new BoxDecoration(
gradient: new LinearGradient(
@ -115,14 +110,15 @@ class LoginPageState extends State<LoginPage> {
end: Alignment.bottomCenter,
),
),
child: Container(
child: AnimatedContainer(
duration: Duration(seconds: 2),
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 :/
child: AnimatedContainer(
duration: Duration(seconds: 2),
margin: EdgeInsets.fromLTRB(15,0,15,0),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/launch_image.png')
@ -177,39 +173,42 @@ class LoginPageState extends State<LoginPage> {
Padding(
padding: EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 30.0),
child : Material(
child: new Container(
decoration: new BoxDecoration(
border: new Border.all(color : Colors.transparent, width: 2),
borderRadius: BorderRadius.all(Radius.circular(2)),
gradient: new LinearGradient(
colors: [
Colors.blue[300],
Colors.blue[500],
],
stops: [0,1],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
child: Opacity(
opacity: _isLoggingIn ? 0.5 : 1,
child : Material(
child: new Container(
decoration: new BoxDecoration(
border: new Border.all(color : Colors.transparent, width: 2),
borderRadius: BorderRadius.all(Radius.circular(2)),
gradient: new LinearGradient(
colors: [
Colors.blue[300],
Colors.blue[500],
],
stops: [0,1],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
),
child : Material(
type: MaterialType.transparency,
child : Material(
type: MaterialType.transparency,
child : InkWell(
onTap: () => login( _emailController.text, _passwordController.text),
child: new Container(
width: 100,
height: 50,
child: new Center(
child: new Text(
'GO', style: new TextStyle(fontSize: 18, color: Colors.white),),
child : InkWell(
onTap: _isLoggingIn ? null : () => login( _emailController.text, _passwordController.text),
child: new Container(
width: 100,
height: 50,
child: new Center(
child: new Text(
'GO', style: new TextStyle(fontSize: 18, color: Colors.white),),
),
),
),
),
),
),
),
),
),
@ -229,22 +228,6 @@ class LoginPageState extends State<LoginPage> {
});
},
),
/*child: LabeledCheckboxWithIcon(
label : "SAVE LOGIN",
textStyle: TextStyle(fontSize: 18, color: Colors.black54, fontWeight: FontWeight.bold),
icon: Icons.account_box, // need to remove icon padding!!
iconSize: 18,
iconColor: Colors.black54,
padding: const EdgeInsets.fromLTRB(0,0,0,0),
value : _saveLoginDetails,
onChanged: (bool newValue) {
setState(() {
_saveLoginDetails = newValue;
});
},
),*/
),
],
),

View File

@ -3,6 +3,7 @@ import 'package:local_spend/common/platform/platform_scaffold.dart';
import 'package:intl/intl.dart';
import 'dart:core';
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';
@ -72,7 +73,7 @@ class ReceiptPage2State extends State<ReceiptPage2> {
// "purchase_time":"2019-08-12T11:06:00.000+01:00",
// "organisation_id":59661,
// "essential":false,
// "session_key":"C438432A-B775-11E9-8EE8-147589E69626"
// "session_key":"C438440A-B775-11E9-8EE8-147589E69626"
// }
Receipt receipt = new Receipt();
@ -149,7 +150,7 @@ class ReceiptPage2State extends State<ReceiptPage2> {
child : Text(
"Receipt Details",
style: TextStyle(
fontSize: 24,
fontSize: 26,
color: Colors.grey[700],
fontWeight: FontWeight.bold,
),
@ -166,17 +167,18 @@ class ReceiptPage2State extends State<ReceiptPage2> {
child : Text(
"Date/Time",
style: TextStyle(
fontSize: 18,
fontSize: 22,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
width: 110,
width: MediaQuery.of(context).size.width * 0.3,
),
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
height: 32.0,
height: 40.0,
width: MediaQuery.of(context).size.width * 0.6,
child: RaisedButton(
onPressed: () {
showModalBottomSheet(
@ -208,7 +210,7 @@ class ReceiptPage2State extends State<ReceiptPage2> {
? '${new DateFormat.MMMd().format(transaction.date)}' + ", " + '${new DateFormat.Hm().format(transaction.date)}'
: '${new DateFormat.MMMd().format(transaction.date)}' + " " + transaction.date.year.toString() + ", " + '${new DateFormat.Hm().format(transaction.date)}',
style:
TextStyle(color: Colors.white, fontSize: 18.0),
TextStyle(color: Colors.white, fontSize: 22.0),
),
color: Colors.blue,
),
@ -228,17 +230,18 @@ class ReceiptPage2State extends State<ReceiptPage2> {
child : Text(
"Payee",
style: TextStyle(
fontSize: 18,
fontSize: 22,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
width: 110,
width: MediaQuery.of(context).size.width * 0.3,
),
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
height: 32.0,
height: 40.0,
width: MediaQuery.of(context).size.width * 0.6,
child: RaisedButton(
onPressed: () {
var organisations = new FindOrganisations();
@ -261,7 +264,7 @@ class ReceiptPage2State extends State<ReceiptPage2> {
? transaction.organisation.name.substring(0,12) + "..."
: transaction.organisation.name,
style:
TextStyle(color: Colors.white, fontSize: 18.0),
TextStyle(color: Colors.white, fontSize: 22.0),
),
color: Colors.blue,
),
@ -281,17 +284,18 @@ class ReceiptPage2State extends State<ReceiptPage2> {
child : Text(
"Recurring",
style: TextStyle(
fontSize: 18,
fontSize: 22,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
width: 110,
width: MediaQuery.of(context).size.width * 0.3,
),
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
height: 32.0,
height: 40.0,
width: MediaQuery.of(context).size.width * 0.6,
child: RaisedButton(
onPressed: () {
showModalBottomSheet(
@ -306,7 +310,7 @@ class ReceiptPage2State extends State<ReceiptPage2> {
transaction.recurring = _sampleRecurringOptions[newValue];
setState(() {});
}),
itemExtent: 32,
itemExtent: 40,
),
);
});
@ -316,7 +320,7 @@ class ReceiptPage2State extends State<ReceiptPage2> {
? 'None'
: transaction.recurring,
style:
TextStyle(color: Colors.white, fontSize: 18.0),
TextStyle(color: Colors.white, fontSize: 22.0),
),
color: Colors.blue,
),
@ -334,17 +338,18 @@ class ReceiptPage2State extends State<ReceiptPage2> {
child : Text(
"Category",
style: TextStyle(
fontSize: 18,
fontSize: 22,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
width: 110,
width: MediaQuery.of(context).size.width * 0.3,
),
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
height: 32.0,
height: 40.0,
width: MediaQuery.of(context).size.width * 0.6,
child: Tooltip(
message: "Category of transaction",
child: RaisedButton(
@ -361,7 +366,7 @@ class ReceiptPage2State extends State<ReceiptPage2> {
transaction.category = _categories[newValue];
setState(() {});
}),
itemExtent: 32,
itemExtent: 40,
),
);
});
@ -371,7 +376,7 @@ class ReceiptPage2State extends State<ReceiptPage2> {
? 'None'
: transaction.category,
style:
TextStyle(color: Colors.white, fontSize: 18.0),
TextStyle(color: Colors.white, fontSize: 22.0),
),
color: Colors.blue,
),
@ -391,16 +396,17 @@ class ReceiptPage2State extends State<ReceiptPage2> {
child : Text(
"Essential",
style: TextStyle(
fontSize: 18,
fontSize: 22,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
width: 95,
width: MediaQuery.of(context).size.width * 0.3,
),
Container(
height: 32.0,
height: 40.0,
width: MediaQuery.of(context).size.width * 0.6,
child: Checkbox(
value: transaction.isEssential,
onChanged: ((value) {
@ -423,19 +429,19 @@ class ReceiptPage2State extends State<ReceiptPage2> {
child : Text(
"Amount",
style: TextStyle(
fontSize: 18,
fontSize: 22,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
width: 110,
width: MediaQuery.of(context).size.width * 0.3,
),
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
height: 32.0,
width: 100,
height: 40.0,
width: MediaQuery.of(context).size.width * 0.6,
child: TextField(
controller: transaction.amount,
decoration: InputDecoration(
@ -454,46 +460,57 @@ class ReceiptPage2State extends State<ReceiptPage2> {
child: Tooltip(
message: "Submit receipt",
child: Container(
height: 65.0,
child: RaisedButton(
onPressed: () {
try {
if (transaction.amount.text == "" || transaction.organisation.name == null) {
showDialog(
context: context,
builder: (BuildContext context) {
return _invalidDialog(context);
decoration: new BoxDecoration(
border: new Border.all(color : Colors.transparent, width: 2),
borderRadius: BorderRadius.all(Radius.circular(2)),
),
height: 75.0,
child: Stack(
children: [
AnimatedBackground(),
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);
}
);
}
}
);
} 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);
}
);
}
},
child: Text("GO",
style:
TextStyle(color: Colors.white, fontSize: 22.0),
),
color: Colors.blue,
}
catch (_) {
showDialog(
context: context,
builder: (BuildContext context) {
return _invalidDialog(context);
}
);
}
},
),
),
],
),
),
),

View File

@ -394,6 +394,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.3"
simple_animations:
dependency: "direct main"
description:
name: simple_animations
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.3"
sky_engine:
dependency: transitive
description: flutter

View File

@ -25,6 +25,7 @@ dependencies:
flutter_linkify: ^1.0.3
flutter_fadein: ^1.1.1
charts_flutter: ^0.6.0
simple_animations: ^1.3.3
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2