Google login

This commit is contained in:
Hillel Coren 2018-08-14 23:02:34 -07:00
parent 1e76adbdd2
commit 3eb618c5e0
8 changed files with 97 additions and 41 deletions

3
.gitignore vendored
View File

@ -13,4 +13,5 @@ build/
.flutter-plugins .flutter-plugins
.env.dart .env.dart
key.properties key.properties
google-services.json

View File

@ -12,6 +12,7 @@ if (flutterRoot == null) {
} }
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystorePropertiesFile = rootProject.file("key.properties") def keystorePropertiesFile = rootProject.file("key.properties")

View File

@ -6,6 +6,7 @@ buildscript {
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.google.gms:google-services:3.2.1'
} }
} }

View File

@ -5,7 +5,6 @@ import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart'; import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
import 'package:invoiceninja_flutter/ui/app/invoice/invoice_email_view.dart'; import 'package:invoiceninja_flutter/ui/app/invoice/invoice_email_view.dart';
import 'package:invoiceninja_flutter/utils/completers.dart'; import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart';

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/redux/auth/auth_state.dart'; import 'package:invoiceninja_flutter/ui/app/buttons/elevated_button.dart';
import 'package:invoiceninja_flutter/ui/app/progress_button.dart'; import 'package:invoiceninja_flutter/ui/app/progress_button.dart';
import 'package:invoiceninja_flutter/ui/auth/login_vm.dart';
import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart';
import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/ui/app/form_card.dart'; import 'package:invoiceninja_flutter/ui/app/form_card.dart';
@ -9,15 +10,11 @@ import 'package:invoiceninja_flutter/utils/keys.dart';
class LoginView extends StatefulWidget { class LoginView extends StatefulWidget {
final bool isLoading; final LoginVM viewModel;
final AuthState authState;
final Function(BuildContext, String, String, String, String) onLoginPressed;
const LoginView({ const LoginView({
Key key, Key key,
@required this.isLoading, @required this.viewModel,
@required this.authState,
@required this.onLoginPressed,
}) : super(key: key); }) : super(key: key);
@override @override
@ -40,7 +37,7 @@ class _LoginState extends State<LoginView> {
@override @override
void didChangeDependencies() { void didChangeDependencies() {
final state = widget.authState; final state = widget.viewModel.authState;
_emailController.text = state.email; _emailController.text = state.email;
_passwordController.text = state.password; _passwordController.text = state.password;
_urlController.text = formatApiUrlReadable(state.url); _urlController.text = formatApiUrlReadable(state.url);
@ -62,7 +59,10 @@ class _LoginState extends State<LoginView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (!widget.authState.isInitialized) { final localization = AppLocalization.of(context);
final viewModel = widget.viewModel;
if (!viewModel.authState.isInitialized) {
return Container(); return Container();
} }
@ -83,10 +83,10 @@ class _LoginState extends State<LoginView> {
key: _emailKey, key: _emailKey,
autocorrect: false, autocorrect: false,
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalization.of(context).email), labelText: localization.email),
keyboardType: TextInputType.emailAddress, keyboardType: TextInputType.emailAddress,
validator: (val) => val.isEmpty || val.trim().isEmpty validator: (val) => val.isEmpty || val.trim().isEmpty
? AppLocalization.of(context).pleaseEnterYourEmail ? localization.pleaseEnterYourEmail
: null, : null,
), ),
TextFormField( TextFormField(
@ -94,9 +94,9 @@ class _LoginState extends State<LoginView> {
key: _passwordKey, key: _passwordKey,
autocorrect: false, autocorrect: false,
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalization.of(context).password), labelText: localization.password),
validator: (val) => val.isEmpty || val.trim().isEmpty validator: (val) => val.isEmpty || val.trim().isEmpty
? AppLocalization.of(context).pleaseEnterYourPassword ? localization.pleaseEnterYourPassword
: null, : null,
obscureText: true, obscureText: true,
), ),
@ -105,9 +105,9 @@ class _LoginState extends State<LoginView> {
key: _urlKey, key: _urlKey,
autocorrect: false, autocorrect: false,
decoration: decoration:
InputDecoration(labelText: AppLocalization.of(context).url), InputDecoration(labelText: localization.url),
validator: (val) => val.isEmpty || val.trim().isEmpty validator: (val) => val.isEmpty || val.trim().isEmpty
? AppLocalization.of(context).pleaseEnterYourUrl ? localization.pleaseEnterYourUrl
: null, : null,
keyboardType: TextInputType.url, keyboardType: TextInputType.url,
), ),
@ -116,21 +116,16 @@ class _LoginState extends State<LoginView> {
key: _secretKey, key: _secretKey,
autocorrect: false, autocorrect: false,
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalization.of(context).secret), labelText: localization.secret),
/*
validator: (val) => val.isEmpty || val.trim().length == 0
? AppLocalization.of(context).pleaseEnterYourPassword
: null,
*/
obscureText: true, obscureText: true,
), ),
widget.authState.error == null viewModel.authState.error == null
? Container() ? Container()
: Container( : Container(
padding: EdgeInsets.only(top: 26.0, bottom: 4.0), padding: EdgeInsets.only(top: 26.0, bottom: 4.0),
child: Center( child: Center(
child: Text( child: Text(
widget.authState.error, viewModel.authState.error,
style: TextStyle( style: TextStyle(
color: Colors.red, color: Colors.red,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -142,13 +137,13 @@ class _LoginState extends State<LoginView> {
), ),
), ),
ProgressButton( ProgressButton(
label: AppLocalization.of(context).login.toUpperCase(), label: localization.login.toUpperCase(),
isLoading: widget.isLoading, isLoading: viewModel.isLoading,
onPressed: () { onPressed: () {
if (!_formKey.currentState.validate()) { if (!_formKey.currentState.validate()) {
return; return;
} }
widget.onLoginPressed( viewModel.onLoginPressed(
context, context,
_emailController.text, _emailController.text,
_passwordController.text, _passwordController.text,
@ -156,6 +151,13 @@ class _LoginState extends State<LoginView> {
_secretController.text); _secretController.text);
}, },
), ),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ElevatedButton(
label: 'Google ${localization.login}'.toUpperCase(),
onPressed: () => viewModel.onGoogleLoginPressed(context),
),
),
], ],
); );
} }

