WT1.1.4 build 45 android fb, iap

This commit is contained in:
bossanyit
2021-01-12 16:45:43 +01:00
parent f3cc241f65
commit 4bd1950ffd
26 changed files with 772 additions and 180 deletions
+37 -6
View File
@@ -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);
}
}
-1
View File
@@ -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));
+1 -1
View File
@@ -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;
+1
View File
@@ -12,6 +12,7 @@ class Product {
String productIdAndroid;
double priceIos;
double priceAndroid;
String localizedPrice;
Product.fromJson(Map json) {
this.productId = json['productId'];
+33 -6
View File
@@ -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 " +
+19
View File
@@ -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);
+22
View File
@@ -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);
+6
View File
@@ -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);
+63 -9
View File
@@ -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
View File
@@ -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",
+3
View File
@@ -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) {
+1 -1
View File
@@ -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: [
+8
View File
@@ -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();
}
}
+4 -19
View File
@@ -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(
+30 -23
View File
@@ -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,
);
}