Initial framework in place, non-functional POST & env vars atm

This commit is contained in:
Finn 2019-05-08 19:54:14 +01:00
parent 3bc940063a
commit 5c718fc14b
No known key found for this signature in database
GPG key ID: 7455B4B17685B598
29 changed files with 1263 additions and 170 deletions

View file

@ -1,29 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
</code_scheme>
</component>

View file

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="main.dart" type="FlutterRunConfigurationType" factoryName="Flutter">
<configuration default="false" name="main.dart" type="FlutterRunConfigurationType" factoryName="Flutter" singleton="false">
<option name="filePath" value="$PROJECT_DIR$/lib/main.dart" />
<method />
<method v="2" />
</configuration>
</component>

View file

@ -15,6 +15,12 @@ For help getting started with Flutter, view our
[online documentation](https://flutter.io/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
## Environments
To build an apk from dev, use:
flutter build apk -t lib/main_dev.dart
## Links and Things
https://github.com/putraxor/flutter-login-ui
https://github.com/putraxor/flutter-login-ui
https://github.com/pbirdsall/medium_splash_tokenauth

View file

@ -0,0 +1,50 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
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/config.dart';
import 'package:flutter/foundation.dart';
Future<LoginModel> requestLoginAPI(
BuildContext context, String email, String password) async {
//var apiUrl = ConfigWrapper.of(context).apiKey;
final url = "https://dev.peartrade.org/api/login";
Map<String, String> body = {
'email': email,
'password': password,
};
debugPrint('$body');
final response = await http.post(
url,
body: body,
);
debugPrint(response.body);
if (response.statusCode == 200) {
final responseJson = json.decode(response.body);
var user = new LoginModel.fromJson(responseJson);
saveCurrentLogin(responseJson);
Navigator.of(context).pushReplacementNamed('/HomePage');
return LoginModel.fromJson(responseJson);
} else {
final responseJson = json.decode(response.body);
saveCurrentLogin(responseJson);
showDialogSingleButton(
context,
"Unable to Login",
"You may have supplied an invalid 'Email' / 'Password' combination. Please try again or email an administrator.",
"OK");
return null;
}
}

View file

@ -0,0 +1,31 @@
import 'dart:io';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:local_spend/common/functions/get_token.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 {
final url = "https://www.yoururl.com/logout";
var token;
await getToken().then((result) {
token = result;
});
final response = await http.post(
url,
headers: {HttpHeaders.authorizationHeader: "Token $token"},
);
if (response.statusCode == 200) {
saveLogout();
return null;
} else {
saveLogout();
return null;
}
}

View file

@ -0,0 +1,8 @@
import 'package:shared_preferences/shared_preferences.dart';
getToken() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
String getToken = await preferences.getString("LastToken");
return getToken;
}

View file

@ -0,0 +1,30 @@
import 'package:local_spend/model/json/login_model.dart';
import 'package:shared_preferences/shared_preferences.dart';
saveCurrentLogin(Map responseJson) async {
SharedPreferences preferences = await SharedPreferences.getInstance();
var user;
if ((responseJson != null && responseJson.isNotEmpty)) {
user = LoginModel.fromJson(responseJson).userName;
} else {
user = "";
}
var token = (responseJson != null && responseJson.isNotEmpty)
? LoginModel.fromJson(responseJson).token
: "";
var email = (responseJson != null && responseJson.isNotEmpty)
? LoginModel.fromJson(responseJson).email
: "";
var pk = (responseJson != null && responseJson.isNotEmpty)
? LoginModel.fromJson(responseJson).userId
: 0;
await preferences.setString(
'LastUser', (user != null && user.length > 0) ? user : "");
await preferences.setString(
'LastToken', (token != null && token.length > 0) ? token : "");
await preferences.setString(
'LastEmail', (email != null && email.length > 0) ? email : "");
await preferences.setInt('LastUserId', (pk != null && pk > 0) ? pk : 0);
}

View file

@ -0,0 +1,10 @@
import 'package:shared_preferences/shared_preferences.dart';
saveLogout() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString('LastUser', "");
await preferences.setString('LastToken', "");
await preferences.setString('LastEmail', "");
await preferences.setInt('LastUserId', 0);
}