View File

@ -11,6 +11,7 @@ import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart'; import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart';
import 'package:invoiceninja_flutter/ui/auth/login.dart'; import 'package:invoiceninja_flutter/ui/auth/login.dart';
import 'package:invoiceninja_flutter/redux/auth/auth_state.dart'; import 'package:invoiceninja_flutter/redux/auth/auth_state.dart';
import 'package:google_sign_in/google_sign_in.dart';
class LoginScreen extends StatelessWidget { class LoginScreen extends StatelessWidget {
const LoginScreen({Key key}) : super(key: key); const LoginScreen({Key key}) : super(key: key);
@ -22,11 +23,9 @@ class LoginScreen extends StatelessWidget {
return Scaffold( return Scaffold(
body: StoreConnector<AppState, LoginVM>( body: StoreConnector<AppState, LoginVM>(
converter: LoginVM.fromStore, converter: LoginVM.fromStore,
builder: (context, vm) { builder: (context, viewModel) {
return LoginView( return LoginView(
isLoading: vm.isLoading, viewModel: viewModel,
authState: vm.authState,
onLoginPressed: vm.onLoginPressed,
); );
}, },
), ),
@ -38,31 +37,68 @@ class LoginVM {
bool isLoading; bool isLoading;
AuthState authState; AuthState authState;
final Function(BuildContext, String, String, String, String) onLoginPressed; final Function(BuildContext, String, String, String, String) onLoginPressed;
final Function(BuildContext) onGoogleLoginPressed;
LoginVM({ LoginVM({
@required this.isLoading, @required this.isLoading,
@required this.authState, @required this.authState,
@required this.onLoginPressed, @required this.onLoginPressed,
@required this.onGoogleLoginPressed,
}); });
static LoginVM fromStore(Store<AppState> store) { static LoginVM fromStore(Store<AppState> store) {
final GoogleSignIn _googleSignIn = new GoogleSignIn(
scopes: [
'email',
'openid',
'profile',
],
);
_googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount account) {
print('gogole account: $account');
if (account != null) {
account.authentication.then((GoogleSignInAuthentication value) {
print('token: ${value.idToken}');
print('token: ${value.accessToken}');
});
}
});
//_googleSignIn.signInSilently();
return LoginVM( return LoginVM(
isLoading: store.state.isLoading, isLoading: store.state.isLoading,
authState: store.state.authState, authState: store.state.authState,
onGoogleLoginPressed: (context) async {
try {
final result = await _googleSignIn.signIn();
print(result);
} catch (error) {
print(error);
}
},
onLoginPressed: (BuildContext context, String email, String password, onLoginPressed: (BuildContext context, String email, String password,
String url, String secret) { String url, String secret) async {
try {
await _googleSignIn.signIn();
} catch (error) {
print(error);
}
if (store.state.isLoading) { if (store.state.isLoading) {
return; return;
} }
final Completer<Null> completer = Completer<Null>(); final Completer<Null> completer = Completer<Null>();
store.dispatch(UserLoginRequest( store.dispatch(UserLoginRequest(
completer: completer, completer: completer,
email: email.trim(), email: email.trim(),
password: password.trim(), password: password.trim(),
url: url.trim(), url: url.trim(),
secret: secret.trim(), secret: secret.trim(),
platform: getPlatform(context), platform: getPlatform(context),
)); ));
completer.future.then((_) { completer.future.then((_) {
Navigator.of(context).pushReplacementNamed(DashboardScreen.route); Navigator.of(context).pushReplacementNamed(DashboardScreen.route);

View File

@ -162,6 +162,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.0.1" version: "5.0.1"
firebase_auth:
dependency: "direct main"
description:
name: firebase_auth
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.18"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
@ -199,7 +206,7 @@ packages:
name: flutter_html_view name: flutter_html_view
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.5.3" version: "0.5.4"
flutter_localizations: flutter_localizations:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -252,6 +259,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.7" version: "1.1.7"
google_sign_in:
dependency: "direct main"
description:
name: google_sign_in
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.4"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:
@ -620,7 +634,7 @@ packages:
name: video_player name: video_player
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.5.4" version: "0.6.4"
vm_service_client: vm_service_client:
dependency: transitive dependency: transitive
description: description:

View File

@ -25,6 +25,8 @@ dependencies:
intl: ^0.15.6 intl: ^0.15.6
flutter_pdf_viewer: ^0.0.2 flutter_pdf_viewer: ^0.0.2
flutter_html_view: ^0.5.2 flutter_html_view: ^0.5.2
google_sign_in: ^3.0.4
firebase_auth: ^0.5.14
dev_dependencies: dev_dependencies:
flutter_driver: flutter_driver: