Working on login screen
This commit is contained in:
parent
1811c15b82
commit
cb3f316c4d
|
|
@ -1 +1,23 @@
|
|||
export '../../data/models/entities.dart';
|
||||
export 'package:invoiceninja/data/models/entities.dart';
|
||||
|
||||
class User {
|
||||
final String token;
|
||||
final String id;
|
||||
|
||||
User(this.token, this.id);
|
||||
|
||||
Map<String, dynamic> toJSON() => <String, dynamic>{
|
||||
'token': this.token,
|
||||
'id': this.id
|
||||
};
|
||||
|
||||
factory User.fromJSON(Map<String, dynamic> json) => new User(
|
||||
json['token'],
|
||||
json['id'],
|
||||
);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '{token: $token, id: $id}';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'redux/app/app_state.dart';
|
||||
import 'package:invoiceninja/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja/ui/dashboard_screen.dart';
|
||||
import 'package:invoiceninja/ui/client/client_list.dart';
|
||||
import 'package:invoiceninja/ui/product/product_screen.dart';
|
||||
import 'package:invoiceninja/ui/app/login_screen.dart';
|
||||
import 'routes.dart';
|
||||
import 'redux/product/product_actions.dart';
|
||||
import 'redux/product/product_middleware.dart';
|
||||
import 'redux/app/app_reducer.dart';
|
||||
import 'package:invoiceninja/ui/auth/login_screen.dart';
|
||||
import 'package:invoiceninja/routes.dart';
|
||||
import 'package:invoiceninja/redux/product/product_actions.dart';
|
||||
import 'package:invoiceninja/redux/product/product_middleware.dart';
|
||||
import 'package:invoiceninja/redux/app/app_reducer.dart';
|
||||
|
||||
void main() {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,24 @@
|
|||
import 'package:invoiceninja/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja/redux/app/loading_reducer.dart';
|
||||
import 'package:invoiceninja/redux/product/product_reducer.dart';
|
||||
import 'package:invoiceninja/redux/auth/auth_reducer.dart';
|
||||
|
||||
// We create the State reducer by combining many smaller reducers into one!
|
||||
AppState appReducer(AppState state, action) {
|
||||
|
||||
/*
|
||||
if (action is PersistLoadedAction<AppState>) {
|
||||
return action.state ?? state;
|
||||
} else {
|
||||
return new AppState(
|
||||
auth: authReducer(state.auth, action),
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
return AppState(
|
||||
isLoading: loadingReducer(state.isLoading, action),
|
||||
auth: authReducer(state.auth, action),
|
||||
products: productsReducer(state.products, action),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,39 @@
|
|||
import 'package:meta/meta.dart';
|
||||
import 'package:invoiceninja/data/models/models.dart';
|
||||
import 'package:invoiceninja/redux/auth/auth_state.dart';
|
||||
|
||||
@immutable
|
||||
class AppState {
|
||||
final bool isLoading;
|
||||
final AuthState auth;
|
||||
final List<ProductEntity> products;
|
||||
|
||||
AppState(
|
||||
{this.isLoading = false,
|
||||
AuthState auth,
|
||||
this.products = const []});
|
||||
|
||||
|
||||
factory AppState.loading() => AppState(isLoading: true);
|
||||
|
||||
/*
|
||||
static AppState rehydrationJSON(dynamic json) => new AppState(
|
||||
auth: new AuthState.fromJSON(json['auth'])
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'auth': auth.toJSON()
|
||||
};
|
||||
*/
|
||||
|
||||
AppState copyWith({
|
||||
bool isLoading,
|
||||
AuthState auth,
|
||||
List<ProductEntity> products,
|
||||
}) {
|
||||
return AppState(
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
auth: auth ?? this.auth,
|
||||
products: products ?? this.products,
|
||||
);
|
||||
}
|
||||
|
|
@ -25,6 +41,7 @@ class AppState {
|
|||
@override
|
||||
int get hashCode =>
|
||||
products.hashCode ^
|
||||
auth.hashCode ^
|
||||
isLoading.hashCode;
|
||||
|
||||
@override
|
||||
|
|
@ -33,6 +50,7 @@ class AppState {
|
|||
other is AppState &&
|
||||
runtimeType == other.runtimeType &&
|
||||
products == other.products &&
|
||||
auth == other.auth &&
|
||||
isLoading == other.isLoading;
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja/data/models/models.dart';
|
||||
|
||||
class UserLoginRequest {}
|
||||
|
||||
class UserLoginSuccess {
|
||||
final User user;
|
||||
|
||||
UserLoginSuccess(this.user);
|
||||
}
|
||||
|
||||
class UserLoginFailure {
|
||||
final String error;
|
||||
|
||||
UserLoginFailure(this.error);
|
||||
}
|
||||
|
||||
class UserLogout {}
|
||||
|
||||
final Function login = (BuildContext context, String username, String password) {
|
||||
return (Store<AppState> store) {
|
||||
store.dispatch(new UserLoginRequest());
|
||||
if (username == 'asd' && password == 'asd') {
|
||||
store.dispatch(new UserLoginSuccess(new User('placeholder_token', 'placeholder_id')));
|
||||
Navigator.of(context).pushNamedAndRemoveUntil('/main', (_) => false);
|
||||
} else {
|
||||
store.dispatch(new UserLoginFailure('Username or password were incorrect.'));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
final Function logout = (BuildContext context) {
|
||||
return (Store<AppState> store) {
|
||||
store.dispatch(new UserLogout());
|
||||
Navigator.of(context).pushNamedAndRemoveUntil('/login', (_) => false);
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
import 'package:redux/redux.dart';
|
||||
|
||||
import 'package:invoiceninja/redux/auth/auth_actions.dart';
|
||||
import 'package:invoiceninja/redux/auth/auth_state.dart';
|
||||
|
||||
Reducer<AuthState> authReducer = combineReducers([
|
||||
new TypedReducer<AuthState, UserLoginRequest>(userLoginRequestReducer),
|
||||
new TypedReducer<AuthState, UserLoginSuccess>(userLoginSuccessReducer),
|
||||
new TypedReducer<AuthState, UserLoginFailure>(userLoginFailureReducer),
|
||||
new TypedReducer<AuthState, UserLogout>(userLogoutReducer),
|
||||
]);
|
||||
|
||||
AuthState userLoginRequestReducer(AuthState auth, UserLoginRequest action) {
|
||||
return new AuthState().copyWith(
|
||||
isAuthenticated: false,
|
||||
isAuthenticating: true,
|
||||
);
|
||||
}
|
||||
|
||||
AuthState userLoginSuccessReducer(AuthState auth, UserLoginSuccess action) {
|
||||
return new AuthState().copyWith(
|
||||
isAuthenticated: true,
|
||||
isAuthenticating: false,
|
||||
user: action.user
|
||||
);
|
||||
}
|
||||
|
||||
AuthState userLoginFailureReducer(AuthState auth, UserLoginFailure action) {
|
||||
return new AuthState().copyWith(
|
||||
isAuthenticated: false,
|
||||
isAuthenticating: false,
|
||||
error: action.error
|
||||
);
|
||||
}
|
||||
|
||||
AuthState userLogoutReducer(AuthState auth, UserLogout action) {
|
||||
return new AuthState();
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'package:invoiceninja/data/models/models.dart';
|
||||
|
||||
@immutable
|
||||
class AuthState {
|
||||
|
||||
// properties
|
||||
final bool isAuthenticated;
|
||||
final bool isAuthenticating;
|
||||
final User user;
|
||||
final String error;
|
||||
|
||||
// constructor with default
|
||||
AuthState({
|
||||
this.isAuthenticated = false,
|
||||
this.isAuthenticating = false,
|
||||
this.user,
|
||||
this.error,
|
||||
});
|
||||
|
||||
// allows to modify AuthState parameters while cloning previous ones
|
||||
AuthState copyWith({
|
||||
bool isAuthenticated,
|
||||
bool isAuthenticating,
|
||||
String error,
|
||||
User user
|
||||
}) {
|
||||
return new AuthState(
|
||||
isAuthenticated: isAuthenticated ?? this.isAuthenticated,
|
||||
isAuthenticating: isAuthenticating ?? this.isAuthenticating,
|
||||
error: error ?? this.error,
|
||||
user: user ?? this.user,
|
||||
);
|
||||
}
|
||||
|
||||
factory AuthState.fromJSON(Map<String, dynamic> json) => new AuthState(
|
||||
isAuthenticated: json['isAuthenticated'],
|
||||
isAuthenticating: json['isAuthenticating'],
|
||||
error: json['error'],
|
||||
user: json['user'] == null ? null : new User.fromJSON(json['user']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJSON() => <String, dynamic>{
|
||||
'isAuthenticated': this.isAuthenticated,
|
||||
'isAuthenticating': this.isAuthenticating,
|
||||
'user': this.user == null ? null : this.user.toJSON(),
|
||||
'error': this.error,
|
||||
};
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''{
|
||||
isAuthenticated: $isAuthenticated,
|
||||
isAuthenticating: $isAuthenticating,
|
||||
user: $user,
|
||||
error: $error
|
||||
}''';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
static String tag = 'login-page';
|
||||
@override
|
||||
_LoginScreenState createState() => new _LoginScreenState();
|
||||
}
|
||||
|
||||
class _LoginScreenState extends State<LoginScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final email = TextFormField(
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
autofocus: false,
|
||||
validator: (value) {
|
||||
if (value.isEmpty) {
|
||||
return 'This field is required';
|
||||
}
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Email',
|
||||
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
|
||||
),
|
||||
);
|
||||
|
||||
final password = TextFormField(
|
||||
autofocus: false,
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Password',
|
||||
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
|
||||
),
|
||||
);
|
||||
|
||||
final loginButton = Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: Material(
|
||||
//borderRadius: BorderRadius.circular(30.0),
|
||||
shadowColor: Colors.lightBlueAccent.shade100,
|
||||
elevation: 5.0,
|
||||
child: MaterialButton(
|
||||
minWidth: 200.0,
|
||||
height: 42.0,
|
||||
onPressed: () {
|
||||
//Navigator.of(context).pushNamed(HomeScreen.tag);
|
||||
},
|
||||
color: Colors.lightBlueAccent,
|
||||
child: Text('Log In', style: TextStyle(color: Colors.white)),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
/*
|
||||
final forgotLabel = FlatButton(
|
||||
child: Text(
|
||||
'Forgot password?',
|
||||
style: TextStyle(color: Colors.black54),
|
||||
),
|
||||
onPressed: () {},
|
||||
);
|
||||
*/
|
||||
|
||||
return Scaffold(
|
||||
//backgroundColor: Colors.white,
|
||||
body: Center(
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
children: <Widget>[
|
||||
SizedBox(height: 48.0),
|
||||
email,
|
||||
SizedBox(height: 8.0),
|
||||
password,
|
||||
SizedBox(height: 24.0),
|
||||
loginButton,
|
||||
//forgotLabel
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja/redux/auth/auth_actions.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
@override
|
||||
_LoginScreenState createState() => _LoginScreenState();
|
||||
}
|
||||
|
||||
class _LoginScreenState extends State<LoginScreen> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
String _username;
|
||||
String _password;
|
||||
|
||||
void _submit() {
|
||||
final form = _formKey.currentState;
|
||||
|
||||
if (form.validate()) {
|
||||
form.save();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StoreConnector<AppState, dynamic>(
|
||||
converter: (Store<AppState> store) {
|
||||
return (BuildContext context, String username, String password) =>
|
||||
store.dispatch(login(context, username, password));
|
||||
}, builder: (BuildContext context, loginAction) {
|
||||
return Scaffold(
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(left: 24.0, right: 24.0, top: 40.0),
|
||||
children: [
|
||||
TextFormField(
|
||||
decoration: InputDecoration(labelText: 'Email'),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
//contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
|
||||
//autofocus: false,
|
||||
validator: (val) =>
|
||||
val.isEmpty ? 'Please enter your email.' : null,
|
||||
onSaved: (val) => _username = val,
|
||||
),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(labelText: 'Password'),
|
||||
validator: (val) =>
|
||||
val.isEmpty ? 'Please enter your password.' : null,
|
||||
onSaved: (val) => _password = val,
|
||||
obscureText: true,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20.0),
|
||||
child: Material(
|
||||
//borderRadius: BorderRadius.circular(30.0),
|
||||
shadowColor: Colors.lightBlueAccent.shade100,
|
||||
elevation: 5.0,
|
||||
child: MaterialButton(
|
||||
minWidth: 200.0,
|
||||
height: 42.0,
|
||||
onPressed: () {
|
||||
_submit();
|
||||
loginAction(context, _username, _password);
|
||||
//Navigator.of(context).pushNamed(HomeScreen.tag);
|
||||
},
|
||||
color: Colors.lightBlueAccent,
|
||||
child:
|
||||
Text('Log In', style: TextStyle(color: Colors.white)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue