WT1.1.4 build 45 android fb, iap
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:aitrainer_app/model/cache.dart';
|
||||
@@ -6,6 +7,7 @@ import 'package:aitrainer_app/model/product.dart';
|
||||
import 'package:aitrainer_app/model/product_test.dart';
|
||||
import 'package:aitrainer_app/service/logging.dart';
|
||||
import 'package:aitrainer_app/service/product_test_service.dart';
|
||||
import 'package:aitrainer_app/util/platform_purchase.dart';
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flurry/flurry.dart';
|
||||
@@ -26,20 +28,46 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
|
||||
try {
|
||||
if (event is SalesLoad) {
|
||||
yield SalesLoading();
|
||||
Flurry.logEvent("SalesPageOpen");
|
||||
//Flurry.logEvent("SalesPageOpen");
|
||||
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().requestPurchase(null);
|
||||
//Flurry.logEvent("PurchaseRequest");
|
||||
PlatformPurchaseApi().purchase(getSelectedProduct(productId));
|
||||
}
|
||||
} on Exception catch (ex) {
|
||||
yield SalesError(message: ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Product getSelectedProduct(int productId) {
|
||||
Product prod;
|
||||
for (var product in this.product2Display) {
|
||||
if (product.productId == productId) {
|
||||
prod = product;
|
||||
}
|
||||
}
|
||||
return prod;
|
||||
}
|
||||
|
||||
String getLocalizedPrice(String productId) {
|
||||
String price = "";
|
||||
for (var product in PlatformPurchaseApi().getIAPItems()) {
|
||||
if (Platform.isAndroid) {
|
||||
print("PlatformProduct " + product.toString());
|
||||
if (productId == product.productId) {
|
||||
price = product.localizedPrice;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return price;
|
||||
}
|
||||
|
||||
void getProductSet() {
|
||||
int productId = 0;
|
||||
this.tests = Cache().productTests;
|
||||
@@ -51,7 +79,7 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
|
||||
trace("Previous ProductTest: " + tests[0].toJson().toString());
|
||||
productId = tests[0].productId;
|
||||
for (var elem in Cache().products) {
|
||||
Product product = elem as Product;
|
||||
final Product product = elem as Product;
|
||||
if (product.productId == productId) {
|
||||
productSet = product.productSet;
|
||||
break;
|
||||
@@ -63,8 +91,11 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
|
||||
trace("ProductSet: " + productSet.toString());
|
||||
for (var elem in Cache().products) {
|
||||
Product product = elem as Product;
|
||||
|
||||
if (product.productSet == productSet) {
|
||||
productId = product.productId;
|
||||
final String platformProductId = Platform.isAndroid ? product.productIdAndroid : product.productIdIos;
|
||||
product.localizedPrice = getLocalizedPrice(platformProductId);
|
||||
product2Display.add(product);
|
||||
}
|
||||
}
|
||||
@@ -76,7 +107,7 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
|
||||
productTest.productId = productId;
|
||||
productTest.customerId = Cache().userLoggedIn.customerId;
|
||||
productTest.dateView = DateTime.now();
|
||||
ProductTestApi().saveProductTest(productTest);
|
||||
Cache().productTests.add(productTest);
|
||||
//ProductTestApi().saveProductTest(productTest);
|
||||
//Cache().productTests.add(productTest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ class SessionBloc extends Bloc<SessionEvent, SessionState> with Logging {
|
||||
// ignore: close_sinks
|
||||
SettingsBloc settingsBloc = event.settingsBloc;
|
||||
await session.fetchSessionAndNavigate();
|
||||
await PlatformPurchaseApi().initPurchasePlatformState();
|
||||
String lang = AppLanguage().appLocal.languageCode;
|
||||
log("Change lang to $lang");
|
||||
settingsBloc.add(SettingsChangeLanguage(language: lang));
|
||||
|
||||
@@ -105,7 +105,7 @@ class Cache with Logging {
|
||||
LinkedHashMap<String, int> _badges = LinkedHashMap();
|
||||
|
||||
List deviceLanguages;
|
||||
String startPage;
|
||||
String startPage = "home";
|
||||
String testEnvironment;
|
||||
bool liveServer = true;
|
||||
bool hasHardware = false;
|
||||
|
||||
@@ -12,6 +12,7 @@ class Product {
|
||||
String productIdAndroid;
|
||||
double priceIos;
|
||||
double priceAndroid;
|
||||
String localizedPrice;
|
||||
|
||||
Product.fromJson(Map json) {
|
||||
this.productId = json['productId'];
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:aitrainer_app/service/exercise_tree_service.dart';
|
||||
import 'package:aitrainer_app/service/exercisetype_service.dart';
|
||||
import 'package:aitrainer_app/service/logging.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class Antagonist {
|
||||
static String chest = "Chest";
|
||||
@@ -47,6 +48,19 @@ class WorkoutTreeRepository with Logging {
|
||||
Antagonist.calf: Antagonist.calfNr
|
||||
};
|
||||
|
||||
Future<String> _buildImage(String imageUrl) async {
|
||||
String assetImage = 'asset/menu/' + imageUrl.substring(7);
|
||||
//print("Loading image " + assetImage);
|
||||
return rootBundle.load(assetImage).then((value) {
|
||||
return assetImage;
|
||||
}).catchError((_) {
|
||||
String imagePath = assetImage.substring(10);
|
||||
String url = Cache.mediaUrl + 'images' + imagePath;
|
||||
//print("Exception: " + assetImage + " will be loaded from the network " + url);
|
||||
return url;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> createTree() async {
|
||||
isEnglish = AppLanguage().appLocal == Locale('en');
|
||||
log("** Start creating tree on lang: " + AppLanguage().appLocal.languageCode);
|
||||
@@ -65,7 +79,7 @@ class WorkoutTreeRepository with Logging {
|
||||
//log(" -- TreeItem " + treeItem.toJson().toString() + " active " + treeItem.active.toString());
|
||||
if (treeItem.active == true) {
|
||||
String treeName = isEnglish ? treeItem.name : treeItem.nameTranslation;
|
||||
String assetImage = 'asset/menu/' + treeItem.imageUrl.substring(7);
|
||||
//String assetImage = await _buildImage(treeItem.imageUrl);
|
||||
|
||||
bool is1RM = treeItem.name == 'One Rep Max' ? true : false;
|
||||
if (is1RM == false && treeItem.parentId != 0) {
|
||||
@@ -86,7 +100,7 @@ class WorkoutTreeRepository with Logging {
|
||||
treeItem.treeId,
|
||||
treeItem.parentId,
|
||||
treeName,
|
||||
assetImage,
|
||||
treeItem.imageUrl,
|
||||
Colors.white,
|
||||
30,
|
||||
false,
|
||||
@@ -104,11 +118,11 @@ class WorkoutTreeRepository with Logging {
|
||||
}
|
||||
});
|
||||
|
||||
exerciseTypes.forEach((exerciseType) {
|
||||
exerciseTypes.forEach((exerciseType) async {
|
||||
if (!(exerciseType.imageUrl.isEmpty || exerciseType.name.isEmpty || exerciseType.nameTranslation.isEmpty) &&
|
||||
exerciseType.active == true) {
|
||||
String exerciseTypeName = isEnglish ? exerciseType.name : exerciseType.nameTranslation;
|
||||
String assetImage = 'asset/menu/' + exerciseType.imageUrl.substring(7);
|
||||
//String assetImage = await _buildImage(exerciseType.imageUrl); //'asset/menu/' + exerciseType.imageUrl.substring(7);
|
||||
if (exerciseType.parents.isNotEmpty) {
|
||||
exerciseType.parents.forEach((parentId) {
|
||||
bool is1RM = this.isParent1RM(parentId);
|
||||
@@ -117,8 +131,21 @@ class WorkoutTreeRepository with Logging {
|
||||
if (isEndurance) exerciseType.setAbility(ExerciseAbility.endurance);
|
||||
bool isRunning = this.isParentRunning(parentId);
|
||||
if (isRunning) exerciseType.setAbility(ExerciseAbility.running);
|
||||
WorkoutMenuTree menuItem = WorkoutMenuTree(exerciseType.exerciseTypeId, parentId, exerciseTypeName, assetImage, Colors.white,
|
||||
24, true, exerciseType.exerciseTypeId, exerciseType, exerciseType.base, is1RM, isEndurance, isRunning, exerciseType.name);
|
||||
WorkoutMenuTree menuItem = WorkoutMenuTree(
|
||||
exerciseType.exerciseTypeId,
|
||||
parentId,
|
||||
exerciseTypeName,
|
||||
exerciseType.imageUrl,
|
||||
Colors.white,
|
||||
24,
|
||||
true,
|
||||
exerciseType.exerciseTypeId,
|
||||
exerciseType,
|
||||
exerciseType.base,
|
||||
is1RM,
|
||||
isEndurance,
|
||||
isRunning,
|
||||
exerciseType.name);
|
||||
this.tree[exerciseType.name] = menuItem;
|
||||
//log("WorkoutMenuTree item " + menuItem.toJson().toString());
|
||||
/* log("ExerciseType in Menu item " +
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:convert';
|
||||
import 'package:aitrainer_app/model/cache.dart';
|
||||
import 'package:aitrainer_app/model/exercise_tree.dart';
|
||||
import 'package:aitrainer_app/model/exercise_tree_parents.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'api.dart';
|
||||
|
||||
class ExerciseTreeApi {
|
||||
@@ -15,10 +16,28 @@ class ExerciseTreeApi {
|
||||
|
||||
exerciseTree = await getExerciseTreeParents(exerciseTree);
|
||||
|
||||
if (exerciseTree != null) {
|
||||
exerciseTree.forEach((element) async {
|
||||
element.imageUrl = await _buildImage(element.imageUrl);
|
||||
});
|
||||
}
|
||||
Cache().setExerciseTree(exerciseTree);
|
||||
return exerciseTree;
|
||||
}
|
||||
|
||||
Future<String> _buildImage(String imageUrl) async {
|
||||
String assetImage = 'asset/menu/' + imageUrl.substring(7);
|
||||
//print("Loading image " + assetImage);
|
||||
return rootBundle.load(assetImage).then((value) {
|
||||
return assetImage;
|
||||
}).catchError((_) {
|
||||
String imagePath = assetImage.substring(10);
|
||||
String url = Cache.mediaUrl + 'images' + imagePath;
|
||||
//print("Exception: " + assetImage + " will be loaded from the network " + url);
|
||||
return url;
|
||||
});
|
||||
}
|
||||
|
||||
Future<List<ExerciseTree>> getExerciseTreeParents(List<ExerciseTree> exerciseTree) async {
|
||||
List<ExerciseTree> copyList = this._copyList(exerciseTree);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:aitrainer_app/model/cache.dart';
|
||||
import 'package:aitrainer_app/model/exercise_type.dart';
|
||||
import 'package:aitrainer_app/service/api.dart';
|
||||
import 'package:aitrainer_app/service/logging.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class ExerciseTypeApi with Logging {
|
||||
final APIClient _client = new APIClient();
|
||||
@@ -11,10 +12,31 @@ class ExerciseTypeApi with Logging {
|
||||
final body = await _client.get("exercise_type", "");
|
||||
final Iterable json = jsonDecode(body);
|
||||
final List<ExerciseType> exerciseTypes = json.map((exerciseType) => ExerciseType.fromJson(exerciseType)).toList();
|
||||
if (exerciseTypes != null) {
|
||||
exerciseTypes.forEach((element) async {
|
||||
element.imageUrl = await _buildImage(element.imageUrl);
|
||||
});
|
||||
}
|
||||
Cache().setExerciseTypes(exerciseTypes);
|
||||
return exerciseTypes;
|
||||
}
|
||||
|
||||
Future<String> _buildImage(String imageUrl) async {
|
||||
if (imageUrl.length > 8) {
|
||||
String assetImage = 'asset/menu/' + imageUrl.substring(7);
|
||||
return rootBundle.load(assetImage).then((value) {
|
||||
return assetImage;
|
||||
}).catchError((_) {
|
||||
String imagePath = assetImage.substring(10);
|
||||
String url = Cache.mediaUrl + 'images' + imagePath;
|
||||
//print("Exception: " + assetImage + " will be loaded from the network " + url);
|
||||
return url;
|
||||
});
|
||||
} else {
|
||||
return imageUrl;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveExerciseType(ExerciseType exerciseType) async {
|
||||
String body = JsonEncoder().convert(exerciseType.toJson());
|
||||
log(" ===== saving exerciseType id: " + exerciseType.exerciseTypeId.toString() + ":" + body);
|
||||
|
||||
@@ -35,6 +35,12 @@ class FirebaseApi with Logging {
|
||||
}
|
||||
|
||||
Future<String> signInEmail(String email, String password) async {
|
||||
if (email == null) {
|
||||
throw Exception("Please type an email address");
|
||||
}
|
||||
if (password == null) {
|
||||
throw Exception("Password too short");
|
||||
}
|
||||
String rc = SIGN_IN_OK;
|
||||
try {
|
||||
userCredential = await FirebaseAuth.instance.signInWithEmailAndPassword(email: email, password: password);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
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';
|
||||
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
|
||||
|
||||
@@ -11,10 +14,14 @@ class PlatformPurchaseApi with Logging {
|
||||
StreamSubscription _conectionSubscription;
|
||||
String _platformVersion = 'Unknown';
|
||||
List<IAPItem> _items = [];
|
||||
List getIAPItems() => _items;
|
||||
List<PurchasedItem> _purchases = [];
|
||||
|
||||
final List<String> _productLists = List();
|
||||
/* Platform.isAndroid
|
||||
final List<String> _productList = List();
|
||||
|
||||
List getProductList() => _productList;
|
||||
|
||||
/* Platform.isAndroid
|
||||
? [
|
||||
'android.test.purchased',
|
||||
'point_1000',
|
||||
@@ -22,7 +29,7 @@ class PlatformPurchaseApi with Logging {
|
||||
'android.test.canceled',
|
||||
]
|
||||
: ['com.cooni.point1000', 'com.cooni.point5000'];
|
||||
*/
|
||||
*/
|
||||
|
||||
factory PlatformPurchaseApi() {
|
||||
return _singleton;
|
||||
@@ -30,27 +37,32 @@ class PlatformPurchaseApi with Logging {
|
||||
|
||||
PlatformPurchaseApi._internal();
|
||||
|
||||
void close() {
|
||||
Future<void> close() async {
|
||||
if (_conectionSubscription != null) {
|
||||
_conectionSubscription.cancel();
|
||||
_conectionSubscription = null;
|
||||
}
|
||||
await FlutterInappPurchase.instance.endConnection;
|
||||
}
|
||||
|
||||
// Platform messages are asynchronous, so we initialize in an async method.
|
||||
Future<void> initPurchasePlatformState() async {
|
||||
if (_productList.length > 0) {
|
||||
return;
|
||||
}
|
||||
log(" --- Init PurchasePlatform");
|
||||
String platformVersion;
|
||||
// Platform messages may fail, so we use a try/catch PlatformException.
|
||||
try {
|
||||
platformVersion = await FlutterInappPurchase.instance.platformVersion;
|
||||
} on Exception {
|
||||
platformVersion = 'Failed to get platform version for InAppPurchase.';
|
||||
log(platformVersion);
|
||||
log("PlatformVersion" + platformVersion);
|
||||
}
|
||||
|
||||
// prepare
|
||||
var result = await FlutterInappPurchase.instance.initConnection;
|
||||
log('result: $result');
|
||||
log(' FlutterInappPurchase init result: $result');
|
||||
|
||||
_platformVersion = platformVersion;
|
||||
|
||||
@@ -73,15 +85,57 @@ class PlatformPurchaseApi with Logging {
|
||||
_purchaseErrorSubscription = FlutterInappPurchase.purchaseError.listen((purchaseError) {
|
||||
log('purchase-error: $purchaseError');
|
||||
});
|
||||
|
||||
getSubscriptions();
|
||||
}
|
||||
|
||||
void getSubscriptions() {
|
||||
Cache().products.forEach((element) {
|
||||
Product product = element as Product;
|
||||
if (Platform.isAndroid) {
|
||||
if (product.productIdAndroid != null && product.productIdAndroid.isNotEmpty) {
|
||||
_productList.add(product.productIdAndroid);
|
||||
}
|
||||
} else {
|
||||
if (product.productIdIos != null && product.productIdIos.isNotEmpty) {
|
||||
_productList.add(product.productIdIos);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_productList.forEach((element) {
|
||||
print(element);
|
||||
});
|
||||
_getSubscription();
|
||||
}
|
||||
|
||||
void purchase(Product product) {
|
||||
if (product == null) {
|
||||
throw Exception("No product to purchase");
|
||||
}
|
||||
String productId = Platform.isAndroid ? product.productIdAndroid : product.productIdIos;
|
||||
IAPItem selected;
|
||||
for (var item in _items) {
|
||||
if (item.productId == productId) {
|
||||
selected = item;
|
||||
log("Item to purchase: " + item.toString());
|
||||
}
|
||||
}
|
||||
|
||||
if (selected != null) {
|
||||
requestPurchase(selected);
|
||||
} else {
|
||||
throw Exception("product " + productId + " not defined in the PlatformStore");
|
||||
}
|
||||
}
|
||||
|
||||
void requestPurchase(IAPItem item) {
|
||||
//FlutterInappPurchase.instance.requestPurchase(item.productId);
|
||||
FlutterInappPurchase.instance.requestPurchase("WT_monthly");
|
||||
FlutterInappPurchase.instance.requestPurchase(item.productId);
|
||||
}
|
||||
|
||||
Future _getProduct() async {
|
||||
List<IAPItem> items = await FlutterInappPurchase.instance.getProducts(_productLists);
|
||||
Future _getSubscription() async {
|
||||
List<IAPItem> items = await FlutterInappPurchase.instance.getSubscriptions(_productList);
|
||||
for (var item in items) {
|
||||
log('${item.toString()}');
|
||||
this._items.add(item);
|
||||
|
||||
+108
-106
@@ -1,4 +1,5 @@
|
||||
import 'package:aitrainer_app/bloc/sales/sales_bloc.dart';
|
||||
import 'package:aitrainer_app/service/logging.dart';
|
||||
import 'package:aitrainer_app/util/trans.dart';
|
||||
import 'package:aitrainer_app/widgets/app_bar_min.dart';
|
||||
import 'package:aitrainer_app/widgets/sales_button.dart';
|
||||
@@ -8,121 +9,122 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:modal_progress_hud/modal_progress_hud.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class SalesPage extends StatelessWidget with Trans {
|
||||
class SalesPage extends StatelessWidget with Trans, Logging {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
setContext(context);
|
||||
return BlocProvider(
|
||||
create: (context) => SalesBloc()..add(SalesLoad()),
|
||||
child: BlocConsumer<SalesBloc, SalesState>(listener: (context, state) {
|
||||
if (state is SalesError) {
|
||||
Scaffold.of(context).showSnackBar(
|
||||
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
|
||||
}
|
||||
}, builder: (context, state) {
|
||||
final salesBloc = BlocProvider.of<SalesBloc>(context);
|
||||
return ModalProgressHUD(
|
||||
child: salesWidget(salesBloc),
|
||||
inAsyncCall: state is SalesLoading,
|
||||
opacity: 0.5,
|
||||
color: Colors.black54,
|
||||
progressIndicator: CircularProgressIndicator(),
|
||||
);
|
||||
}));
|
||||
return Scaffold(
|
||||
appBar: AppBarMin(
|
||||
back: true,
|
||||
),
|
||||
body: BlocProvider(
|
||||
create: (context) => SalesBloc()..add(SalesLoad()),
|
||||
child: BlocConsumer<SalesBloc, SalesState>(listener: (context, state) {
|
||||
if (state is SalesError) {
|
||||
log("Error: " + state.message);
|
||||
Scaffold.of(context).showSnackBar(
|
||||
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
|
||||
}
|
||||
}, builder: (context, state) {
|
||||
final salesBloc = BlocProvider.of<SalesBloc>(context);
|
||||
return ModalProgressHUD(
|
||||
child: salesWidget(salesBloc),
|
||||
inAsyncCall: state is SalesLoading,
|
||||
opacity: 0.5,
|
||||
color: Colors.black54,
|
||||
progressIndicator: CircularProgressIndicator(),
|
||||
);
|
||||
})));
|
||||
}
|
||||
|
||||
Widget salesWidget(SalesBloc bloc) {
|
||||
final double mediaWidth = MediaQuery.of(context).size.width;
|
||||
final double imageWidth = (mediaWidth - 5) / 2;
|
||||
return Scaffold(
|
||||
appBar: AppBarMin(
|
||||
back: true,
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('asset/image/WT_black_background.png'),
|
||||
fit: BoxFit.cover,
|
||||
alignment: Alignment.center,
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('asset/image/WT_black_background.png'),
|
||||
fit: BoxFit.cover,
|
||||
alignment: Alignment.center,
|
||||
),
|
||||
child: CustomScrollView(scrollDirection: Axis.vertical, slivers: [
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate([
|
||||
Divider(),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 65, right: 65),
|
||||
child: Text("Unleash Your Development Now!",
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 4,
|
||||
softWrap: true,
|
||||
style: GoogleFonts.archivoBlack(
|
||||
fontSize: 30,
|
||||
color: Colors.white,
|
||||
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,
|
||||
),
|
||||
],
|
||||
))),
|
||||
Divider(),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 45, right: 45),
|
||||
child: Text("Learn about your development, enjoy AI-driven predictions of all of your skills and bodyparts.",
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 4,
|
||||
softWrap: true,
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
))),
|
||||
SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
child: CustomScrollView(scrollDirection: Axis.vertical, slivers: [
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate([
|
||||
Divider(),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 65, right: 65),
|
||||
child: Text("Unleash Your Development Now!",
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 4,
|
||||
softWrap: true,
|
||||
style: GoogleFonts.archivoBlack(
|
||||
fontSize: 30,
|
||||
color: Colors.white,
|
||||
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,
|
||||
),
|
||||
],
|
||||
))),
|
||||
Divider(),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 45, right: 45),
|
||||
child: Text("Learn about your development, enjoy AI-driven predictions of all of your skills and bodyparts.",
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 4,
|
||||
softWrap: true,
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
))),
|
||||
SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
])),
|
||||
SliverGrid(
|
||||
delegate: SliverChildListDelegate(getButtons(bloc)),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 10.0,
|
||||
crossAxisSpacing: 10.0,
|
||||
childAspectRatio: 0.55,
|
||||
),
|
||||
),
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate([
|
||||
SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 55, right: 55),
|
||||
child: Text(
|
||||
"Subscription Conditions",
|
||||
style: GoogleFonts.inter(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
)),
|
||||
Divider(),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 55, right: 55),
|
||||
child: Text(
|
||||
"Payment will be charged to your account. Subscription automatically renews unless auto-renew is turned off at least 24 hourse before the end of the current period",
|
||||
style: GoogleFonts.inter(fontSize: 12, color: Colors.white),
|
||||
)),
|
||||
Divider(),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 55, right: 55),
|
||||
child: Text(
|
||||
"Account will be charged for renewal within 24 hours prior to the end of the current period",
|
||||
style: GoogleFonts.inter(fontSize: 12, color: Colors.white),
|
||||
)),
|
||||
])),
|
||||
])));
|
||||
])),
|
||||
SliverGrid(
|
||||
delegate: SliverChildListDelegate(getButtons(bloc)),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 10.0,
|
||||
crossAxisSpacing: 10.0,
|
||||
childAspectRatio: 0.55,
|
||||
),
|
||||
),
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate([
|
||||
SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 55, right: 55),
|
||||
child: Text(
|
||||
"Subscription Conditions",
|
||||
style: GoogleFonts.inter(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
)),
|
||||
Divider(),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 55, right: 55),
|
||||
child: Text(
|
||||
"Payment will be charged to your account. Subscription automatically renews unless auto-renew is turned off at least 24 hourse before the end of the current period",
|
||||
style: GoogleFonts.inter(fontSize: 12, color: Colors.white),
|
||||
)),
|
||||
Divider(),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 55, right: 55),
|
||||
child: Text(
|
||||
"Account will be charged for renewal within 24 hours prior to the end of the current period",
|
||||
style: GoogleFonts.inter(fontSize: 12, color: Colors.white),
|
||||
)),
|
||||
])),
|
||||
]));
|
||||
;
|
||||
}
|
||||
|
||||
@@ -141,7 +143,7 @@ class SalesPage extends StatelessWidget with Trans {
|
||||
}
|
||||
Widget button = SalesButton(
|
||||
title: title,
|
||||
price: element.description + interval,
|
||||
price: element.localizedPrice,
|
||||
desc1: "Development programs",
|
||||
desc2: "Suggestions based on your actual status",
|
||||
desc3: "Special customized training plans",
|
||||
|
||||
@@ -142,6 +142,9 @@ class _AppBarNav extends State<AppBarNav> with SingleTickerProviderStateMixin, C
|
||||
ExerciseRepository exerciseRepository = ExerciseRepository();
|
||||
exerciseRepository.getBaseExerciseFinishedPercent();
|
||||
percent = Cache().getPercentExercises();
|
||||
if (percent == null || percent == -1) {
|
||||
percent = 0;
|
||||
}
|
||||
}
|
||||
int sizeExerciseList = Cache().getExercises() == null ? 0 : Cache().getExercises().length;
|
||||
if (sizeExerciseList == 0) {
|
||||
|
||||
@@ -160,7 +160,7 @@ class _DialogPremiumState extends State<DialogPremium> with Trans {
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: GestureDetector(
|
||||
onTap: widget.onTap ?? widget.onTap,
|
||||
onTap: () => Navigator.of(context).pushNamed("salesPage"),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
|
||||
@@ -2,12 +2,14 @@ import 'package:aitrainer_app/bloc/session/session_bloc.dart';
|
||||
import 'package:aitrainer_app/bloc/settings/settings_bloc.dart';
|
||||
import 'package:aitrainer_app/model/cache.dart';
|
||||
import 'package:aitrainer_app/service/logging.dart';
|
||||
import 'package:aitrainer_app/util/platform_purchase.dart';
|
||||
import 'package:aitrainer_app/view/login.dart';
|
||||
import 'package:aitrainer_app/view/menu_page.dart';
|
||||
import 'package:aitrainer_app/view/registration.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/semantics.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'loading.dart';
|
||||
@@ -85,4 +87,10 @@ class _HomePageState extends State<AitrainerHome> with Logging {
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() async {
|
||||
super.dispose();
|
||||
await PlatformPurchaseApi().close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ class ImageButton extends StatelessWidget {
|
||||
top = height - (style.fontSize - 5) * text.length - 2 * left < 0 ? height - 2 * style.fontSize - 22 : height - style.fontSize - 37;
|
||||
//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));
|
||||
return Stack(alignment: AlignmentDirectional.bottomStart, children: [
|
||||
FlatButton(
|
||||
child: image == null
|
||||
@@ -101,29 +103,12 @@ class ImageButton extends StatelessWidget {
|
||||
],
|
||||
)),
|
||||
)),
|
||||
/* Positioned(
|
||||
top: top,
|
||||
left: left,
|
||||
child: Container(
|
||||
height: width - 2 * left,
|
||||
width: width - 2 * left,
|
||||
child: InkWell(
|
||||
onTap: onTap ?? onTap,
|
||||
child: Text(
|
||||
text,
|
||||
maxLines: 2,
|
||||
style: style,
|
||||
),
|
||||
),
|
||||
color: Colors.transparent,
|
||||
),
|
||||
), */
|
||||
Cache().hasPurchased
|
||||
? Offstage()
|
||||
: Stack(alignment: Alignment.topCenter, children: [
|
||||
Positioned(
|
||||
top: 20,
|
||||
left: 20,
|
||||
top: 10,
|
||||
left: (width / 2 - 30) / 2 - 75,
|
||||
child: this.isLocked
|
||||
? GestureDetector(
|
||||
child: Image.asset(
|
||||
|
||||
@@ -8,12 +8,14 @@ import 'package:aitrainer_app/localization/app_localization.dart';
|
||||
import 'package:aitrainer_app/model/cache.dart';
|
||||
import 'package:aitrainer_app/model/workout_menu_tree.dart';
|
||||
import 'package:aitrainer_app/service/logging.dart';
|
||||
import 'package:aitrainer_app/util/not_found_exception.dart';
|
||||
import 'package:aitrainer_app/util/trans.dart';
|
||||
import 'package:aitrainer_app/widgets/dialog_common.dart';
|
||||
import 'package:badges/badges.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/painting.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
@@ -331,30 +333,30 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
dynamic _getButtonImage(WorkoutMenuTree workoutTree, double cWidth, double cHeight) {
|
||||
dynamic image;
|
||||
try {
|
||||
Widget _getButtonImage(WorkoutMenuTree workoutTree, double cWidth, double cHeight) {
|
||||
Widget image;
|
||||
|
||||
if (workoutTree.imageName.startsWith('https')) {
|
||||
image = ClipRRect(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
child: Image.asset(
|
||||
workoutTree.imageName,
|
||||
height: 210,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
String url = Cache.mediaUrl + 'images/' + workoutTree.imageName.substring(11);
|
||||
Widget image = FadeInImage.assetNetwork(
|
||||
placeholder: 'asset/image/dots.gif',
|
||||
image: url,
|
||||
height: 210,
|
||||
);
|
||||
return image;
|
||||
},
|
||||
borderRadius: BorderRadius.circular(24.0),
|
||||
child: Container(
|
||||
color: Colors.black87,
|
||||
child: FadeInImage(
|
||||
image: NetworkImage(workoutTree.imageName),
|
||||
placeholder: AssetImage("asset/image/dots.gif"),
|
||||
),
|
||||
));
|
||||
} on Exception catch (_) {
|
||||
String url = Cache.mediaUrl + '/images/' + workoutTree.imageName;
|
||||
image = FadeInImage.assetNetwork(
|
||||
placeholder: 'asset/image/dots.gif',
|
||||
image: url,
|
||||
height: 210,
|
||||
} else {
|
||||
image = Container(
|
||||
//width: cWidth - 30,
|
||||
//height: 210.0,
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.cover,
|
||||
image: AssetImage(workoutTree.imageName),
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(24.0)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -365,6 +367,7 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
|
||||
String badgeKey = workoutMenuTree.nameEnglish;
|
||||
bool show = Cache().getBadges()[badgeKey] != null;
|
||||
int counter = Cache().getBadges()[badgeKey] != null ? Cache().getBadges()[badgeKey] : 0;
|
||||
Widget buttonImage = _getButtonImage(workoutMenuTree, cWidth, cHeight);
|
||||
return Badge(
|
||||
padding: EdgeInsets.all(8),
|
||||
position: BadgePosition.topEnd(top: 3, end: 3),
|
||||
@@ -377,7 +380,11 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
)),
|
||||
child: _getButtonImage(workoutMenuTree, cWidth, cHeight),
|
||||
child: buttonImage == null
|
||||
? Container(
|
||||
color: Colors.red,
|
||||
)
|
||||
: buttonImage,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user