View file

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
void showDialogSingleButton(
BuildContext context, String title, String message, String buttonLabel) {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text(title),
content: new Text(message),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text(buttonLabel),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}

View file

@ -0,0 +1,72 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class PlatformScaffold extends StatelessWidget {
final Key key;
final PreferredSizeWidget appBar;
final Widget body;
final Widget floatingActionButton;
final FloatingActionButtonLocation floatingActionButtonLocation;
final FloatingActionButtonAnimator floatingActionButtonAnimator;
final List<Widget> persistentFooterButtons;
final Widget drawer;
final Widget endDrawer;
final Widget bottomNavigationBar;
final Color backgroundColor;
final bool resizeToAvoidBottomPadding;
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
Widget build(BuildContext context) {
return Platform.isIOS
? Scaffold(
key: key,
appBar: appBar,
body: body,
floatingActionButton: floatingActionButton,
persistentFooterButtons: persistentFooterButtons,
floatingActionButtonLocation: floatingActionButtonLocation,
floatingActionButtonAnimator: floatingActionButtonAnimator,
drawer: endDrawer,
endDrawer: drawer,
bottomNavigationBar: bottomNavigationBar,
backgroundColor: backgroundColor,
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
primary: primary,
)
: Scaffold(
key: key,
appBar: appBar,
body: body,
floatingActionButton: floatingActionButton,
persistentFooterButtons: persistentFooterButtons,
floatingActionButtonLocation: floatingActionButtonLocation,
floatingActionButtonAnimator: floatingActionButtonAnimator,
drawer: drawer,
endDrawer: endDrawer,
bottomNavigationBar: bottomNavigationBar,
backgroundColor: backgroundColor,
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
primary: primary,
);
}
}

View file

@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:local_spend/common/apifunctions/request_logout_api.dart';
class BasicDrawer extends StatefulWidget {
@override
_BasicDrawerState createState() => _BasicDrawerState();
}
class _BasicDrawerState extends State<BasicDrawer> {
@override
Widget build(BuildContext context) {
return Drawer(
child: Container(
padding: new EdgeInsets.all(32.0),
child: ListView(
children: <Widget>[
ListTile(
title: Text(
"Submit Receipt",
style: TextStyle(color: Colors.black, fontSize: 20.0),
),
onTap: () {
requestLogoutAPI(context);
Navigator.of(context).pushNamed('/ReceiptPage');
},
),
ListTile(
title: Text(
"About",
style: TextStyle(color: Colors.black, fontSize: 20.0),
),
onTap: () {
SystemChannels.textInput.invokeMethod('TextInput.hide');
// Here I have not implemented an actual about screen, but if you did you would navigate to it's route
// Navigator.of(context).pushReplacementNamed('/AboutScreen');
},
),
ListTile(
title: Text(
"Logout",
style: TextStyle(color: Colors.black, fontSize: 20.0),
),
onTap: () {
requestLogoutAPI(context);
Navigator.of(context).pushReplacementNamed('/LoginPage');
},
),
],
),
),
);
}
}

46
lib/config.dart Normal file
View file

@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:json_annotation/json_annotation.dart';
part 'config.g.dart';
@JsonSerializable(createToJson: false)
class Config {
final String env;
final bool production;
final String apiKey;
Config({this.env, this.production, this.apiKey});
factory Config.fromJson(Map<String, dynamic> json) => _$ConfigFromJson(json);
}
class ConfigWrapper extends StatelessWidget {
ConfigWrapper({Key key, this.config, this.child});
@override
Widget build(BuildContext context) {
return new _InheritedConfig(config: this.config, child: this.child);
}
static Config of(BuildContext context) {
final _InheritedConfig inheritedConfig =
context.inheritFromWidgetOfExactType(_InheritedConfig);
return inheritedConfig.config;
}
final Config config;
final Widget child;
}
class _InheritedConfig extends InheritedWidget {
const _InheritedConfig(
{Key key, @required this.config, @required Widget child})
: assert(config != null),
assert(child != null),
super(key: key, child: child);
final Config config;
@override
bool updateShouldNotify(_InheritedConfig oldWidget) =>
config != oldWidget.config;
}

14
lib/config.g.dart Normal file
View file

@ -0,0 +1,14 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'config.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Config _$ConfigFromJson(Map<String, dynamic> json) {
return Config(
env: json['env'] as String,
production: json['production'] as bool,
apiKey: json['apiKey'] as String);
}

6
lib/env/dev.dart vendored Normal file
View file

@ -0,0 +1,6 @@
import 'package:json_annotation/json_annotation.dart';
part 'dev.g.dart';
@JsonLiteral('dev.json', asConst: true)
Map<String, dynamic> get config => _$configJsonLiteral;

13
lib/env/dev.g.dart vendored Normal file
View file

@ -0,0 +1,13 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'dev.dart';
// **************************************************************************
// JsonLiteralGenerator
// **************************************************************************
const _$configJsonLiteral = {
'env': 'DEV',
'production': false,
'apiUrl': 'https://dev.peartrade.org/api'
};

5
lib/env/dev.json vendored Normal file
View file

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

6
lib/env/prod.dart vendored Normal file
View file

@ -0,0 +1,6 @@
import 'package:json_annotation/json_annotation.dart';
part 'prod.g.dart';
@JsonLiteral('prod.json', asConst: true)
Map<String, dynamic> get config => _$configJsonLiteral;

13
lib/env/prod.g.dart vendored Normal file
View file

@ -0,0 +1,13 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'prod.dart';
// **************************************************************************
// JsonLiteralGenerator
// **************************************************************************
const _$configJsonLiteral = {
'env': 'PROD',
'production': true,
'apiUrl': 'https://www.peartrade.org/api'
};

5
lib/env/prod.json vendored Normal file
View file

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

View file

@ -1,125 +0,0 @@
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
@override
Widget build(BuildContext context) {
final email = TextFormField(
keyboardType: TextInputType.emailAddress,
autofocus: true,
decoration: InputDecoration(
hintText: 'Email',
)
);
final password = TextFormField(
autofocus: false,
obscureText: true,
decoration: InputDecoration(
hintText: 'Password',
)
);
return Scaffold(
body: Center(
child: ListView(
children: <Widget>[
email,
password
]
)
)
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title + 'derpaderp'),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}

View file

@ -1,18 +1,29 @@
import 'package:flutter/material.dart';
import 'package:local_spend/login_page.dart';
import 'package:local_spend/pages/home_page.dart';
import 'package:local_spend/pages/login_page.dart';
import 'package:local_spend/pages/receipt_page.dart';
import 'package:local_spend/pages/spash_screen.dart';
import 'package:local_spend/config.dart';
void main() => runApp(MyApp());
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'LocalSpend Tracker',
theme: ThemeData(
primarySwatch: Colors.blueGrey,
),
home: LoginPage(),
//var config = ConfigWrapper.of(context);
return new MaterialApp(
title: "Splash and Token Authentication",
// theme: new ThemeData(
// primarySwatch: config.production ? Colors.green : Colors.yellow,
// ),
routes: <String, WidgetBuilder>{
"/HomePage": (BuildContext context) => HomePage(),
"/LoginPage": (BuildContext context) => LoginPage(),
"/ReceiptPage": (BuildContext context) => ReceiptPage(),
},
home: SplashScreen(),
);
}
}

7
lib/main_dev.dart Normal file
View file

@ -0,0 +1,7 @@
import 'package:flutter/material.dart';
import 'package:local_spend/config.dart';
import 'package:local_spend/env/dev.dart';
import 'package:local_spend/main.dart';
void main() => runApp(
new ConfigWrapper(config: Config.fromJson(config), child: new MyApp()));

View file

@ -0,0 +1,21 @@
class LoginModel {
final String userName;
final String token;
final String email;
final int userId;
LoginModel(this.userName, this.token, this.email, this.userId);
LoginModel.fromJson(Map<String, dynamic> json)
: userName = json['name'],
token = json['token'],
email = json['email'],
userId = json['pk'];
Map<String, dynamic> toJson() => {
'name': userName,
'token': token,
'email': email,
'pk': userId,
};
}

49
lib/pages/home_page.dart Normal file
View file

