Google login
This commit is contained in:
parent
1e76adbdd2
commit
3eb618c5e0
|
|
@ -13,4 +13,5 @@ build/
|
||||||
.flutter-plugins
|
.flutter-plugins
|
||||||
|
|
||||||
.env.dart
|
.env.dart
|
||||||
key.properties
|
key.properties
|
||||||
|
google-services.json
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
18
pubspec.lock
18
pubspec.lock
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue