Working on login
This commit is contained in:
parent
9487c89307
commit
b444ce2e2a
|
|
@ -16,7 +16,7 @@ class FileStorage {
|
|||
);
|
||||
|
||||
/// LoadProducts
|
||||
Future<List<ProductEntity>> loadProducts() async {
|
||||
Future<List<dynamic>> loadData() async {
|
||||
final file = await _getLocalFile();
|
||||
final contents = await file.readAsString();
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ class FileStorage {
|
|||
*/
|
||||
}
|
||||
|
||||
Future<File> saveProducts(List<ProductEntity> products) async {
|
||||
Future<File> saveData(List<dynamic> products) async {
|
||||
final file = await _getLocalFile();
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -3,18 +3,18 @@ import 'package:json_annotation/json_annotation.dart';
|
|||
part 'entities.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class ProductResponse extends Object with _$ProductResponseSerializerMixin {
|
||||
class BaseResponse extends Object with _$BaseResponseSerializerMixin {
|
||||
//final String message;
|
||||
|
||||
@JsonKey(name: "data")
|
||||
final List<ProductEntity> products;
|
||||
final List<dynamic> data;
|
||||
|
||||
ProductResponse(
|
||||
BaseResponse(
|
||||
//this.message,
|
||||
this.products,
|
||||
this.data,
|
||||
);
|
||||
|
||||
factory ProductResponse.fromJson(Map<String, dynamic> json) => _$ProductResponseFromJson(json);
|
||||
factory BaseResponse.fromJson(Map<String, dynamic> json) => _$BaseResponseFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
|
|
|
|||
|
|
@ -6,16 +6,12 @@ part of 'entities.dart';
|
|||
// Generator: JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
ProductResponse _$ProductResponseFromJson(Map<String, dynamic> json) =>
|
||||
new ProductResponse((json['data'] as List)
|
||||
?.map((e) => e == null
|
||||
? null
|
||||
: new ProductEntity.fromJson(e as Map<String, dynamic>))
|
||||
?.toList());
|
||||
BaseResponse _$BaseResponseFromJson(Map<String, dynamic> json) =>
|
||||
new BaseResponse(json['data'] as List);
|
||||
|
||||
abstract class _$ProductResponseSerializerMixin {
|
||||
List<ProductEntity> get products;
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{'data': products};
|
||||
abstract class _$BaseResponseSerializerMixin {
|
||||
List<dynamic> get data;
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{'data': data};
|
||||
}
|
||||
|
||||
ProductEntity _$ProductEntityFromJson(Map<String, dynamic> json) =>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ export 'package:invoiceninja/data/models/entities.dart';
|
|||
|
||||
class User {
|
||||
final String token;
|
||||
final String id;
|
||||
final int id;
|
||||
|
||||
User(this.token, this.id);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
import 'dart:core';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'package:invoiceninja/redux/auth/auth_state.dart';
|
||||
import 'package:invoiceninja/data/models/entities.dart';
|
||||
import 'package:invoiceninja/data/repositories/repositories.dart';
|
||||
import 'package:invoiceninja/data/file_storage.dart';
|
||||
|
|
@ -9,7 +10,7 @@ import 'package:invoiceninja/data/web_client.dart';
|
|||
|
||||
/// A class that glues together our local file storage and web client. It has a
|
||||
/// clear responsibility: Load Products and Persist products.
|
||||
class ProductsRepositoryFlutter implements ProductsRepository {
|
||||
class ProductsRepositoryFlutter implements BaseRepository {
|
||||
final FileStorage fileStorage;
|
||||
final WebClient webClient;
|
||||
|
||||
|
|
@ -21,29 +22,35 @@ class ProductsRepositoryFlutter implements ProductsRepository {
|
|||
/// Loads products first from File storage. If they don't exist or encounter an
|
||||
/// error, it attempts to load the Products from a Web Client.
|
||||
@override
|
||||
Future<List<ProductEntity>> loadProducts() async {
|
||||
|
||||
Future<List<dynamic>> loadData(AuthState auth) async {
|
||||
print('ProductRepo: loadProducts...');
|
||||
|
||||
final products = await webClient.fetchData(
|
||||
auth.url + '/products', auth.token);
|
||||
|
||||
//fileStorage.saveProducts(products);
|
||||
|
||||
return products.map((product) => ProductEntity.fromJson(product)).toList();
|
||||
|
||||
/*
|
||||
try {
|
||||
return await fileStorage.loadProducts();
|
||||
} catch (e) {
|
||||
final products = await webClient.fetchProducts();
|
||||
return await fileStorage.loadData();
|
||||
} catch (exception) {
|
||||
final products = await webClient.fetchData(
|
||||
auth.url + '/products', auth.token);
|
||||
|
||||
print('ProductRepo: result');
|
||||
print(products);
|
||||
|
||||
fileStorage.saveProducts(products);
|
||||
//fileStorage.saveProducts(products);
|
||||
|
||||
return products;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// Persists products to local disk and the web
|
||||
@override
|
||||
Future saveProducts(List<ProductEntity> products) {
|
||||
Future saveData(List<dynamic> products) {
|
||||
return Future.wait<dynamic>([
|
||||
fileStorage.saveProducts(products),
|
||||
fileStorage.saveData(products),
|
||||
webClient.postProducts(products),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:core';
|
||||
import 'package:invoiceninja/redux/auth/auth_state.dart';
|
||||
|
||||
import 'package:invoiceninja/data/models/entities.dart';
|
||||
|
||||
|
|
@ -11,11 +12,11 @@ import 'package:invoiceninja/data/models/entities.dart';
|
|||
/// The domain layer should depend on this abstract class, and each app can
|
||||
/// inject the correct implementation depending on the environment, such as
|
||||
/// web or Flutter.
|
||||
abstract class ProductsRepository {
|
||||
abstract class BaseRepository {
|
||||
/// Loads products first from File storage. If they don't exist or encounter an
|
||||
/// error, it attempts to load the Todos from a Web Client.
|
||||
Future<List<ProductEntity>> loadProducts();
|
||||
Future<List<dynamic>> loadData(AuthState auth);
|
||||
|
||||
// Persists products to local disk and the web
|
||||
Future saveProducts(List<ProductEntity> products);
|
||||
Future saveData(List<dynamic> data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,20 +13,16 @@ class WebClient {
|
|||
const WebClient();
|
||||
|
||||
/// Mock that "fetches" some Products from a "web service" after a short delay
|
||||
Future<List<ProductEntity>> fetchProducts() async {
|
||||
|
||||
print('Web Client: fetchProducts...');
|
||||
|
||||
var url = "";
|
||||
Future<List<dynamic>> fetchData(String url, String token) async {
|
||||
|
||||
final response = await http.Client().get(
|
||||
url,
|
||||
headers: {'X-Ninja-Token': ""},
|
||||
headers: {'X-Ninja-Token': token},
|
||||
);
|
||||
|
||||
return ProductResponse
|
||||
return BaseResponse
|
||||
.fromJson(json.decode(response.body))
|
||||
.products
|
||||
.data
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ 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';
|
||||
import 'package:redux_logging/redux_logging.dart';
|
||||
|
||||
void main() {
|
||||
|
||||
|
|
@ -25,7 +26,11 @@ class InvoiceNinjaApp extends StatelessWidget {
|
|||
final store = Store<AppState>(
|
||||
appReducer,
|
||||
initialState: AppState.loading(),
|
||||
middleware: createStoreProductsMiddleware(),
|
||||
middleware: []
|
||||
..addAll(createStoreProductsMiddleware())
|
||||
..addAll([
|
||||
LoggingMiddleware.printer(),
|
||||
])
|
||||
);
|
||||
|
||||
@override
|
||||
|
|
@ -40,7 +45,6 @@ class InvoiceNinjaApp extends StatelessWidget {
|
|||
theme: new ThemeData.dark(),
|
||||
title: 'Invoice Ninja',
|
||||
routes: {
|
||||
/*
|
||||
NinjaRoutes.login: (context) {
|
||||
return StoreBuilder<AppState>(
|
||||
builder: (context, store) {
|
||||
|
|
@ -48,7 +52,6 @@ class InvoiceNinjaApp extends StatelessWidget {
|
|||
},
|
||||
);
|
||||
},
|
||||
*/
|
||||
NinjaRoutes.dashboard: (context) {
|
||||
return StoreBuilder<AppState>(
|
||||
builder: (context, store) {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ AppState appReducer(AppState state, action) {
|
|||
|
||||
return AppState(
|
||||
isLoading: loadingReducer(state.isLoading, action),
|
||||
auth: authReducer(state.auth, action),
|
||||
products: productsReducer(state.products, action),
|
||||
auth: authReducer(state.auth, action),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ class AppState {
|
|||
|
||||
AppState(
|
||||
{this.isLoading = false,
|
||||
AuthState auth,
|
||||
this.products = const []});
|
||||
this.products = const [],
|
||||
AuthState auth}):
|
||||
auth = auth ?? new AuthState();
|
||||
|
||||
|
||||
factory AppState.loading() => AppState(isLoading: true);
|
||||
|
|
@ -55,6 +56,6 @@ class AppState {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppState{isLoading: $isLoading}';
|
||||
return 'AppState{isLoading: $isLoading, url: ${auth.url}, token ${auth.token}}';
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,16 @@ import 'package:redux/redux.dart';
|
|||
import 'package:invoiceninja/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja/data/models/models.dart';
|
||||
|
||||
class UserLoginRequest {}
|
||||
class UserLoginRequest {
|
||||
final String email;
|
||||
final String password;
|
||||
final String url;
|
||||
final String secret;
|
||||
final String token;
|
||||
|
||||
//UserLoginRequest(this.email, this.password, this.url, this.secret);
|
||||
UserLoginRequest(this.url, this.token);
|
||||
}
|
||||
|
||||
class UserLoginSuccess {
|
||||
final User user;
|
||||
|
|
@ -19,21 +28,21 @@ class UserLoginFailure {
|
|||
|
||||
class UserLogout {}
|
||||
|
||||
final Function login = (BuildContext context, String username, String password) {
|
||||
/*
|
||||
final Function login = (BuildContext context, String url, String token) {
|
||||
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.'));
|
||||
}
|
||||
store.dispatch(new UserLoginRequest(url, token));
|
||||
store.dispatch(new UserLoginSuccess(new User(token, 1)));
|
||||
Navigator.of(context).pushNamed('/dashboard');
|
||||
};
|
||||
};
|
||||
*/
|
||||
|
||||
/*
|
||||
final Function logout = (BuildContext context) {
|
||||
return (Store<AppState> store) {
|
||||
store.dispatch(new UserLogout());
|
||||
Navigator.of(context).pushNamedAndRemoveUntil('/login', (_) => false);
|
||||
};
|
||||
};
|
||||
*/
|
||||
|
|
@ -4,21 +4,24 @@ 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),
|
||||
TypedReducer<AuthState, UserLoginRequest>(userLoginRequestReducer),
|
||||
TypedReducer<AuthState, UserLoginSuccess>(userLoginSuccessReducer),
|
||||
TypedReducer<AuthState, UserLoginFailure>(userLoginFailureReducer),
|
||||
TypedReducer<AuthState, UserLogout>(userLogoutReducer),
|
||||
]);
|
||||
|
||||
AuthState userLoginRequestReducer(AuthState auth, UserLoginRequest action) {
|
||||
return new AuthState().copyWith(
|
||||
return AuthState().copyWith(
|
||||
url: action.url,
|
||||
token: action.token,
|
||||
secret: action.secret,
|
||||
isAuthenticated: false,
|
||||
isAuthenticating: true,
|
||||
);
|
||||
}
|
||||
|
||||
AuthState userLoginSuccessReducer(AuthState auth, UserLoginSuccess action) {
|
||||
return new AuthState().copyWith(
|
||||
return AuthState().copyWith(
|
||||
isAuthenticated: true,
|
||||
isAuthenticating: false,
|
||||
user: action.user
|
||||
|
|
@ -26,7 +29,7 @@ AuthState userLoginSuccessReducer(AuthState auth, UserLoginSuccess action) {
|
|||
}
|
||||
|
||||
AuthState userLoginFailureReducer(AuthState auth, UserLoginFailure action) {
|
||||
return new AuthState().copyWith(
|
||||
return AuthState().copyWith(
|
||||
isAuthenticated: false,
|
||||
isAuthenticating: false,
|
||||
error: action.error
|
||||
|
|
@ -34,5 +37,5 @@ AuthState userLoginFailureReducer(AuthState auth, UserLoginFailure action) {
|
|||
}
|
||||
|
||||
AuthState userLogoutReducer(AuthState auth, UserLogout action) {
|
||||
return new AuthState();
|
||||
return AuthState();
|
||||
}
|
||||
|
|
@ -6,6 +6,9 @@ import 'package:invoiceninja/data/models/models.dart';
|
|||
class AuthState {
|
||||
|
||||
// properties
|
||||
final String url;
|
||||
final String secret;
|
||||
final String token;
|
||||
final bool isAuthenticated;
|
||||
final bool isAuthenticating;
|
||||
final User user;
|
||||
|
|
@ -13,6 +16,9 @@ class AuthState {
|
|||
|
||||
// constructor with default
|
||||
AuthState({
|
||||
this.url,
|
||||
this.secret,
|
||||
this.token,
|
||||
this.isAuthenticated = false,
|
||||
this.isAuthenticating = false,
|
||||
this.user,
|
||||
|
|
@ -21,12 +27,18 @@ class AuthState {
|
|||
|
||||
// allows to modify AuthState parameters while cloning previous ones
|
||||
AuthState copyWith({
|
||||
String url,
|
||||
String secret,
|
||||
String token,
|
||||
bool isAuthenticated,
|
||||
bool isAuthenticating,
|
||||
String error,
|
||||
User user
|
||||
}) {
|
||||
return new AuthState(
|
||||
url: url ?? this.url,
|
||||
token: token ?? this.token,
|
||||
secret: secret ?? this.secret,
|
||||
isAuthenticated: isAuthenticated ?? this.isAuthenticated,
|
||||
isAuthenticating: isAuthenticating ?? this.isAuthenticating,
|
||||
error: error ?? this.error,
|
||||
|
|
@ -35,6 +47,9 @@ class AuthState {
|
|||
}
|
||||
|
||||
factory AuthState.fromJSON(Map<String, dynamic> json) => new AuthState(
|
||||
url: json['url'],
|
||||
token: json['token'],
|
||||
secret: json['secret'],
|
||||
isAuthenticated: json['isAuthenticated'],
|
||||
isAuthenticating: json['isAuthenticating'],
|
||||
error: json['error'],
|
||||
|
|
@ -42,6 +57,9 @@ class AuthState {
|
|||
);
|
||||
|
||||
Map<String, dynamic> toJSON() => <String, dynamic>{
|
||||
'url': this.url,
|
||||
'token': this.token,
|
||||
'secret': this.secret,
|
||||
'isAuthenticated': this.isAuthenticated,
|
||||
'isAuthenticating': this.isAuthenticating,
|
||||
'user': this.user == null ? null : this.user.toJSON(),
|
||||
|
|
@ -51,6 +69,7 @@ class AuthState {
|
|||
@override
|
||||
String toString() {
|
||||
return '''{
|
||||
url: $url,
|
||||
isAuthenticated: $isAuthenticated,
|
||||
isAuthenticating: $isAuthenticating,
|
||||
user: $user,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,16 @@ import 'package:invoiceninja/data/models/models.dart';
|
|||
|
||||
class LoadProductsAction {}
|
||||
|
||||
class ProductsNotLoadedAction {}
|
||||
class ProductsNotLoadedAction {
|
||||
final dynamic error;
|
||||
|
||||
ProductsNotLoadedAction(this.error);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProductsNotLoadedAction{products: $error}';
|
||||
}
|
||||
}
|
||||
|
||||
class ProductsLoadedAction {
|
||||
final List<ProductEntity> products;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ import 'package:invoiceninja/data/repositories/repositories.dart';
|
|||
import 'package:invoiceninja/data/repositories/product_repository.dart';
|
||||
import 'package:invoiceninja/data/file_storage.dart';
|
||||
import 'package:invoiceninja/redux/product/product_selectors.dart';
|
||||
import 'package:invoiceninja/data/models/entities.dart';
|
||||
|
||||
List<Middleware<AppState>> createStoreProductsMiddleware([
|
||||
ProductsRepository repository = const ProductsRepositoryFlutter(
|
||||
BaseRepository repository = const ProductsRepositoryFlutter(
|
||||
fileStorage: const FileStorage(
|
||||
'__invoiceninja__',
|
||||
getApplicationDocumentsDirectory,
|
||||
|
|
@ -24,7 +25,7 @@ List<Middleware<AppState>> createStoreProductsMiddleware([
|
|||
];
|
||||
}
|
||||
|
||||
Middleware<AppState> _createSaveProducts(ProductsRepository repository) {
|
||||
Middleware<AppState> _createSaveProducts(BaseRepository repository) {
|
||||
return (Store<AppState> store, action, NextDispatcher next) {
|
||||
next(action);
|
||||
|
||||
|
|
@ -36,14 +37,12 @@ Middleware<AppState> _createSaveProducts(ProductsRepository repository) {
|
|||
};
|
||||
}
|
||||
|
||||
Middleware<AppState> _createLoadProducts(ProductsRepository repository) {
|
||||
Middleware<AppState> _createLoadProducts(BaseRepository repository) {
|
||||
return (Store<AppState> store, action, NextDispatcher next) {
|
||||
|
||||
print('Product Middleware: createLoadProducts...');
|
||||
|
||||
//repository.loadProducts();
|
||||
|
||||
repository.loadProducts().then(
|
||||
repository.loadData(store.state.auth).then(
|
||||
(products) {
|
||||
store.dispatch(
|
||||
ProductsLoadedAction(
|
||||
|
|
@ -52,7 +51,7 @@ Middleware<AppState> _createLoadProducts(ProductsRepository repository) {
|
|||
),
|
||||
);
|
||||
},
|
||||
).catchError((_) => store.dispatch(ProductsNotLoadedAction()));
|
||||
).catchError((error) => store.dispatch(ProductsNotLoadedAction(error)));
|
||||
|
||||
next(action);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
class NinjaRoutes {
|
||||
//static final login = "/";
|
||||
static final dashboard = "/";
|
||||
static final login = "/";
|
||||
static final dashboard = "/dashboard";
|
||||
static final clientList = "/clientList";
|
||||
static final productList = "/productList";
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ 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';
|
||||
import 'package:invoiceninja/data/models/models.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
@override
|
||||
|
|
@ -14,6 +15,9 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||
|
||||
String _username;
|
||||
String _password;
|
||||
String _url;
|
||||
String _token;
|
||||
String _secret;
|
||||
|
||||
void _submit() {
|
||||
final form = _formKey.currentState;
|
||||
|
|
@ -27,8 +31,11 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||
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));
|
||||
return (BuildContext context, String url, String token) {
|
||||
store.dispatch(UserLoginRequest(url, token));
|
||||
//store.dispatch(UserLoginSuccess(User(token, 1)));
|
||||
Navigator.of(context).pushNamed('/dashboard');
|
||||
};
|
||||
}, builder: (BuildContext context, loginAction) {
|
||||
return Scaffold(
|
||||
body: Form(
|
||||
|
|
@ -37,26 +44,41 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(left: 24.0, right: 24.0, top: 40.0),
|
||||
children: [
|
||||
TextFormField(
|
||||
decoration: InputDecoration(labelText: 'URL'),
|
||||
validator: (val) =>
|
||||
val.isEmpty ? 'Please enter your URL.' : null,
|
||||
onSaved: (val) => _url = val,
|
||||
),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(labelText: 'Token'),
|
||||
validator: (val) =>
|
||||
val.isEmpty ? 'Please enter your token.' : null,
|
||||
onSaved: (val) => _token = val,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 40.0, bottom: 20.0),
|
||||
child: Text(
|
||||
'Note: this will be changed to email/password in the future'),
|
||||
),
|
||||
/*
|
||||
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(
|
||||
|
|
@ -68,7 +90,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||
height: 42.0,
|
||||
onPressed: () {
|
||||
_submit();
|
||||
loginAction(context, _username, _password);
|
||||
loginAction(context, _url, _token);
|
||||
//Navigator.of(context).pushNamed(HomeScreen.tag);
|
||||
},
|
||||
color: Colors.lightBlueAccent,
|
||||
|
|
|
|||
|
|
@ -27,8 +27,6 @@ class ProductList extends StatelessWidget {
|
|||
}
|
||||
|
||||
ListView _buildListView() {
|
||||
print('_buildListView');
|
||||
print(products);
|
||||
return ListView.builder(
|
||||
key: NinjaKeys.productList,
|
||||
itemCount: products.length,
|
||||
|
|
|
|||
|
|
@ -347,6 +347,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
redux_logging:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: redux_logging
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ dependencies:
|
|||
cupertino_icons: ^0.1.0
|
||||
json_annotation: ^0.2.3
|
||||
path_provider: "^0.4.0"
|
||||
|
||||
redux_logging: "^0.3.0"
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||
Loading…
Reference in New Issue