@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:local_spend/common/platform/platform_scaffold.dart';
import 'package:local_spend/common/widgets/basic_drawer.dart';
import 'package:shared_preferences/shared_preferences.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
void initState() {
super.initState();
_saveCurrentRoute("/HomePage");
}
_saveCurrentRoute(String lastRoute) async {
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString('LastScreenRoute', lastRoute);
}
@override
Widget build(BuildContext context) {
return PlatformScaffold(
appBar: AppBar(
title: Text(
"Home Page",
style: TextStyle(color: Colors.black),
),
backgroundColor: Colors.white,
iconTheme: IconThemeData(color: Colors.black),
elevation: Theme.of(context).platform == TargetPlatform.iOS ? 0.0 : 6.0,
),
drawer: BasicDrawer(),
backgroundColor: Colors.white,
body: Container(
padding: EdgeInsets.all(32.0),
child: Center(
child: Column(
children: <Widget>[
Text('This is the Home page'),
],
),
),
),
);
}
}

188
lib/pages/login_page.dart Normal file
View file

@ -0,0 +1,188 @@
import 'dart:async';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:local_spend/common/apifunctions/request_login_api.dart';
import 'package:local_spend/common/functions/show_dialog_single_button.dart';
import 'package:local_spend/common/platform/platform_scaffold.dart';
import 'package:local_spend/common/widgets/basic_drawer.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
const URL = "https://flutter.io/";
class LoginPage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new LoginPageState();
}
}
class LoginPageState extends State<LoginPage> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
String _welcomeString = "";
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() {
super.initState();
_saveCurrentRoute("/LoginPage");
}
_saveCurrentRoute(String lastRoute) async {
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString('LastPageRoute', lastRoute);
}
@override
Widget build(BuildContext context) {
var drawer = Drawer();
return WillPopScope(
onWillPop: () {
if (Navigator.canPop(context)) {
Navigator.of(context).pushNamedAndRemoveUntil(
'/HomePage', (Route<dynamic> route) => false);
} else {
Navigator.of(context).pushReplacementNamed('/HomePage');
}
},
child: PlatformScaffold(
drawer: BasicDrawer(),
appBar: AppBar(
title: Text(
"LOGIN",
style: TextStyle(
fontSize: 30.0,
color: Colors.black,
),
),
centerTitle: true,
backgroundColor: Colors.white,
iconTheme: IconThemeData(color: Colors.black),
),
backgroundColor: Colors.white,
body: Container(
child: Padding(
padding: EdgeInsets.fromLTRB(30.0, 0.0, 30.0, 0.0),
child: ListView(
children: <Widget>[
Container(
alignment: Alignment.topCenter,
child: Padding(
padding: EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 15.0),
child: Text(
"Local Loop",
style: TextStyle(fontSize: 40.0, color: Colors.black),
),
)),
Padding(
padding: EdgeInsets.fromLTRB(0.0, 5.0, 0.0, 78.0),
child: RichText(
text: TextSpan(
children: [
TextSpan(
text:
'This is the logon page.',
style: new TextStyle(
fontSize: 20.0,
color: Colors.black,
),
),
TextSpan(
text:
' It is currently in development.',
style: new TextStyle(
fontSize: 20.0,
color: Colors.black,
),
),
],
),
),
),
Text(
"Email",
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: _emailController,
decoration: InputDecoration(
hintText: "Use your login email",
),
style: TextStyle(
fontSize: 18.0,
color: Colors.grey,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: EdgeInsets.fromLTRB(0.0, 35.0, 0.0, 0.0),
child: Text(
"Password",
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: _passwordController,
decoration: InputDecoration(
hintText: 'Your password, keep it secret, keep it safe.',
),
obscureText: true,
style: TextStyle(
fontSize: 18.0,
color: Colors.grey,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: EdgeInsets.fromLTRB(0.0, 70.0, 0.0, 0.0),
child: Container(
height: 65.0,
child: RaisedButton(
onPressed: () {
SystemChannels.textInput.invokeMethod('TextInput.hide');
requestLoginAPI(context, _emailController.text,
_passwordController.text);
},
child: Text("LOGIN",
style:
TextStyle(color: Colors.white, fontSize: 22.0)),
color: Colors.blue,
),
),
),
],
),
),
),
),
);
}
}

187
lib/pages/receipt_page.dart Normal file
View file

@ -0,0 +1,187 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:local_spend/common/apifunctions/request_login_api.dart';
import 'package:local_spend/common/functions/show_dialog_single_button.dart';
import 'package:local_spend/common/platform/platform_scaffold.dart';
import 'package:local_spend/common/widgets/basic_drawer.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
const URL = "https://flutter.io/";
class ReceiptPage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new ReceiptPageState();
}
}
class ReceiptPageState extends State<ReceiptPage> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
String _welcomeString = "";
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() {
super.initState();
_saveCurrentRoute("/LoginPage");
}
_saveCurrentRoute(String lastRoute) async {
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString('LastPageRoute', lastRoute);
}
@override
Widget build(BuildContext context) {
var drawer = Drawer();
return WillPopScope(
onWillPop: () {
if (Navigator.canPop(context)) {
Navigator.of(context).pushNamedAndRemoveUntil(
'/HomePage', (Route<dynamic> route) => false);
} else {
Navigator.of(context).pushReplacementNamed('/HomePage');
}
},
child: PlatformScaffold(
drawer: BasicDrawer(),
appBar: AppBar(
title: Text(
"LOGIN",
style: TextStyle(
fontSize: 30.0,
color: Colors.black,
),
),
centerTitle: true,
backgroundColor: Colors.white,
iconTheme: IconThemeData(color: Colors.black),
),
backgroundColor: Colors.white,
body: Container(
child: Padding(
padding: EdgeInsets.fromLTRB(30.0, 0.0, 30.0, 0.0),
child: ListView(
children: <Widget>[
Container(
alignment: Alignment.topCenter,
child: Padding(
padding: EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 15.0),
child: Text(
"Local Loop",
style: TextStyle(fontSize: 40.0, color: Colors.black),
),
)),
Padding(
padding: EdgeInsets.fromLTRB(0.0, 5.0, 0.0, 78.0),
child: RichText(
text: TextSpan(
children: [
TextSpan(
text:
'This is the logon page.',
style: new TextStyle(
fontSize: 20.0,
color: Colors.black,
),
),
TextSpan(
text:
' It is currently in development.',
style: new TextStyle(
fontSize: 20.0,
color: Colors.black,
),
),
],
),
),
),
Text(
"Email",
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: _emailController,
decoration: InputDecoration(
hintText: "Use your login email",
),
style: TextStyle(
fontSize: 18.0,
color: Colors.grey,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: EdgeInsets.fromLTRB(0.0, 35.0, 0.0, 0.0),
child: Text(
"Password",
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: _passwordController,
decoration: InputDecoration(
hintText: 'Your password, keep it secret, keep it safe.',
),
obscureText: true,
style: TextStyle(
fontSize: 18.0,
color: Colors.grey,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: EdgeInsets.fromLTRB(0.0, 70.0, 0.0, 0.0),
child: Container(
height: 65.0,
child: RaisedButton(
onPressed: () {
SystemChannels.textInput.invokeMethod('TextInput.hide');
requestLoginAPI(context, _emailController.text,
_passwordController.text);
},
child: Text("LOGIN",
style:
TextStyle(color: Colors.white, fontSize: 22.0)),
color: Colors.blue,
),
),
),
],
),
),
),
),
);
}
}

View file

@ -0,0 +1,61 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:local_spend/common/platform/platform_scaffold.dart';
class SplashScreen extends StatefulWidget {
@override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
final int splashDuration = 3;
startTime() async {
return Timer(Duration(seconds: splashDuration), () {
SystemChannels.textInput.invokeMethod('TextInput.hide');
Navigator.of(context).pushReplacementNamed('/HomePage');
});
}
@override
void initState() {
super.initState();
startTime();
}
@override
Widget build(BuildContext context) {
var drawer = Drawer();
return PlatformScaffold(
drawer: drawer,
body: Container(
decoration: BoxDecoration(color: Colors.black),
child: Column(
children: <Widget>[
Expanded(
child: Container(
decoration: BoxDecoration(color: Colors.black),
alignment: FractionalOffset(0.5, 0.3),
child: Text(
"Local Loop",
style: TextStyle(fontSize: 40.0, color: Colors.white),
),
),
),
Container(
margin: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 30.0),
child: Text(
"© Copyright Statement 2018",
style: TextStyle(
fontSize: 16.0,
color: Colors.white,
),
),
),
],
)));
}
}

View file

@ -1,6 +1,20 @@
# Generated by pub
# See https://www.dartlang.org/tools/pub/glossary#lockfile
packages:
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "0.36.2"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.1"
async:
dependency: transitive
description:
@ -15,6 +29,62 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
build:
dependency: transitive
description:
name: build
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.4"
build_config:
dependency: transitive
description:
name: build_config
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.2"
build_daemon:
dependency: transitive
description:
name: build_daemon
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
build_runner:
dependency: "direct dev"
description:
name: build_runner
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.3"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.3"
built_collection:
dependency: transitive
description:
name: built_collection
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.0"
built_value:
dependency: transitive
description:
name: built_value
url: "https://pub.dartlang.org"
source: hosted
version: "6.4.0"
charcode:
dependency: transitive
description:
@ -22,6 +92,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
code_builder:
dependency: transitive
description:
name: code_builder
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.0"
collection:
dependency: transitive
description:
@ -29,6 +106,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.14.11"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.6"
csslib:
dependency: transitive
description:
name: csslib
url: "https://pub.dartlang.org"
source: hosted
version: "0.16.0"
cupertino_icons:
dependency: "direct main"
description:
@ -36,6 +134,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2"
dart_style:
dependency: transitive
description:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.7"
fixnum:
dependency: transitive
description:
name: fixnum
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.9"
flutter:
dependency: "direct main"
description: flutter
@ -46,6 +158,97 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
front_end:
dependency: transitive
description:
name: front_end
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.17"
glob:
dependency: transitive
description:
name: glob
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.7"
graphs:
dependency: transitive
description:
name: graphs
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
html:
dependency: transitive
description:
name: html
url: "https://pub.dartlang.org"
source: hosted
version: "0.14.0+2"
http:
dependency: "direct main"
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.0+2"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.6"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.3"
io:
dependency: transitive
description:
name: io
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.3"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.1+1"
json_annotation:
dependency: "direct main"
description:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
json_serializable:
dependency: "direct dev"
description:
name: json_serializable
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
kernel:
dependency: transitive
description:
name: kernel
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.17"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "0.11.3+2"
matcher:
dependency: transitive
description:
@ -60,6 +263,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.6"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.6+2"
package_config:
dependency: transitive
description:
name: package_config
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
package_resolver:
dependency: transitive
description:
name: package_resolver
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.10"
path:
dependency: transitive
description:
@ -67,6 +291,34 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.2"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
pool:
dependency: transitive
description:
name: pool
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
pub_semver:
dependency: transitive
description:
name: pub_semver
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.2"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
quiver:
dependency: transitive
description:
@ -74,18 +326,46 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.3"
shelf:
dependency: transitive
description:
name: shelf
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.5"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.3"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_gen:
dependency: transitive
description:
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.4+2"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.1"
version: "1.5.4"
stack_trace:
dependency: transitive
description:
@ -100,6 +380,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.8"
stream_transform:
dependency: transitive
description:
name: stream_transform
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.16+1"
string_scanner:
dependency: transitive
description:
@ -113,14 +400,21 @@ packages:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "1.1.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.1"
version: "0.2.2"
timing:
dependency: transitive
description:
name: timing
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.1+1"
typed_data:
dependency: transitive
description:
@ -128,6 +422,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.6"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.3"
vector_math:
dependency: transitive
description:
@ -135,5 +436,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
watcher:
dependency: transitive
description:
name: watcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.7+10"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.12"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.15"
sdks:
dart: ">=2.0.0 <3.0.0"
dart: ">=2.1.1 <3.0.0"
flutter: ">=0.1.4 <2.0.0"

View file

@ -15,6 +15,10 @@ environment:
dependencies:
flutter:
sdk: flutter
shared_preferences: ^0.4.2
url_launcher: ^3.0.3
json_annotation : ^2.2.0
http: ^0.12.0+2
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
@ -23,6 +27,8 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^1.1.3
json_serializable: ^2.1.2
# For information on the generic Dart part of this file, see the