WT1.1.3 build 2 iOS. Apple SignIn, Google SignIn
This commit is contained in:
@@ -21,6 +21,7 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> with Trans {
|
||||
final BuildContext context;
|
||||
final bool isRegistration;
|
||||
bool dataPolicyAllowed = false;
|
||||
bool obscure = true;
|
||||
LoginBloc({this.accountBloc, this.userRepository, this.context, this.isRegistration}) : super(LoginInitial());
|
||||
|
||||
@override
|
||||
@@ -51,6 +52,20 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> with Trans {
|
||||
Flurry.logEvent("Login");
|
||||
Flurry.logEvent("LoginFB");
|
||||
yield LoginSuccess();
|
||||
} else if (event is LoginGoogle) {
|
||||
yield LoginLoading();
|
||||
await userRepository.getUserByGoogle();
|
||||
accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn));
|
||||
Flurry.logEvent("Login");
|
||||
Flurry.logEvent("LoginGoogle");
|
||||
yield LoginSuccess();
|
||||
} else if (event is LoginApple) {
|
||||
yield LoginLoading();
|
||||
await userRepository.getUserByApple();
|
||||
accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn));
|
||||
Flurry.logEvent("Login");
|
||||
Flurry.logEvent("LoginApple");
|
||||
yield LoginSuccess();
|
||||
} else if (event is RegistrationSubmit) {
|
||||
yield LoginLoading();
|
||||
if (!this.dataPolicyAllowed) {
|
||||
@@ -75,9 +90,36 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> with Trans {
|
||||
Flurry.logEvent("RegistrationFB");
|
||||
Flurry.logEvent("Registration");
|
||||
yield LoginSuccess();
|
||||
} else if (event is RegistrationGoogle) {
|
||||
yield LoginLoading();
|
||||
if (!this.dataPolicyAllowed) {
|
||||
yield LoginError();
|
||||
throw Exception("Please accept our data policy");
|
||||
}
|
||||
await userRepository.addUserGoogle();
|
||||
accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn));
|
||||
await saveCustomer();
|
||||
Flurry.logEvent("RegistrationGoogle");
|
||||
Flurry.logEvent("Registration");
|
||||
yield LoginSuccess();
|
||||
} else if (event is RegistrationApple) {
|
||||
yield LoginLoading();
|
||||
if (!this.dataPolicyAllowed) {
|
||||
yield LoginError();
|
||||
throw Exception("Please accept our data policy");
|
||||
}
|
||||
await userRepository.addUserApple();
|
||||
accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn));
|
||||
await saveCustomer();
|
||||
Flurry.logEvent("RegistrationApple");
|
||||
Flurry.logEvent("Registration");
|
||||
yield LoginSuccess();
|
||||
} else if (event is DataProtectionClicked) {
|
||||
this.dataPolicyAllowed = event.marked;
|
||||
yield LoginLoading();
|
||||
this.dataPolicyAllowed = !dataPolicyAllowed;
|
||||
yield LoginReady();
|
||||
} else if (event is LoginPasswordChangeObscure) {
|
||||
this.obscure = !this.obscure;
|
||||
}
|
||||
} on Exception catch (e) {
|
||||
yield LoginError(message: e.toString());
|
||||
|
||||
@@ -35,6 +35,14 @@ class LoginFB extends LoginEvent {
|
||||
const LoginFB();
|
||||
}
|
||||
|
||||
class LoginGoogle extends LoginEvent {
|
||||
const LoginGoogle();
|
||||
}
|
||||
|
||||
class LoginApple extends LoginEvent {
|
||||
const LoginApple();
|
||||
}
|
||||
|
||||
class DataProtectionClicked extends LoginEvent {
|
||||
final bool marked;
|
||||
const DataProtectionClicked({this.marked});
|
||||
@@ -47,3 +55,11 @@ class RegistrationSubmit extends LoginEvent {
|
||||
class RegistrationFB extends LoginEvent {
|
||||
const RegistrationFB();
|
||||
}
|
||||
|
||||
class RegistrationGoogle extends LoginEvent {
|
||||
const RegistrationGoogle();
|
||||
}
|
||||
|
||||
class RegistrationApple extends LoginEvent {
|
||||
const RegistrationApple();
|
||||
}
|
||||
|
||||
@@ -29,14 +29,14 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
|
||||
if (event is SalesLoad) {
|
||||
yield SalesLoading();
|
||||
//Flurry.logEvent("SalesPageOpen");
|
||||
await PlatformPurchaseApi().initPurchasePlatformState();
|
||||
this.getProductSet();
|
||||
// await PlatformPurchaseApi().initPurchasePlatformState();
|
||||
// this.getProductSet();
|
||||
yield SalesReady();
|
||||
} else if (event is SalesPurchase) {
|
||||
final int productId = event.productId;
|
||||
trace("Requesting purchase for" + productId.toString());
|
||||
//Flurry.logEvent("PurchaseRequest");
|
||||
PlatformPurchaseApi().purchase(getSelectedProduct(productId));
|
||||
// PlatformPurchaseApi().purchase(getSelectedProduct(productId));
|
||||
}
|
||||
} on Exception catch (ex) {
|
||||
yield SalesError(message: ex.toString());
|
||||
@@ -53,7 +53,7 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
|
||||
return prod;
|
||||
}
|
||||
|
||||
String getLocalizedPrice(String productId) {
|
||||
/* String getLocalizedPrice(String productId) {
|
||||
String price = "";
|
||||
for (var product in PlatformPurchaseApi().getIAPItems()) {
|
||||
if (Platform.isAndroid) {
|
||||
@@ -109,5 +109,5 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
|
||||
productTest.dateView = DateTime.now();
|
||||
//ProductTestApi().saveProductTest(productTest);
|
||||
//Cache().productTests.add(productTest);
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class SessionBloc extends Bloc<SessionEvent, SessionState> with Logging {
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await this.close();
|
||||
PlatformPurchaseApi().close();
|
||||
//PlatformPurchaseApi().close();
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -32,6 +32,8 @@ import 'package:aitrainer_app/view/reset_password.dart';
|
||||
import 'package:aitrainer_app/view/sales_page.dart';
|
||||
import 'package:aitrainer_app/view/settings.dart';
|
||||
import 'package:aitrainer_app/widgets/home.dart';
|
||||
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||
import 'package:firebase_analytics/observer.dart';
|
||||
import 'package:flurry/flurry.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -163,7 +165,7 @@ class WorkoutTestApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||
//final FirebaseAnalytics analytics = FirebaseAnalytics();
|
||||
final FirebaseAnalytics analytics = FirebaseAnalytics();
|
||||
initFlurry();
|
||||
PushNotificationsManager().init();
|
||||
return MaterialApp(
|
||||
@@ -233,7 +235,7 @@ class WorkoutTestApp extends StatelessWidget {
|
||||
bodyText1: GoogleFonts.openSans(textStyle: TextStyle(fontSize: 14.0)),
|
||||
)),
|
||||
navigatorObservers: [
|
||||
//FirebaseAnalyticsObserver(analytics: analytics),
|
||||
FirebaseAnalyticsObserver(analytics: analytics),
|
||||
],
|
||||
home: AitrainerHome(),
|
||||
);
|
||||
|
||||
@@ -73,7 +73,6 @@ class Cache with Logging {
|
||||
AccessToken accessTokenFacebook;
|
||||
Customer userLoggedIn;
|
||||
String firebaseUid;
|
||||
String facebookUid;
|
||||
|
||||
bool hasPurchased = false;
|
||||
|
||||
@@ -201,7 +200,7 @@ class Cache with Logging {
|
||||
Future<SharedPreferences> prefs = SharedPreferences.getInstance();
|
||||
|
||||
userLoggedIn = customer;
|
||||
final String uid = Cache().firebaseUid == null ? Cache().facebookUid : Cache().firebaseUid;
|
||||
final String uid = Cache().firebaseUid;
|
||||
await setPreferences(prefs, SharePrefsChange.registration, customer.customerId, uid);
|
||||
}
|
||||
|
||||
@@ -219,7 +218,7 @@ class Cache with Logging {
|
||||
|
||||
afterFacebookLogin() async {
|
||||
Future<SharedPreferences> prefs = SharedPreferences.getInstance();
|
||||
await setPreferences(prefs, SharePrefsChange.login, userLoggedIn.customerId, Cache().facebookUid);
|
||||
await setPreferences(prefs, SharePrefsChange.login, userLoggedIn.customerId, Cache().firebaseUid);
|
||||
}
|
||||
|
||||
logout() async {
|
||||
@@ -457,7 +456,4 @@ class Cache with Logging {
|
||||
|
||||
AccessToken get getAccessTokenFacebook => accessTokenFacebook;
|
||||
set setAccessTokenFacebook(AccessToken accessTokenFacebook) => this.accessTokenFacebook = accessTokenFacebook;
|
||||
|
||||
String get getFacebookUid => facebookUid;
|
||||
set setFacebookUid(String facebookUid) => this.facebookUid = facebookUid;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,12 @@ import 'package:aitrainer_app/model/cache.dart';
|
||||
import 'package:aitrainer_app/model/user.dart';
|
||||
import 'package:aitrainer_app/service/customer_service.dart';
|
||||
import 'package:aitrainer_app/service/firebase_api.dart';
|
||||
import 'package:aitrainer_app/service/logging.dart';
|
||||
import 'package:aitrainer_app/util/not_found_exception.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart' as auth;
|
||||
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
|
||||
|
||||
class UserRepository {
|
||||
class UserRepository with Logging {
|
||||
User user;
|
||||
|
||||
UserRepository() {
|
||||
@@ -31,29 +35,166 @@ class UserRepository {
|
||||
await CustomerApi().addUser(modelUser);
|
||||
}
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
log(e.toString());
|
||||
throw new Exception(e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addUserFB() async {
|
||||
final User modelUser = this.user;
|
||||
try {
|
||||
Map<String, dynamic> userData = await FirebaseApi().registerWithFacebook();
|
||||
if (userData != null) {
|
||||
modelUser.email = userData['email'];
|
||||
if (modelUser.email == null) {
|
||||
throw new Exception("Facebook signup was not successful. Please try another method");
|
||||
}
|
||||
modelUser.password = Cache().firebaseUid;
|
||||
modelUser.firebaseUid = Cache().firebaseUid;
|
||||
await CustomerApi().addUser(modelUser);
|
||||
} else {
|
||||
throw new Exception("Facebook signup was not successful. Please try another method");
|
||||
}
|
||||
} on auth.FirebaseAuthException catch (e) {
|
||||
if (e.code == 'email-already-in-use') {
|
||||
log('The account already exists for that email.');
|
||||
throw Exception("The email address has been registered already");
|
||||
}
|
||||
} on WorkoutTestException catch (ex) {
|
||||
if (ex.code == WorkoutTestException.CUSTOMER_EXISTS) {
|
||||
log('The account already exists for that email.');
|
||||
throw Exception("The email address has been registered already");
|
||||
}
|
||||
} on Exception catch (ex) {
|
||||
log("Google exception: " + ex.toString());
|
||||
throw Exception("Google Sign In failed");
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> userData = await FirebaseApi().signInWithFacebook();
|
||||
if (userData != null) {
|
||||
modelUser.email = userData['email'];
|
||||
modelUser.password = "1234567";
|
||||
modelUser.firebaseUid = Cache().facebookUid;
|
||||
await CustomerApi().addUser(modelUser);
|
||||
Future<void> addUserGoogle() async {
|
||||
final User modelUser = this.user;
|
||||
try {
|
||||
Map<String, dynamic> userData = await FirebaseApi().registerWithGoogle();
|
||||
if (userData != null) {
|
||||
modelUser.email = userData['email'];
|
||||
if (modelUser.email == null) {
|
||||
throw new Exception("Google signup was not successful. Please try another method");
|
||||
}
|
||||
modelUser.password = Cache().firebaseUid;
|
||||
modelUser.firebaseUid = Cache().firebaseUid;
|
||||
await CustomerApi().addUser(modelUser);
|
||||
} else {
|
||||
throw new Exception("Google signup was not successful. Please try another method");
|
||||
}
|
||||
} on auth.FirebaseAuthException catch (e) {
|
||||
if (e.code == 'email-already-in-use') {
|
||||
log('The account already exists for that email.');
|
||||
throw Exception("The email address has been registered already");
|
||||
}
|
||||
} on WorkoutTestException catch (ex) {
|
||||
if (ex.code == WorkoutTestException.CUSTOMER_EXISTS) {
|
||||
log('The account already exists for that email.');
|
||||
throw Exception("The email address has been registered already");
|
||||
}
|
||||
} on Exception catch (ex) {
|
||||
log("Google exception: " + ex.toString());
|
||||
throw Exception("Google Sign In failed");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addUserApple() async {
|
||||
final User modelUser = this.user;
|
||||
try {
|
||||
Map<String, dynamic> userData = await FirebaseApi().registerWithApple();
|
||||
if (userData != null) {
|
||||
modelUser.email = userData['email'];
|
||||
if (modelUser.email == null) {
|
||||
throw new Exception("Apple signup was not successful. Please try another method");
|
||||
}
|
||||
modelUser.password = Cache().firebaseUid;
|
||||
modelUser.firebaseUid = Cache().firebaseUid;
|
||||
await CustomerApi().addUser(modelUser);
|
||||
} else {
|
||||
throw new Exception("Apple signup was not successful. Please try another method");
|
||||
}
|
||||
} on auth.FirebaseAuthException catch (e) {
|
||||
if (e.code == 'email-already-in-use') {
|
||||
log('The account already exists for that email.');
|
||||
throw Exception("The email address has been registered already");
|
||||
}
|
||||
} on WorkoutTestException catch (ex) {
|
||||
if (ex.code == WorkoutTestException.CUSTOMER_EXISTS) {
|
||||
log('The account already exists for that email.');
|
||||
throw Exception("The email address has been registered already");
|
||||
}
|
||||
} on Exception catch (ex) {
|
||||
log("Apple exception: " + ex.toString());
|
||||
throw Exception(ex);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getUserByFB() async {
|
||||
final User modelUser = this.user;
|
||||
Map<String, dynamic> userData = await FirebaseApi().signInWithFacebook();
|
||||
try {
|
||||
Map<String, dynamic> userData = await FirebaseApi().signInWithFacebook();
|
||||
if (userData == null) {
|
||||
throw new Exception("Facebook login was not successful");
|
||||
}
|
||||
modelUser.email = userData['email'];
|
||||
|
||||
await CustomerApi().getUserByEmail(modelUser.email);
|
||||
await Cache().afterFirebaseLogin();
|
||||
} on FacebookAuthException catch (e) {
|
||||
switch (e.errorCode) {
|
||||
case FacebookAuthErrorCode.OPERATION_IN_PROGRESS:
|
||||
throw Exception("You have a previous Facebook login operation in progress");
|
||||
break;
|
||||
case FacebookAuthErrorCode.CANCELLED:
|
||||
throw Exception("Facebook login cancelled");
|
||||
break;
|
||||
case FacebookAuthErrorCode.FAILED:
|
||||
throw Exception("Facebook login failed");
|
||||
break;
|
||||
}
|
||||
} on NotFoundException catch (ex) {
|
||||
log("FB exception: " + ex.toString());
|
||||
throw Exception("Customer does not exist or the password is wrong");
|
||||
} on Exception catch (e) {
|
||||
log(e.toString());
|
||||
throw new Exception(e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getUserByGoogle() async {
|
||||
final User modelUser = this.user;
|
||||
Map<String, dynamic> userData = await FirebaseApi().signInWithGoogle();
|
||||
if (userData == null || userData['email'] == null) {
|
||||
throw new Exception("Google login was not successful");
|
||||
}
|
||||
modelUser.email = userData['email'];
|
||||
await CustomerApi().getUserByEmail(modelUser.email);
|
||||
await Cache().afterFacebookLogin();
|
||||
try {
|
||||
await CustomerApi().getUserByEmail(modelUser.email);
|
||||
await Cache().afterFirebaseLogin();
|
||||
} on Exception catch (ex) {
|
||||
log("Google exception: " + ex.toString());
|
||||
throw Exception("Customer does not exist or the password is wrong");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getUserByApple() async {
|
||||
final User modelUser = this.user;
|
||||
Map<String, dynamic> userData = await FirebaseApi().signInWithApple();
|
||||
if (userData == null || userData['email'] == null) {
|
||||
throw new Exception("Apple login was not successful");
|
||||
}
|
||||
modelUser.email = userData['email'];
|
||||
try {
|
||||
await CustomerApi().getUserByEmail(modelUser.email);
|
||||
await Cache().afterFirebaseLogin();
|
||||
} on Exception catch (ex) {
|
||||
log("Apple exception: " + ex.toString());
|
||||
throw Exception("Customer does not exist or the password is wrong");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getUser() async {
|
||||
@@ -64,7 +205,7 @@ class UserRepository {
|
||||
await CustomerApi().getUserByEmail(modelUser.email);
|
||||
await Cache().afterFirebaseLogin();
|
||||
} else {
|
||||
print("Exception: user not found or password is wrong");
|
||||
log("Exception: user not found or password is wrong");
|
||||
throw Exception("Customer does not exist or the password is wrong");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:aitrainer_app/model/user.dart';
|
||||
import 'package:aitrainer_app/service/api.dart';
|
||||
import 'package:aitrainer_app/model/cache.dart';
|
||||
import 'package:aitrainer_app/service/logging.dart';
|
||||
import 'package:aitrainer_app/util/not_found_exception.dart';
|
||||
|
||||
class CustomerApi with Logging {
|
||||
final APIClient _client = new APIClient();
|
||||
@@ -44,12 +45,16 @@ class CustomerApi with Logging {
|
||||
try {
|
||||
int status = jsonDecode(responseBody)['status'];
|
||||
if (status != null) {
|
||||
throw new Exception(jsonDecode(responseBody)['error']);
|
||||
String error = jsonDecode(responseBody)['error'];
|
||||
throw new Exception(error);
|
||||
} else {
|
||||
customer = Customer.fromJson(jsonDecode(responseBody));
|
||||
await Cache().afterRegistration(customer);
|
||||
}
|
||||
} on FormatException {
|
||||
if (responseBody == "Customer exists") {
|
||||
throw WorkoutTestException(code: WorkoutTestException.CUSTOMER_EXISTS, message: responseBody);
|
||||
}
|
||||
throw new Exception(responseBody);
|
||||
}
|
||||
}
|
||||
|
||||
+197
-27
@@ -1,10 +1,13 @@
|
||||
import 'package:aitrainer_app/model/cache.dart';
|
||||
import 'package:aitrainer_app/service/logging.dart';
|
||||
import 'package:aitrainer_app/service/logging.dart' as logging;
|
||||
import 'package:apple_sign_in/apple_sign_in.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
|
||||
import 'package:google_sign_in/google_sign_in.dart';
|
||||
|
||||
class FirebaseApi with Logging {
|
||||
class FirebaseApi with logging.Logging {
|
||||
bool appleSignInAvailable = false;
|
||||
static FirebaseApi _instance;
|
||||
|
||||
static final FirebaseAuth auth = FirebaseAuth.instance;
|
||||
@@ -28,6 +31,7 @@ class FirebaseApi with Logging {
|
||||
try {
|
||||
// Wait for Firebase to initialize and set `_initialized` state to true
|
||||
await Firebase.initializeApp();
|
||||
this.appleSignInAvailable = await AppleSignIn.isAvailable();
|
||||
} catch (e) {
|
||||
// Set `_error` state to true if Firebase initialization fails
|
||||
log("Error initializing Firebase");
|
||||
@@ -81,34 +85,200 @@ class FirebaseApi with Logging {
|
||||
return rc;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> signInWithApple() async {
|
||||
Map<String, dynamic> userData = Map();
|
||||
|
||||
final AuthorizationResult result = await AppleSignIn.performRequests([
|
||||
AppleIdRequest(requestedScopes: [Scope.email, Scope.fullName])
|
||||
]);
|
||||
switch (result.status) {
|
||||
case AuthorizationStatus.authorized:
|
||||
print('User authorized');
|
||||
break;
|
||||
case AuthorizationStatus.error:
|
||||
print('User error');
|
||||
throw Exception("Apple Sign-In failed");
|
||||
break;
|
||||
case AuthorizationStatus.cancelled:
|
||||
print('User cancelled');
|
||||
throw Exception("Apple Sign-In cancelled");
|
||||
break;
|
||||
}
|
||||
|
||||
// Create an `OAuthCredential` from the credential returned by Apple.
|
||||
final oauthCredential = OAuthProvider("apple.com").credential(
|
||||
idToken: String.fromCharCodes(result.credential.identityToken),
|
||||
accessToken: String.fromCharCodes(result.credential.authorizationCode));
|
||||
|
||||
// Sign in the user with Firebase. If the nonce we generated earlier does
|
||||
// not match the nonce in `appleCredential.identityToken`, sign in will fail.
|
||||
UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(oauthCredential);
|
||||
|
||||
log("userCredential: " + userCredential.toString());
|
||||
|
||||
log("Apple Credentials: " +
|
||||
result.credential.user.toString() +
|
||||
" state " +
|
||||
result.credential.state.toString() +
|
||||
" email " +
|
||||
userCredential.user.email);
|
||||
userData['email'] = userCredential.user.email;
|
||||
|
||||
return userData;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> registerWithApple() async {
|
||||
Map<String, dynamic> userData = Map();
|
||||
final AuthorizationResult result = await AppleSignIn.performRequests([
|
||||
AppleIdRequest(requestedScopes: [Scope.email, Scope.fullName])
|
||||
]);
|
||||
switch (result.status) {
|
||||
case AuthorizationStatus.authorized:
|
||||
print('Apple User authorized');
|
||||
break;
|
||||
case AuthorizationStatus.error:
|
||||
print('Apple User error');
|
||||
throw Exception("Apple Sign-In failed");
|
||||
break;
|
||||
case AuthorizationStatus.cancelled:
|
||||
print('User cancelled');
|
||||
throw Exception("Apple Sign-In cancelled");
|
||||
break;
|
||||
}
|
||||
|
||||
// Create an `OAuthCredential` from the credential returned by Apple.
|
||||
final oauthCredential = OAuthProvider("apple.com").credential(
|
||||
idToken: String.fromCharCodes(result.credential.identityToken),
|
||||
accessToken: String.fromCharCodes(result.credential.authorizationCode));
|
||||
|
||||
// Sign in the user with Firebase. If the nonce we generated earlier does
|
||||
// not match the nonce in `appleCredential.identityToken`, sign in will fail.
|
||||
UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(oauthCredential);
|
||||
|
||||
Cache().firebaseUid = userCredential.user.uid;
|
||||
|
||||
userData['email'] = userCredential.user.email;
|
||||
|
||||
return userData;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> signInWithGoogle() async {
|
||||
Map<String, dynamic> userData = Map();
|
||||
|
||||
try {
|
||||
// Trigger the authentication flow
|
||||
GoogleSignIn _googleSignIn = GoogleSignIn(
|
||||
scopes: [
|
||||
'email',
|
||||
'https://www.googleapis.com/auth/contacts.readonly',
|
||||
],
|
||||
);
|
||||
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
|
||||
|
||||
if (googleUser == null) {
|
||||
throw Exception("Google Sign In failed");
|
||||
}
|
||||
|
||||
// Obtain the auth details from the request
|
||||
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
|
||||
|
||||
// Create a new credential
|
||||
final GoogleAuthCredential credential = GoogleAuthProvider.credential(
|
||||
accessToken: googleAuth.accessToken,
|
||||
idToken: googleAuth.idToken,
|
||||
);
|
||||
|
||||
await FirebaseAuth.instance.signInWithCredential(credential);
|
||||
|
||||
if (googleUser != null) {
|
||||
log("GoogleUser: " + googleUser.toString());
|
||||
userData['email'] = googleUser.email;
|
||||
userData['id'] = googleUser.id;
|
||||
userData['name'] = googleUser.displayName;
|
||||
}
|
||||
} on Exception catch (ex) {
|
||||
log("Google exception: " + ex.toString());
|
||||
throw Exception("Google Sign In failed");
|
||||
}
|
||||
return userData;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> registerWithGoogle() async {
|
||||
Map<String, dynamic> userData = Map();
|
||||
|
||||
// Trigger the authentication flow
|
||||
GoogleSignIn _googleSignIn = GoogleSignIn(
|
||||
scopes: [
|
||||
'email',
|
||||
'https://www.googleapis.com/auth/contacts.readonly',
|
||||
],
|
||||
);
|
||||
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
|
||||
|
||||
if (googleUser == null) {
|
||||
throw Exception("Google Sign In failed");
|
||||
}
|
||||
|
||||
// Obtain the auth details from the request
|
||||
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
|
||||
|
||||
// Create a new credential
|
||||
final GoogleAuthCredential credential = GoogleAuthProvider.credential(
|
||||
accessToken: googleAuth.accessToken,
|
||||
idToken: googleAuth.idToken,
|
||||
);
|
||||
|
||||
final userCredential = await FirebaseAuth.instance.signInWithCredential(credential);
|
||||
|
||||
log("Google credentials: " + credential.toString() + " GoogleUser: " + googleUser.toString());
|
||||
Cache().firebaseUid = userCredential.user.uid;
|
||||
|
||||
userData['email'] = googleUser.email;
|
||||
|
||||
return userData;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> signInWithFacebook() async {
|
||||
Map<String, dynamic> userData;
|
||||
try {
|
||||
// by default the login method has the next permissions ['email','public_profile']
|
||||
AccessToken accessToken = await FacebookAuth.instance.login();
|
||||
if (accessToken != null) {
|
||||
log(accessToken.toJson().toString());
|
||||
Cache().accessTokenFacebook = accessToken;
|
||||
// get the user data
|
||||
userData = await FacebookAuth.instance.getUserData();
|
||||
Cache().facebookUid = userData['id'];
|
||||
log(userData.toString());
|
||||
} else {
|
||||
throw Exception("Facebook login was not successful");
|
||||
}
|
||||
} on FacebookAuthException catch (e) {
|
||||
switch (e.errorCode) {
|
||||
case FacebookAuthErrorCode.OPERATION_IN_PROGRESS:
|
||||
throw Exception("You have a previous Facebook login operation in progress");
|
||||
break;
|
||||
case FacebookAuthErrorCode.CANCELLED:
|
||||
throw Exception("Facebook login cancelled");
|
||||
break;
|
||||
case FacebookAuthErrorCode.FAILED:
|
||||
throw Exception("Facebook login failed");
|
||||
break;
|
||||
}
|
||||
|
||||
// by default the login method has the next permissions ['email','public_profile']
|
||||
AccessToken accessToken = await FacebookAuth.instance.login();
|
||||
if (accessToken != null) {
|
||||
log(accessToken.toJson().toString());
|
||||
Cache().accessTokenFacebook = accessToken;
|
||||
// get the user data
|
||||
userData = await FacebookAuth.instance.getUserData();
|
||||
Cache().firebaseUid = userData['id'];
|
||||
log(userData.toString());
|
||||
} else {
|
||||
throw Exception("Facebook login was not successful");
|
||||
}
|
||||
|
||||
return userData;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> registerWithFacebook() async {
|
||||
Map<String, dynamic> userData;
|
||||
|
||||
// by default the login method has the next permissions ['email','public_profile']
|
||||
AccessToken accessToken = await FacebookAuth.instance.login();
|
||||
if (accessToken != null) {
|
||||
Cache().accessTokenFacebook = accessToken;
|
||||
// get the user data
|
||||
userData = await FacebookAuth.instance.getUserData();
|
||||
|
||||
// Create a credential from the access token
|
||||
final FacebookAuthCredential facebookAuthCredential = FacebookAuthProvider.credential(accessToken.token);
|
||||
|
||||
// Once signed in, return the UserCredential
|
||||
final userCredential = await FirebaseAuth.instance.signInWithCredential(facebookAuthCredential);
|
||||
|
||||
Cache().firebaseUid = userCredential.user.uid;
|
||||
log(userData.toString());
|
||||
} else {
|
||||
throw Exception("Facebook login was not successful");
|
||||
}
|
||||
|
||||
return userData;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
class NotFoundException implements Exception {
|
||||
final String message;
|
||||
const NotFoundException({this.message});
|
||||
}
|
||||
}
|
||||
|
||||
class WorkoutTestException implements Exception {
|
||||
static const String CUSTOMER_EXISTS = "customer-exists";
|
||||
static const String LOGIN_CANCELLED = "login-cancelled";
|
||||
|
||||
final String message;
|
||||
final String code;
|
||||
const WorkoutTestException({this.message, this.code});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
/*
|
||||
import 'package:aitrainer_app/model/cache.dart';
|
||||
import 'package:aitrainer_app/model/product.dart';
|
||||
import 'package:aitrainer_app/service/logging.dart';
|
||||
@@ -21,15 +21,7 @@ class PlatformPurchaseApi with Logging {
|
||||
|
||||
List getProductList() => _productList;
|
||||
|
||||
/* Platform.isAndroid
|
||||
? [
|
||||
'android.test.purchased',
|
||||
'point_1000',
|
||||
'5000_point',
|
||||
'android.test.canceled',
|
||||
]
|
||||
: ['com.cooni.point1000', 'com.cooni.point5000'];
|
||||
*/
|
||||
|
||||
|
||||
factory PlatformPurchaseApi() {
|
||||
return _singleton;
|
||||
@@ -167,3 +159,4 @@ class PlatformPurchaseApi with Logging {
|
||||
this._purchases = items;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:modal_progress_hud/modal_progress_hud.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class CustomerExerciseDevicePage extends StatelessWidget with Trans {
|
||||
@@ -37,19 +38,20 @@ class CustomerExerciseDevicePage extends StatelessWidget with Trans {
|
||||
..add(CustomerExerciseDeviceLoad()),
|
||||
child: BlocConsumer<CustomerExerciseDeviceBloc, CustomerExerciseDeviceState>(
|
||||
listener: (context, state) {
|
||||
if (state is CustomerExerciseDeviceLoading) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
duration: Duration(milliseconds: 100),
|
||||
backgroundColor: Colors.transparent,
|
||||
content: Container(child: Center(child: CircularProgressIndicator()))));
|
||||
} else if (state is CustomerExerciseDeviceError) {
|
||||
if (state is CustomerExerciseDeviceError) {
|
||||
Scaffold.of(context).showSnackBar(
|
||||
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
final bloc = BlocProvider.of<CustomerExerciseDeviceBloc>(context);
|
||||
return getPage(bloc, cWidth, cHeight);
|
||||
return ModalProgressHUD(
|
||||
child: getPage(bloc, cWidth, cHeight),
|
||||
inAsyncCall: state is CustomerExerciseDeviceLoading,
|
||||
opacity: 0.5,
|
||||
color: Colors.black54,
|
||||
progressIndicator: CircularProgressIndicator(),
|
||||
);
|
||||
},
|
||||
))));
|
||||
}
|
||||
|
||||
+33
-20
@@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:aitrainer_app/bloc/account/account_bloc.dart';
|
||||
import 'package:aitrainer_app/bloc/login/login_bloc.dart';
|
||||
import 'package:aitrainer_app/localization/app_localization.dart';
|
||||
@@ -68,29 +70,40 @@ class LoginPage extends StatelessWidget with Trans {
|
||||
return Form(
|
||||
key: _scaffoldKey,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(left: 15, right: 50),
|
||||
child: ListView(shrinkWrap: false, padding: EdgeInsets.only(top: 100.0), children: <Widget>[
|
||||
FlatButton(
|
||||
child: new Image.asset(
|
||||
'asset/image/login_fb.png',
|
||||
width: MediaQuery.of(context).size.width * .85,
|
||||
),
|
||||
onPressed: () => {loginBloc.add(LoginFB())},
|
||||
),
|
||||
Text(AppLocalizations.of(context).translate("OR")),
|
||||
Divider(),
|
||||
padding: const EdgeInsets.only(left: 20, right: 20),
|
||||
child: ListView(shrinkWrap: false, padding: EdgeInsets.only(top: 150.0), children: <Widget>[
|
||||
ListTile(title: Text(t("Login"), style: GoogleFonts.inter())),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
new InkWell(
|
||||
child: new Text(AppLocalizations.of(context).translate('Login'),
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24)),
|
||||
FlatButton(
|
||||
child: Image.asset(
|
||||
'asset/image/button_fb.png',
|
||||
width: 60,
|
||||
),
|
||||
onPressed: () => {loginBloc.add(LoginFB())},
|
||||
),
|
||||
FlatButton(
|
||||
child: Image.asset(
|
||||
'asset/image/button_google.png',
|
||||
width: 60,
|
||||
),
|
||||
onPressed: () => {loginBloc.add(LoginGoogle())},
|
||||
),
|
||||
Platform.isIOS
|
||||
? FlatButton(
|
||||
child: Image.asset(
|
||||
'asset/image/button_apple.png',
|
||||
width: 60,
|
||||
),
|
||||
onPressed: () => {loginBloc.add(LoginApple())},
|
||||
)
|
||||
: Offstage(),
|
||||
],
|
||||
),
|
||||
Divider(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
Divider(),
|
||||
ListTile(title: Text(t("OR"), style: GoogleFonts.inter())),
|
||||
Divider(),
|
||||
TextFormField(
|
||||
key: LibraryKeys.loginEmailField,
|
||||
decoration: InputDecoration(
|
||||
@@ -118,7 +131,7 @@ class LoginPage extends StatelessWidget with Trans {
|
||||
),
|
||||
TextFormField(
|
||||
key: LibraryKeys.loginPasswordField,
|
||||
obscureText: true,
|
||||
obscureText: loginBloc.obscure,
|
||||
decoration: InputDecoration(
|
||||
labelStyle: TextStyle(fontSize: 14),
|
||||
contentPadding: EdgeInsets.only(left: 15, top: 15, bottom: 15),
|
||||
@@ -168,7 +181,7 @@ class LoginPage extends StatelessWidget with Trans {
|
||||
),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[
|
||||
InkWell(
|
||||
child: Text(AppLocalizations.of(context).translate('SignUp')),
|
||||
child: Text(AppLocalizations.of(context).translate('SignUpLink')),
|
||||
onTap: () => Navigator.of(context).pushNamed('registration'),
|
||||
),
|
||||
Spacer(flex: 2),
|
||||
|
||||
+51
-33
@@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:aitrainer_app/bloc/account/account_bloc.dart';
|
||||
import 'package:aitrainer_app/bloc/login/login_bloc.dart';
|
||||
import 'package:aitrainer_app/localization/app_localization.dart';
|
||||
@@ -11,7 +13,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:modal_progress_hud/modal_progress_hud.dart';
|
||||
import 'package:toggle_switch/toggle_switch.dart';
|
||||
|
||||
import '../library_keys.dart';
|
||||
|
||||
@@ -74,28 +75,48 @@ class RegistrationPage extends StatelessWidget with Trans {
|
||||
return Form(
|
||||
key: _scaffoldKey,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(left: 15, right: 50),
|
||||
padding: const EdgeInsets.only(left: 20, right: 20),
|
||||
child: ListView(shrinkWrap: false, padding: EdgeInsets.only(top: 150.0), children: <Widget>[
|
||||
FlatButton(
|
||||
child: Image.asset(
|
||||
'asset/image/fb_registration.png',
|
||||
width: MediaQuery.of(context).size.width * .85,
|
||||
),
|
||||
onPressed: () => {loginBloc.add(RegistrationFB())},
|
||||
),
|
||||
ListTile(title: Text(AppLocalizations.of(context).translate("OR"))),
|
||||
ListTile(title: Text(t("SignUp"), style: GoogleFonts.inter())),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FlatButton(
|
||||
child: Image.asset(
|
||||
'asset/image/button_fb.png',
|
||||
width: 60,
|
||||
),
|
||||
onPressed: () => {loginBloc.add(RegistrationFB())},
|
||||
),
|
||||
FlatButton(
|
||||
child: Image.asset(
|
||||
'asset/image/button_google.png',
|
||||
width: 60,
|
||||
),
|
||||
onPressed: () => {loginBloc.add(RegistrationGoogle())},
|
||||
),
|
||||
Platform.isIOS
|
||||
? FlatButton(
|
||||
child: Image.asset(
|
||||
'asset/image/button_apple.png',
|
||||
width: 60,
|
||||
),
|
||||
onPressed: () => {loginBloc.add(RegistrationApple())},
|
||||
)
|
||||
: Offstage(),
|
||||
],
|
||||
),
|
||||
ListTile(title: Text(t("OR"), style: GoogleFonts.inter())),
|
||||
/* Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
InkWell(
|
||||
child:
|
||||
Text(AppLocalizations.of(context).translate('SignUp'), style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24)),
|
||||
child: Text(AppLocalizations.of(context).translate('SignUp with Email'),
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24)),
|
||||
),
|
||||
],
|
||||
),
|
||||
Divider(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
), */
|
||||
|
||||
TextFormField(
|
||||
key: LibraryKeys.loginEmailField,
|
||||
decoration: InputDecoration(
|
||||
@@ -123,7 +144,7 @@ class RegistrationPage extends StatelessWidget with Trans {
|
||||
),
|
||||
TextFormField(
|
||||
key: LibraryKeys.loginPasswordField,
|
||||
obscureText: true,
|
||||
obscureText: loginBloc.obscure,
|
||||
decoration: InputDecoration(
|
||||
labelStyle: TextStyle(fontSize: 14),
|
||||
contentPadding: EdgeInsets.only(left: 15, top: 15, bottom: 15),
|
||||
@@ -153,6 +174,9 @@ class RegistrationPage extends StatelessWidget with Trans {
|
||||
color: Colors.transparent,
|
||||
),
|
||||
getDataProtection(loginBloc),
|
||||
Divider(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[
|
||||
FlatButton(
|
||||
key: LibraryKeys.loginOKButton,
|
||||
@@ -196,22 +220,16 @@ class RegistrationPage extends StatelessWidget with Trans {
|
||||
}
|
||||
|
||||
Widget getDataProtection(LoginBloc loginBloc) {
|
||||
return ListTile(
|
||||
subtitle: Text(t("Please accept our data protection policy. For more information please click on 'Privacy'")),
|
||||
title: ToggleSwitch(
|
||||
minWidth: 100.0,
|
||||
minHeight: 30.0,
|
||||
fontSize: 14.0,
|
||||
initialLabelIndex: loginBloc.dataPolicyAllowed ? 0 : 1,
|
||||
activeBgColor: Colors.indigo,
|
||||
activeFgColor: Colors.white,
|
||||
inactiveBgColor: Colors.white30,
|
||||
inactiveFgColor: Colors.black,
|
||||
labels: [t('Yes'), t('No')],
|
||||
onToggle: (index) {
|
||||
loginBloc.add(DataProtectionClicked(marked: index == 0));
|
||||
},
|
||||
),
|
||||
return CheckboxListTile(
|
||||
title: Text(t("Please accept our data protection policy.")),
|
||||
subtitle: Text(t("For more information please click on 'Privacy'")),
|
||||
dense: true,
|
||||
value: loginBloc.dataPolicyAllowed,
|
||||
activeColor: Colors.indigo,
|
||||
onChanged: (value) {
|
||||
loginBloc.add(DataProtectionClicked(marked: value));
|
||||
},
|
||||
controlAffinity: ListTileControlAffinity.leading, // <-- leading Checkbox
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class DialogPremium extends StatefulWidget {
|
||||
String description, function, unlockedText;
|
||||
final int unlockRound;
|
||||
|
||||
final bool unlocked;
|
||||
bool unlocked;
|
||||
|
||||
DialogPremium(
|
||||
{Key key,
|
||||
@@ -27,6 +27,8 @@ class DialogPremium extends StatefulWidget {
|
||||
description = description ?? "";
|
||||
function = function ?? "";
|
||||
unlockedText = unlockedText ?? "";
|
||||
|
||||
unlocked = true;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -88,9 +90,9 @@ class _DialogPremiumState extends State<DialogPremium> with Trans {
|
||||
alignment: AlignmentDirectional.topEnd,
|
||||
children: [
|
||||
Text(
|
||||
t("Go Premium") + " ",
|
||||
widget.unlocked ? t("Keep testing") : t("Go Premium") + " ",
|
||||
style: GoogleFonts.archivoBlack(
|
||||
fontSize: 24,
|
||||
fontSize: widget.unlocked ? 18 : 24,
|
||||
color: Colors.yellow[400],
|
||||
shadows: <Shadow>[
|
||||
Shadow(
|
||||
@@ -106,32 +108,34 @@ class _DialogPremiumState extends State<DialogPremium> with Trans {
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 3,
|
||||
top: 0,
|
||||
child: AnimatedSwitcher(
|
||||
duration: Duration(milliseconds: 900),
|
||||
//reverseDuration: Duration(milliseconds: 200),
|
||||
transitionBuilder: (Widget child, Animation<double> animation) {
|
||||
return FadeTransition(child: child, opacity: animation);
|
||||
},
|
||||
child: isStart
|
||||
? Icon(
|
||||
CustomIcon.star_2,
|
||||
color: Colors.yellow[300],
|
||||
)
|
||||
: Offstage() /* Icon(
|
||||
!widget.unlocked
|
||||
? Positioned(
|
||||
right: 3,
|
||||
top: 0,
|
||||
child: AnimatedSwitcher(
|
||||
duration: Duration(milliseconds: 900),
|
||||
//reverseDuration: Duration(milliseconds: 200),
|
||||
transitionBuilder: (Widget child, Animation<double> animation) {
|
||||
return FadeTransition(child: child, opacity: animation);
|
||||
},
|
||||
child: isStart
|
||||
? Icon(
|
||||
CustomIcon.star_2,
|
||||
color: Colors.yellow[300],
|
||||
)
|
||||
: Offstage() /* Icon(
|
||||
CustomIcon.exclamation_circle,
|
||||
color: Colors.yellow[300],
|
||||
) */
|
||||
)),
|
||||
))
|
||||
: Offstage(),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 35,
|
||||
),
|
||||
Text(
|
||||
t("Unleash your potential with WorkoutTest Premium!"),
|
||||
widget.unlocked ? "" : t("Unleash your potential with WorkoutTest Premium!"),
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
color: Colors.white,
|
||||
@@ -160,7 +164,7 @@ class _DialogPremiumState extends State<DialogPremium> with Trans {
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: GestureDetector(
|
||||
onTap: () => Navigator.of(context).pushNamed("salesPage"),
|
||||
onTap: () => widget.unlocked ? Navigator.of(context).pop() : Navigator.of(context).pushNamed("salesPage"),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
@@ -222,61 +226,60 @@ class _DialogPremiumState extends State<DialogPremium> with Trans {
|
||||
List<TextSpan> getDescriptionText() {
|
||||
List<TextSpan> list = List();
|
||||
|
||||
if (widget.unlocked) {
|
||||
/* if (widget.unlocked) {
|
||||
list.add(TextSpan(text: widget.unlockedText));
|
||||
} else {
|
||||
list.add(TextSpan(text: t("The")));
|
||||
list.add(TextSpan(text: t(" ")));
|
||||
list.add(
|
||||
TextSpan(
|
||||
text: t(widget.function),
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.yellow[300],
|
||||
shadows: <Shadow>[
|
||||
Shadow(
|
||||
offset: Offset(5.0, 5.0),
|
||||
blurRadius: 12.0,
|
||||
color: Colors.black54,
|
||||
),
|
||||
Shadow(
|
||||
offset: Offset(-3.0, 3.0),
|
||||
blurRadius: 12.0,
|
||||
color: Colors.black54,
|
||||
),
|
||||
],
|
||||
),
|
||||
} */
|
||||
list.add(TextSpan(text: t("The")));
|
||||
list.add(TextSpan(text: t(" ")));
|
||||
list.add(
|
||||
TextSpan(
|
||||
text: t(widget.function),
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.yellow[300],
|
||||
shadows: <Shadow>[
|
||||
Shadow(
|
||||
offset: Offset(5.0, 5.0),
|
||||
blurRadius: 12.0,
|
||||
color: Colors.black54,
|
||||
),
|
||||
Shadow(
|
||||
offset: Offset(-3.0, 3.0),
|
||||
blurRadius: 12.0,
|
||||
color: Colors.black54,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
list.add(TextSpan(text: t(" ")));
|
||||
list.add(TextSpan(text: t("feature is reachable after you finished")));
|
||||
list.add(TextSpan(text: t(" ")));
|
||||
list.add(
|
||||
TextSpan(
|
||||
text: widget.unlockRound == 1 ? t("the first") : t("the second"),
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.yellow[300],
|
||||
shadows: <Shadow>[
|
||||
Shadow(
|
||||
offset: Offset(5.0, 5.0),
|
||||
blurRadius: 12.0,
|
||||
color: Colors.black54,
|
||||
),
|
||||
Shadow(
|
||||
offset: Offset(-3.0, 3.0),
|
||||
blurRadius: 12.0,
|
||||
color: Colors.black54,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
list.add(TextSpan(text: t(" ")));
|
||||
list.add(TextSpan(text: t("feature is reachable after you finished")));
|
||||
list.add(TextSpan(text: t(" ")));
|
||||
list.add(
|
||||
TextSpan(
|
||||
text: widget.unlockRound == 1 ? t("the first") : t("the second"),
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.yellow[300],
|
||||
shadows: <Shadow>[
|
||||
Shadow(
|
||||
offset: Offset(5.0, 5.0),
|
||||
blurRadius: 12.0,
|
||||
color: Colors.black54,
|
||||
),
|
||||
Shadow(
|
||||
offset: Offset(-3.0, 3.0),
|
||||
blurRadius: 12.0,
|
||||
color: Colors.black54,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
list.add(TextSpan(text: t(" ")));
|
||||
list.add(TextSpan(text: t("100% test circles")));
|
||||
}
|
||||
),
|
||||
);
|
||||
list.add(TextSpan(text: t(" ")));
|
||||
list.add(TextSpan(text: t("100% test circles")));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -91,6 +91,6 @@ class _HomePageState extends State<AitrainerHome> with Logging {
|
||||
@override
|
||||
void dispose() async {
|
||||
super.dispose();
|
||||
await PlatformPurchaseApi().close();
|
||||
//await PlatformPurchaseApi().close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class ImageButton extends StatelessWidget {
|
||||
//print("Top: " + top.toStringAsFixed(0) + " length: " + ((style.fontSize - 5) * text.length).toString());
|
||||
}
|
||||
final double width = MediaQuery.of(context).size.width;
|
||||
print("Mediawidth: " + width.toStringAsFixed(0));
|
||||
//print("Mediawidth: " + width.toStringAsFixed(0));
|
||||
return Stack(alignment: AlignmentDirectional.bottomStart, children: [
|
||||
FlatButton(
|
||||
child: image == null
|
||||
@@ -152,11 +152,11 @@ class ImageButton extends StatelessWidget {
|
||||
imageName,
|
||||
fit: BoxFit.fitWidth,
|
||||
alignment: Alignment.center,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
String url = Cache.mediaUrl + '/' + imageName; //.substring(11);
|
||||
/* errorBuilder: (context, error, stackTrace) {
|
||||
String url = Cache.mediaUrl + imageName; //.substring(11);
|
||||
Widget image = FadeInImage.assetNetwork(placeholder: 'asset/image/dots.gif', image: url, height: this.height);
|
||||
return image;
|
||||
},
|
||||
}, */
|
||||
);
|
||||
} on Exception catch (_) {
|
||||
String url = Cache.mediaUrl + '/images/' + imageName;
|
||||
|
||||
Reference in New Issue
Block a user