diff options
author | Marvin Borner | 2018-07-21 17:59:36 +0200 |
---|---|---|
committer | Marvin Borner | 2018-07-21 17:59:36 +0200 |
commit | f03f214a47a78a52d73e28a76eec78d3f10d07e5 (patch) | |
tree | eaaf280fa5b89d32018236e06e1fc22220b8fa80 /lib | |
parent | acc5d5e9e960db9525a4368b393cb97fab8658e7 (diff) |
Rewritten login activity
Diffstat (limited to 'lib')
-rw-r--r-- | lib/auth.dart | 43 | ||||
-rw-r--r-- | lib/components.dart | 83 | ||||
-rw-r--r-- | lib/data/database_helper.dart | 54 | ||||
-rw-r--r-- | lib/data/rest_ds.dart | 22 | ||||
-rw-r--r-- | lib/globals.dart | 93 | ||||
-rw-r--r-- | lib/lockedscreen/home.dart | 49 | ||||
-rw-r--r-- | lib/main.dart | 603 | ||||
-rw-r--r-- | lib/models/user.dart | 21 | ||||
-rw-r--r-- | lib/pincode/pincode_create.dart | 239 | ||||
-rw-r--r-- | lib/pincode/pincode_verify.dart | 163 | ||||
-rw-r--r-- | lib/routes.dart | 9 | ||||
-rw-r--r-- | lib/screens/home/home_screen.dart | 14 | ||||
-rw-r--r-- | lib/screens/login/login_screen.dart | 141 | ||||
-rw-r--r-- | lib/screens/login/login_screen_presenter.dart | 19 | ||||
-rw-r--r-- | lib/utils/network_util.dart | 38 |
15 files changed, 1220 insertions, 371 deletions
diff --git a/lib/auth.dart b/lib/auth.dart deleted file mode 100644 index 6ee8a0b..0000000 --- a/lib/auth.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:beam_messenger/data/database_helper.dart'; - -enum AuthState { LOGGED_IN, LOGGED_OUT } - -abstract class AuthStateListener { - void onAuthStateChanged(AuthState state); -} - -// A naive implementation of Observer/Subscriber Pattern. Will do for now. -class AuthStateProvider { - static final AuthStateProvider _instance = new AuthStateProvider.internal(); - - List<AuthStateListener> _subscribers; - - factory AuthStateProvider() => _instance; - AuthStateProvider.internal() { - _subscribers = new List<AuthStateListener>(); - initState(); - } - - void initState() async { - var db = new DatabaseHelper(); - var isLoggedIn = await db.isLoggedIn(); - if (isLoggedIn) - notify(AuthState.LOGGED_IN); - else - notify(AuthState.LOGGED_OUT); - } - - void subscribe(AuthStateListener listener) { - _subscribers.add(listener); - } - - void dispose(AuthStateListener listener) { - for (var l in _subscribers) { - if (l == listener) _subscribers.remove(l); - } - } - - void notify(AuthState state) { - _subscribers.forEach((AuthStateListener s) => s.onAuthStateChanged(state)); - } -} diff --git a/lib/components.dart b/lib/components.dart new file mode 100644 index 0000000..17e6767 --- /dev/null +++ b/lib/components.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; + +class InputField extends StatelessWidget { + IconData icon; + String hintText; + TextInputType textInputType; + Color textFieldColor, iconColor; + bool obscureText; + var validateFunction; + var onSaved; + + String onEmpty; + String name; + + //passing props in the Constructor. + //Java like style + InputField({ + this.name, + this.hintText, + this.onEmpty, + this.obscureText, + this.textInputType, + this.icon, + this.validateFunction, + this.onSaved, + }); + + @override + Widget build(BuildContext context) { + // TODO: implement build + return (new Container( + margin: new EdgeInsets.only(bottom: 10.0), + child: new DecoratedBox( + decoration: new BoxDecoration( + borderRadius: new BorderRadius.all(new Radius.circular(30.0)), + color: Colors.grey[50]), + child: new Padding( + padding: EdgeInsets.all(5.0), + child: new TextFormField( + decoration: new InputDecoration( + icon: new Icon(icon), + labelText: name, + border: InputBorder.none, + hintText: hintText, + hintStyle: const TextStyle(color: Colors.grey, fontSize: 15.0), + ), + validator: (val) => val.isEmpty ? onEmpty : null, + onSaved: (val) => onSaved = val, + obscureText: obscureText, + keyboardType: textInputType, + ), + ), + ), + )); + } +} + +class TextButton extends StatelessWidget { + VoidCallback onPressed; + String name; + + //passing props in the Constructor. + //Java like style + TextButton({ + this.name, + this.onPressed, + }); + + @override + Widget build(BuildContext context) { + // TODO: implement build + return (new FlatButton( + child: new Text(name, + textAlign: TextAlign.center, + style: const TextStyle( + color: Colors.black, + fontSize: 14.0, + fontFamily: "Roboto", + fontWeight: FontWeight.bold)), + onPressed: onPressed, + )); + } +} diff --git a/lib/data/database_helper.dart b/lib/data/database_helper.dart deleted file mode 100644 index da65dcc..0000000 --- a/lib/data/database_helper.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'dart:async'; -import 'dart:io' as io; - -import 'package:path/path.dart'; -import 'package:beam_messenger/models/user.dart'; -import 'package:sqflite/sqflite.dart'; -import 'package:path_provider/path_provider.dart'; - -class DatabaseHelper { - static final DatabaseHelper _instance = new DatabaseHelper.internal(); - factory DatabaseHelper() => _instance; - - static Database _db; - - Future<Database> get db async { - if (_db != null) return _db; - _db = await initDb(); - return _db; - } - - DatabaseHelper.internal(); - - initDb() async { - io.Directory documentsDirectory = await getApplicationDocumentsDirectory(); - String path = join(documentsDirectory.path, "main.db"); - var theDb = await openDatabase(path, version: 1, onCreate: _onCreate); - return theDb; - } - - void _onCreate(Database db, int version) async { - // When creating the db, create the table - await db.execute( - "CREATE TABLE User(id INTEGER PRIMARY KEY, email TEXT, password TEXT)"); - print("Created tables"); - } - - Future<int> saveUser(User user) async { - var dbClient = await db; - int res = await dbClient.insert("User", user.toMap()); - return res; - } - - Future<int> deleteUsers() async { - var dbClient = await db; - int res = await dbClient.delete("User"); - return res; - } - - Future<bool> isLoggedIn() async { - var dbClient = await db; - var res = await dbClient.query("User"); - return res.length > 0 ? true : false; - } -} diff --git a/lib/data/rest_ds.dart b/lib/data/rest_ds.dart deleted file mode 100644 index 36a82c7..0000000 --- a/lib/data/rest_ds.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; // not needed for later use - -import 'package:beam_messenger/utils/network_util.dart'; -import 'package:beam_messenger/models/user.dart'; - -class RestDatasource { - NetworkUtil _netUtil = new NetworkUtil(); - static final baseUrl = "http://192.168.0.74:8000"; - static final loginUrl = baseUrl + "/login"; - - Future<User> login(String email, String password) { - return _netUtil.post(loginUrl, - body: {"email": email, "password": password}).then((dynamic res) { - print(res.toString()); - if (res["status"]) throw new Exception(res["message"]); - return jsonDecode( - "{ error: false, user: { email: “marvin@borners.de”, password: “password” } }"); // later: access token - // return new User.map(res["user"]); - }); - } -} diff --git a/lib/globals.dart b/lib/globals.dart new file mode 100644 index 0000000..7195798 --- /dev/null +++ b/lib/globals.dart @@ -0,0 +1,93 @@ +library globals; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'package:flutter/material.dart'; + +//Variables +bool isLoggedIn = false; +String token = ""; +String domain = ""; +String apiURL = "http://192.168.0.74:8000/login"; +String error = ""; + +String id = "0"; +String firstname = ""; +String email = ""; +String avatar ="https://api.adorable.io/avatars/128/BEAM-Messenger.png"; + +class Utility { + static Future<Null> showAlertPopup( + BuildContext context, String title, String detail) async { + return showDialog<Null>( + context: context, + barrierDismissible: false, // user must tap button! + child: new AlertDialog( + title: new Text(title), + content: new SingleChildScrollView( + child: new ListBody( + children: <Widget>[ + new Text(detail), + ], + ), + ), + actions: <Widget>[ + new FlatButton( + child: new Text('Done'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ), + ); + } + + static Future<String> getData(String params) async { + var requestURL = apiURL; + requestURL = requestURL + params; +// requestURL = requestURL + "calltype=" + calltypeParm; +// requestURL = requestURL + "&mod=" + modParm; +// requestURL = requestURL + "&?action=" + actionParm; +// requestURL = requestURL + "&?param=" + paramsParm; +// requestURL = requestURL + "&?foo=" + fooParm; + print("Request URL: " + requestURL); + + var url = requestURL; + var httpClient = new HttpClient(); + String result; + try { + var request = await httpClient.getUrl(Uri.parse(url)); + var response = await request.close(); + if (response.statusCode == HttpStatus.OK) { + try { + var json = await response.transform(UTF8.decoder).join(); + result = json; + } catch (exception) { + result = 'Error Getting Data'; + } + } else { + result = + 'Error getting IP address:\nHttp status ${response.statusCode}'; + } + } catch (exception) { + result = 'Failed getting IP address'; + } + print("Result: " + result); + return result; + } + + static Widget newTextButton(String title, VoidCallback onPressed) { + return new FlatButton( + child: new Text(title, + textAlign: TextAlign.center, + style: const TextStyle( + color: Colors.black, + fontSize: 14.0, + fontFamily: "Roboto", + fontWeight: FontWeight.bold)), + onPressed: onPressed, + ); + } +} diff --git a/lib/lockedscreen/home.dart b/lib/lockedscreen/home.dart new file mode 100644 index 0000000..2336bb6 --- /dev/null +++ b/lib/lockedscreen/home.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:beam_messenger/globals.dart' as globals; + +class Home extends StatelessWidget { + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text("Home"), + ), + body: new Center( + child: new Column( + children: <Widget>[ + new Container(height: 20.0), + new CircleAvatar( + radius: 60.0, + backgroundImage: new NetworkImage(globals.avatar), + ), + new Container(height: 10.0), + new Text( + 'ID: ' + globals.id, + style: new TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 20.0, + ), + ), + new Container(height: 10.0), + new Text( + 'First Name: ' + globals.firstname, + style: new TextStyle( + color: Colors.black, + fontSize: 20.0, + ), + ), + new Container(height: 10.0), + new Text( + 'Email: ' + globals.email, + style: new TextStyle( + color: Colors.black, + fontSize: 20.0, + ), + ), + ], + )), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index a7df267..e547f62 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,19 +1,602 @@ +import 'package:meta/meta.dart'; +import 'dart:async'; +import 'dart:convert'; import 'package:flutter/material.dart'; -import 'package:beam_messenger/auth.dart'; -import 'package:beam_messenger/routes.dart'; +import 'package:flutter/rendering.dart'; +import 'package:beam_messenger/globals.dart' as globals; +import 'lockedscreen/home.dart'; +import 'pincode/pincode_verify.dart'; +import 'pincode/pincode_create.dart'; +import 'package:local_auth/local_auth.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'components.dart'; -void main() => runApp(new LoginApp()); +void main() { + runApp(new MaterialApp(home: new LoginPage())); +} + +class LoginPage extends StatefulWidget { + LoginPageState createState() => new LoginPageState(); +} + +class LoginPageState extends State<LoginPage> { + final formKey = new GlobalKey<FormState>(); + final _scaffoldKey = new GlobalKey<ScaffoldState>(); + + String _email; + String _password; + bool _usePinCode = false; + + bool autovalidate = false; + void _handleSubmitted() { + final FormState form = formKey.currentState; + if (!form.validate()) { + autovalidate = true; // Start validating on every change. + showInSnackBar('Please fix the errors in red before submitting.'); + setState(() { + globals.isLoggedIn = false; + }); + } else { + form.save(); + _performLogin(); + } + } + + void showInSnackBar(String value) { + _scaffoldKey.currentState + .showSnackBar(new SnackBar(content: new Text(value))); + } + + void _performLogin() async { + // This is just a demo, so no actual login here. + final snackbar = new SnackBar( + duration: new Duration(seconds: 10), + content: new Row( + children: <Widget>[ + new CircularProgressIndicator(), + new Text(" Signing-In...") + ], + ), + ); + _scaffoldKey.currentState.showSnackBar(snackbar); + await tryLogin(_email, _password); + if (globals.isLoggedIn) { + // await showAlertPopup(); + // await saveData(_usePinCode); + if (_usePinCode) { + navigateToScreen('Create Pin'); + } else { + navigateToScreen('Home'); + } + } + _scaffoldKey.currentState.hideCurrentSnackBar(); + } + + Future<bool> _loginRequest(String email, String password) async { + String result = ""; + result = await globals.Utility.getData(""); + + //Decode Data + try { + Map decoded = JSON.decode(result); + globals.id = decoded["user_data"]['id'].toString(); + globals.firstname = decoded["user_data"]['name'].toString(); + globals.email = decoded["user_data"]['email'].toString(); + globals.avatar = decoded["user_data"]['avatar'].toString(); + + print(globals.id); + print(globals.firstname); + print(globals.email); + print(globals.avatar); + + globals.token = globals.id; + } catch (exception) { + print("Error Decoding Data"); + return false; + } + return true; + } + + tryLogin(String email, String password) async { + await _loginRequest(email, password); + print("Token: " + globals.token + " | Error: " + globals.error); + + if (globals.token != 'null') { + print("Valid Token!"); + setState(() { + globals.isLoggedIn = true; + }); + + //Save Email and Password to Shared Preferences + SharedPreferences prefs = await SharedPreferences.getInstance(); + print('Email: $email Password $password.'); + prefs.setString('userEmail', email); + prefs.setString('userPassword', password); + prefs.setString('userToken', globals.token); + } else { + print("Invalid Token!"); + setState(() { + globals.isLoggedIn = false; + }); + globals.error = "Check Email and Password!"; + globals.Utility.showAlertPopup( + context, "Info", "Please Try Logging In Again! \n" + globals.error); + } + } + + saveData(bool usePin) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + if (usePin) { + prefs.setBool('usePinCode', true); + } else { + prefs.setBool('usePinCode', false); + } + } + + Future<Null> navigateToScreen(String name) async { + if (name.contains('Home')) { + Navigator.push( + context, + new MaterialPageRoute( + builder: (context) => + new Home()), //When Authorized Navigate to the next screen + ); + } else if (name.contains('Create Pin')) { + Navigator.push( + context, + new MaterialPageRoute( + builder: (context) => + new PinCodeCreate()), //When Authorized Navigate to the next screen + ); + } else if (name.contains('Verify Pin')) { + Navigator.push( + context, + new MaterialPageRoute( + builder: (context) => + new PinCodeVerify()), //When Authorized Navigate to the next screen + ); + } else { + print('Error: $name'); + } + } + + Future<Null> showAlertPopup() async { + return showDialog<Null>( + context: context, + barrierDismissible: false, // user must tap button! + child: new AlertDialog( + title: new Text('Info'), + content: new SingleChildScrollView( + child: new ListBody( + children: <Widget>[ + new Text('Would you like to set a Pin Code for a faster log in?'), + new Text('Once a Pin is set you can unlock with biometrics'), + ], + ), + ), + actions: <Widget>[ + new FlatButton( + child: new Text('Yes'), + onPressed: () { + _usePinCode = true; + Navigator.of(context).pop(); + }, + ), + new FlatButton( + child: new Text('No'), + onPressed: () { + _usePinCode = false; + Navigator.of(context).pop(); + }, + ), + ], + ), + ); + } + + String _authorized = 'Not Authorized'; + Future<Null> _goToBiometrics() async { + String email; + String password; + final LocalAuthentication auth = new LocalAuthentication(); + bool authenticated = false; + try { + authenticated = await auth.authenticateWithBiometrics( + localizedReason: 'Scan your fingerprint to authenticate', + useErrorDialogs: true, + stickyAuth: false); + } catch (e) { + print(e); + } + if (!mounted) return; + + setState(() { + _authorized = authenticated ? 'Authorized' : 'Not Authorized'; + print(_authorized); + + if (authenticated) { + //Todo: Get Saved Email and Password from Shared Preferences + //https://github.com/flutter/plugins/tree/master/packages/shared_preferences + String savedEmail = "Test"; + String savedPassword = "Test"; + //Todo: Get Email and Password from Shared Preferences + email = savedEmail; + password = savedPassword; + } + }); + if (authenticated) { + await tryLogin(email, password); + if (globals.isLoggedIn) { + navigateToScreen('Home'); + } else { + globals.Utility.showAlertPopup( + context, "Info", "Login Failed\nPlease Try Logging In Again"); + } + } else { + globals.Utility.showAlertPopup( + context, "Info", "Login Failed\nPlease Try Biometrics Again"); + } + } + + void newAccount() { + Navigator.of(context).push(new MaterialPageRoute<Null>( + builder: (BuildContext context) { + return new NewAccountPage(); + }, + fullscreenDialog: true)); + } + + void needHelp() { + Navigator.of(context).push(new MaterialPageRoute<Null>( + builder: (BuildContext context) { + return new HelpPage(); + }, + fullscreenDialog: true)); + } + + @override + Widget build(BuildContext context) { + return new Scaffold( + key: _scaffoldKey, + appBar: new AppBar( + title: new Text('Login'), + ), + body: new Container( + color: Colors.grey[300], + child: new ListView( + physics: new AlwaysScrollableScrollPhysics(), + key: new PageStorageKey("Divider 1"), + children: <Widget>[ + new Container( + height: 20.0, + ), + new Padding( + padding: EdgeInsets.all(20.0), + child: new Card( + child: new Column( + children: <Widget>[ + new Container(height: 30.0), + new Icon( + globals.isLoggedIn ? Icons.lock_open : Icons.lock_outline, + size: 120.0, + ), + new Padding( + padding: const EdgeInsets.all(16.0), + child: new Form( + key: formKey, + child: new Column( + children: [ + new TextFormField( + decoration: + new InputDecoration(labelText: 'Email'), + validator: (val) => + val.length < 1 ? 'Email Required' : null, + onSaved: (val) => _email = val, + obscureText: false, + keyboardType: TextInputType.text, + autocorrect: false, + ), + new Container(height: 10.0), + new TextFormField( + decoration: + new InputDecoration(labelText: 'Password'), + validator: (val) => + val.length < 1 ? 'Password Required' : null, + onSaved: (val) => _password = val, + obscureText: true, + keyboardType: TextInputType.text, + autocorrect: false, + ), + new Container(height: 5.0), + ], + ), + ), + ), + ], + ), + ), + ), + new Padding( + padding: EdgeInsets.all(20.0), + child: new Row( + children: <Widget>[ + new Expanded( + child: new Padding( + padding: const EdgeInsets.all(5.0), + child: new RaisedButton( + color: Colors.blue, + onPressed: _handleSubmitted, + child: new Text( + 'Login', + style: new TextStyle(color: Colors.white), + ), + ), + ), + ), + new Padding( + padding: const EdgeInsets.all(5.0), + child: new RaisedButton( + color: Colors.redAccent[400], + onPressed: _goToBiometrics, + child: new Icon( + Icons.fingerprint, + color: Colors.white, + ), + ), + ), + ], + ), + ), + new Padding( + padding: EdgeInsets.all(20.0), + child: new Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: <Widget>[ + new TextButton(name: "Create Account", onPressed: newAccount), + new TextButton(name: "Need Help?", onPressed: needHelp), + ], + ), + ) + ], + ), + ), + ); + } +} + +class NewAccountPage extends StatefulWidget { + @override + NewAccountPageState createState() => new NewAccountPageState(); +} + +class NewAccountPageState extends State<NewAccountPage> { + final formKey = new GlobalKey<FormState>(); + final _scaffoldKey = new GlobalKey<ScaffoldState>(); + + String _email; + String _password; + + void openTermsAndConditions() { + Navigator.of(context).push(new MaterialPageRoute<Null>( + builder: (BuildContext context) { + return new TermsConditionsPage(); + }, + fullscreenDialog: true)); + } + + bool isEmail(String email) { + String regex = + r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'; + RegExp regExp = new RegExp(regex); + return regExp.hasMatch(email); + } + + bool autovalidate = false; + void _handleSubmitted() { + final FormState form = formKey.currentState; + if (!form.validate()) { + autovalidate = true; // Start validating on every change. + showInSnackBar('Please fix the errors before submitting again.'); + } else { + form.save(); + Navigator.pop(context); + } + } + + void showInSnackBar(String value) { + _scaffoldKey.currentState + .showSnackBar(new SnackBar(content: new Text(value))); + } -class LoginApp extends StatelessWidget { - // This widget is the root of your application. @override Widget build(BuildContext context) { - return new MaterialApp( - title: 'Login', - theme: new ThemeData( - primarySwatch: Colors.red, + return new Scaffold( + key: _scaffoldKey, + appBar: new AppBar( + title: const Text('Create New Account'), + ), + body: new Container( + color: Colors.grey[300], + child: new ListView( + physics: new AlwaysScrollableScrollPhysics(), + key: new PageStorageKey("Divider 1"), + children: <Widget>[ + new Container( + height: 20.0, + ), + new Padding( + padding: EdgeInsets.all(20.0), + child: new Card( + child: new Column( + children: <Widget>[ + new Padding( + padding: const EdgeInsets.all(16.0), + child: new Form( + key: formKey, + child: new Column( + children: [ + new TextFormField( + decoration: + new InputDecoration(labelText: 'Email'), + validator: (val) => isEmail(val) && val.length < 1 + ? 'Email Required' + : null, + onSaved: (val) => _email = val, + obscureText: false, + keyboardType: TextInputType.emailAddress, + autocorrect: false, + ), + new Container( + height: 10.0, + ), + new TextFormField( + decoration: + new InputDecoration(labelText: 'Email'), + validator: (val) => + isEmail(val) && val.length < 1 ? 'Email Required' : null, + onSaved: (val) => _email = val, + obscureText: true, + keyboardType: TextInputType.emailAddress, + autocorrect: false, + ), + new Container(height: 10.0), + new TextFormField( + decoration: + new InputDecoration(labelText: 'Password'), + validator: (val) => + val.length >= 8 && val.length >= 32 ? 'Password length must be between 8 and 32 characters.' : null, + onSaved: (val) => _password = val, + obscureText: true, + keyboardType: TextInputType.text, + autocorrect: false, + ), + new Container( + height: 10.0, + ), + new TextFormField( + decoration: + new InputDecoration(labelText: 'Password'), + validator: (val) => + val.length >= 8 && val.length >= 32 ? 'Password length must be between 8 and 32 characters.' : null, + onSaved: (val) => _password = val, + obscureText: true, + keyboardType: TextInputType.text, + autocorrect: false, + ), + new Container(height: 5.0), + ], + ), + ), + ), + new TextButton( + name: "Terms and Conditions", + onPressed: openTermsAndConditions), + ], + ), + ), + ), + new Padding( + padding: EdgeInsets.all(20.0), + child: new Row( + children: <Widget>[ + new Expanded( + child: new Padding( + padding: const EdgeInsets.all(5.0), + child: new RaisedButton( + color: Colors.blue, + onPressed: _handleSubmitted, + child: new Text( + 'Save', + style: new TextStyle(color: Colors.white), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} + +class HelpPage extends StatefulWidget { + @override + HelpPageState createState() => new HelpPageState(); +} + +class HelpPageState extends State<HelpPage> { + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: const Text('Help'), + ), + body: new Center( + child: new Column( + children: <Widget>[ + new Padding( + padding: new EdgeInsets.all(10.0), + child: new Text( + '24/7 Customer Support', + textAlign: TextAlign.center, + style: new TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 20.0, + ), + ), + ), + new Padding( + padding: new EdgeInsets.all(10.0), + child: new Text( + 'Email: contact@beam-messenger.com', + textAlign: TextAlign.center, + style: new TextStyle( + color: Colors.black, + fontSize: 15.0, + ), + ), + ), + ], + ), + ), + ); + } +} + +class TermsConditionsPage extends StatefulWidget { + @override + TermsConditionsPageState createState() => new TermsConditionsPageState(); +} + +class TermsConditionsPageState extends State<TermsConditionsPage> { + String termsOfUse = "Your data is secure - don't worry :) (TODO: Write TOU)"; + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: const Text('Terms and Conditions'), + ), + body: new Center( + child: new Column( + children: <Widget>[ + new Padding( + padding: new EdgeInsets.all(10.0), + child: new Text( + termsOfUse, + textAlign: TextAlign.center, + style: new TextStyle( + color: Colors.black, + fontSize: 15.0, + ), + ), + ), + ], + ), ), - routes: routes, ); } } diff --git a/lib/models/user.dart b/lib/models/user.dart deleted file mode 100644 index 89e3ace..0000000 --- a/lib/models/user.dart +++ /dev/null @@ -1,21 +0,0 @@ -class User { - String _email; - String _password; - User(this._email, this._password); - - User.map(dynamic obj) { - this._email = obj["email"]; - this._password = obj["password"]; - } - - String get email => _email; - String get password => _password; - - Map<String, dynamic> toMap() { - var map = new Map<String, dynamic>(); - map["email"] = _email; - map["password"] = _password; - - return map; - } -} diff --git a/lib/pincode/pincode_create.dart b/lib/pincode/pincode_create.dart new file mode 100644 index 0000000..0798ff4 --- /dev/null +++ b/lib/pincode/pincode_create.dart @@ -0,0 +1,239 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +class PinCodeCreate extends StatelessWidget { + var pinCode = ""; + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text("Create Pin"), + ), + body: new ListView( + children: <Widget>[ + new Padding( + padding: new EdgeInsets.all(20.0), + child: new Text("Enter a New Pin", + textAlign: TextAlign.center, + style: new TextStyle( + color: Colors.black.withOpacity(1.0), + fontWeight: FontWeight.bold, + fontSize: 20.0, + )), + ), + new Row( + children: <Widget>[ + new Expanded( + child: new Text(''), + ), + new Expanded( + child: new Text(''), + ), + new Expanded( + child: new Icon(Icons.check_circle_outline), + ), + new Expanded( + child: new Icon(Icons.radio_button_unchecked), + ), + new Expanded( + child: new Icon(Icons.radio_button_unchecked), + ), + new Expanded( + child: new Icon(Icons.radio_button_unchecked), + ), + new Expanded( + child: new Text(''), + ), + new Expanded( + child: new Text(''), + ), + ], + ), + new Row( + children: <Widget>[ + new Expanded( + child: new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "7", + child: new Text('7', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + ), + new Expanded( + child: new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "8", + child: new Text('8', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + ), + new Expanded( + child: new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "9", + child: new Text('9', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + ), + ], + ), + new Row( + children: <Widget>[ + new Expanded( + child: new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "4", + child: new Text('4', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + ), + new Expanded( + child: new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "5", + child: new Text('5', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + ), + new Expanded( + child: new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "6", + child: new Text('6', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + ), + ], + ), + new Row( + children: <Widget>[ + new Expanded( + child: new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "1", + child: new Text('1', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + ), + new Expanded( + child: new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "2", + child: new Text('2', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + ), + new Expanded( + child: new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "3", + child: new Text('3', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + ), + ], + ), + new Row( + children: <Widget>[ + new Expanded( + child: new Text(''), + ), + new Expanded( + child: new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "0", + child: new Text('0', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + ), + new Expanded( + child: new Text(''), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/pincode/pincode_verify.dart b/lib/pincode/pincode_verify.dart new file mode 100644 index 0000000..391e7d2 --- /dev/null +++ b/lib/pincode/pincode_verify.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +class PinCodeVerify extends StatelessWidget { + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text("Verify Pin"), + ), + body: new GridView.count( + primary: false, + padding: const EdgeInsets.all(20.0), + crossAxisSpacing: 10.0, + crossAxisCount: 3, + children: <Widget>[ + new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "1", + child: new Text('1', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "2", + child: new Text('2', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "3", + child: new Text('3', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "4", + child: new Text('4', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "5", + child: new Text('5', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "6", + child: new Text('6', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "7", + child: new Text('7', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "8", + child: new Text('8', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "9", + child: new Text('9', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + new Text(''), + new Padding( + padding: new EdgeInsets.all(20.0), + child: new FloatingActionButton( + elevation: 0.0, + heroTag: "0", + child: new Text('0', + style: new TextStyle( + fontSize: 40.0, + fontFamily: 'Roboto', + color: Colors.white, + )), + backgroundColor: new Color(0xFFE57373), + onPressed: () {}), + ), + new Text(''), + ], + ), + ); + } +} diff --git a/lib/routes.dart b/lib/routes.dart deleted file mode 100644 index 4a9ae07..0000000 --- a/lib/routes.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:beam_messenger/screens/home/home_screen.dart'; -import 'package:beam_messenger/screens/login/login_screen.dart'; - -final routes = { - '/login': (BuildContext context) => new LoginScreen(), - '/home': (BuildContext context) => new HomeScreen(), - '/': (BuildContext context) => new LoginScreen(), -}; diff --git a/lib/screens/home/home_screen.dart b/lib/screens/home/home_screen.dart deleted file mode 100644 index 46acd22..0000000 --- a/lib/screens/home/home_screen.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter/material.dart'; - -class HomeScreen extends StatelessWidget { - @override - Widget build(BuildContext context) { - return new Scaffold( - appBar: new AppBar(title: new Text("Home"),), - body: new Center( - child: new Text("Welcome home!"), - ), - ); - } - -}
\ No newline at end of file diff --git a/lib/screens/login/login_screen.dart b/lib/screens/login/login_screen.dart deleted file mode 100644 index 7c8845e..0000000 --- a/lib/screens/login/login_screen.dart +++ /dev/null @@ -1,141 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; -import 'package:beam_messenger/auth.dart'; -import 'package:beam_messenger/data/database_helper.dart'; -import 'package:beam_messenger/models/user.dart'; -import 'package:beam_messenger/screens/login/login_screen_presenter.dart'; - -class LoginScreen extends StatefulWidget { - @override - State<StatefulWidget> createState() { - return new LoginScreenState(); - } -} - -class LoginScreenState extends State<LoginScreen> - implements LoginScreenContract, AuthStateListener { - BuildContext _ctx; - - bool _isLoading = false; - final formKey = new GlobalKey<FormState>(); - final scaffoldKey = new GlobalKey<ScaffoldState>(); - String _password, _email; - - LoginScreenPresenter _presenter; - - LoginScreenState() { - _presenter = new LoginScreenPresenter(this); - var authStateProvider = new AuthStateProvider(); - authStateProvider.subscribe(this); - } - - void _submit() { - final form = formKey.currentState; - - if (form.validate()) { - setState(() => _isLoading = true); - form.save(); - _presenter.doLogin(_email, _password); - } - } - - void _showSnackBar(String text) { - scaffoldKey.currentState - .showSnackBar(new SnackBar(content: new Text(text))); - } - - @override - onAuthStateChanged(AuthState state) { - if (state == AuthState.LOGGED_IN) - Navigator.of(_ctx).pushReplacementNamed("/home"); - } - - @override - Widget build(BuildContext context) { - _ctx = context; - var loginBtn = new RaisedButton( - onPressed: _submit, - child: new Text("LOGIN"), - color: Colors.primaries[0], - ); - var loginForm = new Column( - children: <Widget>[ - new Text( - "Login", - textScaleFactor: 2.0, - ), - new Form( - key: formKey, - child: new Column( - children: <Widget>[ - new Padding( - padding: const EdgeInsets.all(8.0), - child: new TextFormField( - onSaved: (val) => _email = val, - validator: (val) { - return val.length < 10 - ? "email must have atleast 10 chars" - : null; - }, - decoration: new InputDecoration(labelText: "email"), - ), - ), - new Padding( - padding: const EdgeInsets.all(8.0), - child: new TextFormField( - onSaved: (val) => _password = val, - decoration: new InputDecoration(labelText: "Password"), - ), - ), - ], - ), - ), - _isLoading ? new CircularProgressIndicator() : loginBtn - ], - crossAxisAlignment: CrossAxisAlignment.center, - ); - - return new Scaffold( - appBar: null, - key: scaffoldKey, - body: new Container( - decoration: new BoxDecoration( - image: new DecorationImage( - image: new AssetImage("assets/login_background.jpg"), - fit: BoxFit.cover), - ), - child: new Center( - child: new ClipRect( - child: new BackdropFilter( - filter: new ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), - child: new Container( - child: loginForm, - height: 300.0, - width: 300.0, - decoration: new BoxDecoration( - color: Colors.grey.shade200.withOpacity(0.5)), - ), - ), - ), - ), - ), - ); - } - - @override - void onLoginError(String errorTxt) { - _showSnackBar(errorTxt); - setState(() => _isLoading = false); - } - - @override - void onLoginSuccess(User user) async { - _showSnackBar(user.toString()); - setState(() => _isLoading = false); - var db = new DatabaseHelper(); - await db.saveUser(user); - var authStateProvider = new AuthStateProvider(); - authStateProvider.notify(AuthState.LOGGED_IN); - } -} diff --git a/lib/screens/login/login_screen_presenter.dart b/lib/screens/login/login_screen_presenter.dart deleted file mode 100644 index fc5da4f..0000000 --- a/lib/screens/login/login_screen_presenter.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:beam_messenger/data/rest_ds.dart'; -import 'package:beam_messenger/models/user.dart'; - -abstract class LoginScreenContract { - void onLoginSuccess(User user); - void onLoginError(String errorTxt); -} - -class LoginScreenPresenter { - LoginScreenContract _view; - RestDatasource api = new RestDatasource(); - LoginScreenPresenter(this._view); - - doLogin(String email, String password) { - api.login(email, password).then((User user) { - _view.onLoginSuccess(user); - }).catchError((Exception error) => _view.onLoginError(error.toString())); - } -} diff --git a/lib/utils/network_util.dart b/lib/utils/network_util.dart deleted file mode 100644 index 463e98f..0000000 --- a/lib/utils/network_util.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'package:http/http.dart' as http; - -class NetworkUtil { - // next three lines makes this class a Singleton - static NetworkUtil _instance = new NetworkUtil.internal(); - NetworkUtil.internal(); - factory NetworkUtil() => _instance; - - final JsonDecoder _decoder = new JsonDecoder(); - - Future<dynamic> get(String url) { - return http.get(url).then((http.Response response) { - final String res = response.body; - final int statusCode = response.statusCode; - - if (statusCode < 200 || statusCode > 400 || json == null) { - throw new Exception("Error while fetching data"); - } - return _decoder.convert(res); - }); - } - - Future<dynamic> post(String url, {Map headers, body, encoding}) { - return http - .post(url, body: body, headers: headers, encoding: encoding) - .then((http.Response response) { - final String res = response.body; - final int statusCode = response.statusCode; - - if (statusCode < 200 || statusCode > 400 || json == null) { - throw new Exception("Error while fetching data"); - } - return _decoder.convert(res); - }); - } -}
\ No newline at end of file |