WT 1.1.13 Test Central bug fixes

This commit is contained in:
bossanyit 2021-04-19 00:16:07 +02:00
parent 4cde174a83
commit cc1117d8a1
39 changed files with 1114 additions and 214 deletions

1
android/data/queries.sql Normal file
View File

@ -0,0 +1 @@
SELECT * FROM `tracking` WHERE `date_add` > '2021-04-17 00:00:00' AND `date_add` < '2021-04-18 00:00:00' AND `event` LIKE 'registration'

View File

@ -439,6 +439,8 @@
"How can serve you this result?":"How can serve you this result?",
"Go":"Go",
"Result":"Result",
"Name too short":"Name too short"
"Name too short":"Name too short",
"No, bring me there":"No, bring me there",
"You are about to add a new parallel test":"You are about to add a new parallel test"
}

View File

@ -434,5 +434,7 @@
"Development":"Fejlődésedhez",
"Go":"Érdekel",
"Result":"Értékelés",
"Name too short":"A név túl rövid"
"Name too short":"A név túl rövid",
"No, bring me there":"Nem, vigyél oda",
"You are about to add a new parallel test":"Egy új gyakorlatot készülsz párhuzamosan végrehajtani"
}

View File

@ -156,14 +156,14 @@ PODS:
- path_provider (0.0.1):
- Flutter
- PromisesObjC (1.2.12)
- Purchases (3.10.6):
- PurchasesCoreSwift (= 3.10.6)
- purchases_flutter (3.1.0):
- Purchases (3.10.7):
- PurchasesCoreSwift (= 3.10.7)
- purchases_flutter (3.2.1):
- Flutter
- PurchasesHybridCommon (= 1.6.1)
- PurchasesCoreSwift (3.10.6)
- PurchasesHybridCommon (1.6.1):
- Purchases (= 3.10.6)
- PurchasesHybridCommon (= 1.6.2)
- PurchasesCoreSwift (3.10.7)
- PurchasesHybridCommon (1.6.2):
- Purchases (= 3.10.7)
- shared_preferences (0.0.1):
- Flutter
- smartlook (0.0.5):
@ -312,10 +312,10 @@ SPEC CHECKSUMS:
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97
Purchases: 520fdb59140fed96932a30d02a3ec04858cb541c
purchases_flutter: 05472ba84c83f05a138a3a657f1013f5f7143539
PurchasesCoreSwift: 31c2a3d7394432abbe64d46f0933835de0b33033
PurchasesHybridCommon: 013c8072b73e752a206779747e88c068fbf999ec
Purchases: b8b8fb6e856ac8166e217f6e014df894d821dda1
purchases_flutter: 0130970b895c903e4e0aad793dd3a4c1b70bb434
PurchasesCoreSwift: 8ae0f08e020f0bc97c1befa4e38a0dbc8e9732e0
PurchasesHybridCommon: 5f5c1c245b12fc5e8760af7d11cb10f888109a9b
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
smartlook: bbc5c73a85c752a31dabf100c8930838c646342e
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904

View File

@ -405,7 +405,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 1.1.12;
MARKETING_VERSION = 1.1.13;
PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -548,7 +548,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 1.1.12;
MARKETING_VERSION = 1.1.13;
PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -583,7 +583,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 1.1.12;
MARKETING_VERSION = 1.1.13;
PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";

View File

@ -136,7 +136,7 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
}
product2Display.sort((a, b) {
return a.sort < b.sort ? a.sort : b.sort;
return a.sort < b.sort ? -1 : 1;
});
productTest.productId = productId;

View File

@ -8,6 +8,7 @@ import 'package:aitrainer_app/model/exercise_plan.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/model/exercise_plan_template.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/workout_tree_repository.dart';
import 'package:aitrainer_app/service/exercise_plan_service.dart';
import 'package:aitrainer_app/util/enums.dart';
@ -28,6 +29,9 @@ class TestSetEditBloc extends Bloc<TestSetEditEvent, TestSetEditState> {
final List<ExerciseType> _actualExerciseTypes = [];
final HashMap<int, ExerciseType?> _exercisePlanDetails = HashMap();
final HashMap<int, List<ExerciseType>?> _displayPlanDetails = HashMap();
int? sliderIndex;
TestSetEditBloc(
{required this.templateName, required this.templateNameTranslation, required this.workoutTreeRepository, required this.menuBloc})
: super(TestSetEditInitial()) {
@ -36,12 +40,24 @@ class TestSetEditBloc extends Bloc<TestSetEditEvent, TestSetEditState> {
final ExercisePlanTemplate template = element as ExercisePlanTemplate;
if (template.name == templateName) {
templateDescription = template.descriptionTranslation;
int index = 0;
template.exerciseTypes.forEach((id) {
final ExerciseType? exerciseType = Cache().getExerciseTypeById(id);
if (exerciseType != null) {
_exerciseTypes.add(exerciseType);
_actualExerciseTypes.add(exerciseType);
_exercisePlanDetails[exerciseType.exerciseTypeId] = exerciseType;
_exercisePlanDetails[index] = exerciseType;
_displayPlanDetails[index] = [exerciseType];
final WorkoutMenuTree? workoutTree = menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(exerciseType.exerciseTypeId);
final List<WorkoutMenuTree>? alternativeMenuItems = menuBloc.menuTreeRepository.getWorkoutTreeAlternatives(workoutTree);
if (alternativeMenuItems != null) {
final List<ExerciseType>? actualList = _displayPlanDetails[index];
alternativeMenuItems.forEach((element) {
actualList!.add(element.exerciseType!);
});
}
index++;
}
});
}
@ -54,32 +70,24 @@ class TestSetEditBloc extends Bloc<TestSetEditEvent, TestSetEditState> {
try {
if (event is TestSetEditChangeExerciseType) {
yield TestSetEditLoading();
final List<ExerciseType> alternatives = workoutTreeRepository.getExerciseTypeAlternatives(event.exerciseTypeId);
final ExerciseType? exerciseType = Cache().getExerciseTypeById(event.exerciseTypeId);
if (exerciseType != null) {
if (_exercisePlanDetails[event.exerciseTypeId] == null) {
/// it was skipped
_exercisePlanDetails[exerciseType.exerciseTypeId] = exerciseType;
} else {
if (event.index == 0) {
_exercisePlanDetails[exerciseType.exerciseTypeId] = exerciseType;
} else {
final changedExerciseType = alternatives[event.index - 1];
_exercisePlanDetails[exerciseType.exerciseTypeId] = changedExerciseType;
}
}
ExerciseType? exerciseType;
if (_exercisePlanDetails[event.indexKey] == null) {
exerciseType = displayPlanDetails[event.indexKey][0];
} else {
exerciseType = displayPlanDetails[event.indexKey][event.index];
}
// to keep the slider accurate
refreshActualPlan();
_exercisePlanDetails[event.indexKey] = exerciseType;
this.sliderIndex = event.index;
yield TestSetEditReady();
} else if (event is TestSetEditDeleteExerciseType) {
yield TestSetEditLoading();
final ExerciseType? exerciseType = Cache().getExerciseTypeById(event.exerciseTypeId);
if (exerciseType != null) {
_exercisePlanDetails[exerciseType.exerciseTypeId] = null;
}
refreshActualPlan();
_exercisePlanDetails[event.indexKey] = null;
yield TestSetEditReady();
} else if (event is TestSetEditAddExerciseType) {
yield TestSetEditLoading();
ExerciseType exerciseType = displayPlanDetails[event.indexKey][0];
_exercisePlanDetails[event.indexKey] = exerciseType;
yield TestSetEditReady();
} else if (event is TestSetEditSubmit) {
yield TestSetEditLoading();
@ -114,6 +122,7 @@ class TestSetEditBloc extends Bloc<TestSetEditEvent, TestSetEditState> {
List get exerciseTypes => this._exerciseTypes;
List get actualExerciseTypes => this._actualExerciseTypes;
HashMap get exercisePlanDetails => this._exercisePlanDetails;
get displayPlanDetails => this._displayPlanDetails;
void refreshActualPlan() {
_actualExerciseTypes.removeRange(0, _actualExerciseTypes.length - 1);

View File

@ -13,19 +13,27 @@ class TestSetEditLoad extends TestSetEditEvent {
class TestSetEditChangeExerciseType extends TestSetEditEvent {
final int index;
final int exerciseTypeId;
const TestSetEditChangeExerciseType({required this.index, required this.exerciseTypeId});
final int indexKey;
const TestSetEditChangeExerciseType({required this.index, required this.indexKey});
@override
List<Object> get props => [index, exerciseTypeId];
List<Object> get props => [index, indexKey];
}
class TestSetEditDeleteExerciseType extends TestSetEditEvent {
final int exerciseTypeId;
const TestSetEditDeleteExerciseType({required this.exerciseTypeId});
final int indexKey;
const TestSetEditDeleteExerciseType({required this.indexKey});
@override
List<Object> get props => [exerciseTypeId];
List<Object> get props => [indexKey];
}
class TestSetEditAddExerciseType extends TestSetEditEvent {
final int indexKey;
const TestSetEditAddExerciseType({required this.indexKey});
@override
List<Object> get props => [indexKey];
}
class TestSetEditSkipExerciseType extends TestSetEditEvent {

View File

@ -37,6 +37,7 @@ class TestSetExecuteBloc extends Bloc<TestSetExecuteEvent, TestSetExecuteState>
void initExercisePlan() {
exercisePlan = Cache().activeExercisePlan;
if (exercisePlan != null) {
print("Ability ${exercisePlan!.type!}");
testName = exercisePlan!.name;
this.miniTestSet = exercisePlan!.type != null && ExerciseAbility.mini_test_set.equalsStringTo(exercisePlan!.type!);
this.paralellTest = exercisePlan!.type != null && ExerciseAbility.paralell_test.equalsStringTo(exercisePlan!.type!);
@ -265,7 +266,7 @@ class TestSetExecuteBloc extends Bloc<TestSetExecuteEvent, TestSetExecuteState>
HashMap ret = HashMap();
if (exercisePlan != null && ExerciseAbility.mini_test_set.equalsStringTo(exercisePlan!.type!)) {
final String message = "You have an active Test Set!";
final String message2 = "Do you want you to override it?";
final String message2 = "Do you want to override it?";
ret['message'] = message;
ret['message2'] = message2;
ret['canAdd'] = false;

View File

@ -0,0 +1,340 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
const channelName = 'flutter.oddbit.id/facebook_app_events';
class FacebookAppEvents {
static const _channel = MethodChannel(channelName);
// See: https://github.com/facebook/facebook-android-sdk/blob/master/facebook-core/src/main/java/com/facebook/appevents/AppEventsConstants.java
static const eventNameActivatedApp = 'fb_mobile_activate_app';
static const eventNameDeactivatedApp = 'fb_mobile_deactivate_app';
static const eventNameCompletedRegistration = 'fb_mobile_complete_registration';
static const eventNameViewedContent = 'fb_mobile_content_view';
static const eventNameRated = 'fb_mobile_rate';
static const eventNameInitiatedCheckout = 'fb_mobile_initiated_checkout';
static const eventNameAddedToCart = 'fb_mobile_add_to_cart';
static const eventNameAddedToWishlist = 'fb_mobile_add_to_wishlist';
static const _paramNameValueToSum = "_valueToSum";
static const paramNameCurrency = "fb_currency";
static const paramNameRegistrationMethod = "fb_registration_method";
static const paramNamePaymentInfoAvailable = "fb_payment_info_available";
static const paramNameNumItems = "fb_num_items";
static const paramValueYes = "1";
static const paramValueNo = "0";
/// Parameter key used to specify a generic content type/family for the logged event, e.g.
/// "music", "photo", "video". Options to use will vary depending on the nature of the app.
static const paramNameContentType = "fb_content_type";
/// Parameter key used to specify data for the one or more pieces of content being logged about.
/// Data should be a JSON encoded string.
/// Example:
/// "[{\"id\": \"1234\", \"quantity\": 2, \"item_price\": 5.99}, {\"id\": \"5678\", \"quantity\": 1, \"item_price\": 9.99}]"
static const paramNameContent = "fb_content";
/// Parameter key used to specify an ID for the specific piece of content being logged about.
/// This could be an EAN, article identifier, etc., depending on the nature of the app.
static const paramNameContentId = "fb_content_id";
/// Clears the current user data
Future<void> clearUserData() {
return _channel.invokeMethod<void>('clearUserData');
}
/// Clears the currently set user id.
Future<void> clearUserID() {
return _channel.invokeMethod<void>('clearUserID');
}
/// Explicitly flush any stored events to the server.
Future<void> flush() {
return _channel.invokeMethod<void>('flush');
}
/// Returns the app ID this logger was configured to log to.
Future<String?> getApplicationId() {
return _channel.invokeMethod<String>('getApplicationId');
}
Future<String?> getAnonymousId() {
return _channel.invokeMethod<String>('getAnonymousId');
}
/// Log an app event with the specified [name] and the supplied [parameters] value.
Future<void> logEvent({
required String name,
Map<String, dynamic>? parameters,
double? valueToSum,
}) {
final args = <String, dynamic>{
'name': name,
'parameters': parameters,
_paramNameValueToSum: valueToSum,
};
return _channel.invokeMethod<void>('logEvent', _filterOutNulls(args));
}
/// Sets user data to associate with all app events.
/// All user data are hashed and used to match Facebook user from this
/// instance of an application. The user data will be persisted between
/// application instances.
Future<void> setUserData({
String? email,
String? firstName,
String? lastName,
String? phone,
String? dateOfBirth,
String? gender,
String? city,
String? state,
String? zip,
String? country,
}) {
final args = <String, dynamic>{
'email': email,
'firstName': firstName,
'lastName': lastName,
'phone': phone,
'dateOfBirth': dateOfBirth,
'gender': gender,
'city': city,
'state': state,
'zip': zip,
'country': country,
};
return _channel.invokeMethod<void>('setUserData', args);
}
/// Logs an app event that tracks that the application was open via Push Notification.
Future<void> logPushNotificationOpen({
required Map<String, dynamic> payload,
String? action,
}) {
final args = <String, dynamic>{
'payload': payload,
'action': action,
};
return _channel.invokeMethod<void>('logPushNotificationOpen', args);
}
/// Sets a user [id] to associate with all app events.
/// This can be used to associate your own user id with the
/// app events logged from this instance of an application.
/// The user ID will be persisted between application instances.
Future<void> setUserID(String id) {
return _channel.invokeMethod<void>('setUserID', id);
}
/// Update user properties as provided by a map of [parameters]
Future<void> updateUserProperties({
required Map<String, dynamic> parameters,
String? applicationId,
}) {
final args = <String, dynamic>{
'parameters': parameters,
'applicationId': applicationId,
};
return _channel.invokeMethod<void>('updateUserProperties', args);
}
// Below are shorthand implementations of the predefined app event constants
/// Log this event when an app is being activated.
///
/// See: https://developers.facebook.com/docs/reference/androidsdk/current/facebook/com/facebook/appevents/appeventsconstants.html/#eventnameactivatedapp
Future<void> logActivatedApp() {
return logEvent(name: eventNameActivatedApp);
}
/// Log this event when an app is being deactivated.
///
/// See: https://developers.facebook.com/docs/reference/androidsdk/current/facebook/com/facebook/appevents/appeventsconstants.html/#eventnamedeactivatedapp
Future<void> logDeactivatedApp() {
return logEvent(name: eventNameDeactivatedApp);
}
/// Log this event when the user has completed registration with the app.
/// Parameter [registrationMethod] is used to specify the method the user has
/// used to register for the app, e.g. "Facebook", "email", "Google", etc.
/// See: https://developers.facebook.com/docs/reference/androidsdk/current/facebook/com/facebook/appevents/appeventsconstants.html/#eventnamecompletedregistration
Future<void> logCompletedRegistration({String? registrationMethod}) {
return logEvent(
name: eventNameCompletedRegistration,
parameters: {
paramNameRegistrationMethod: registrationMethod,
},
);
}
/// Log this event when the user has rated an item in the app.
///
/// See: https://developers.facebook.com/docs/reference/androidsdk/current/facebook/com/facebook/appevents/appeventsconstants.html/#eventnamerated
Future<void> logRated({double? valueToSum}) {
return logEvent(
name: eventNameRated,
valueToSum: valueToSum,
);
}
/// Log this event when the user has viewed a form of content in the app.
///
/// See: https://developers.facebook.com/docs/reference/androidsdk/current/facebook/com/facebook/appevents/appeventsconstants.html/#eventnameviewedcontent
Future<void> logViewContent({
Map<String, dynamic>? content,
String? id,
String? type,
String? currency,
double? price,
}) {
return logEvent(
name: eventNameViewedContent,
parameters: {
paramNameContent: content != null ? json.encode(content) : null,
paramNameContentId: id,
paramNameContentType: type,
paramNameCurrency: currency,
},
valueToSum: price,
);
}
/// Log this event when the user has added item to cart
///
/// See: https://developers.facebook.com/docs/reference/androidsdk/current/facebook/com/facebook/appevents/appeventsconstants.html/#eventnameaddedtocart
Future<void> logAddToCart({
Map<String, dynamic>? content,
@required String? id,
@required String? type,
@required String? currency,
@required double? price,
}) {
return logEvent(
name: eventNameAddedToCart,
parameters: {
paramNameContent: content != null ? json.encode(content) : null,
paramNameContentId: id,
paramNameContentType: type,
paramNameCurrency: currency,
},
valueToSum: price,
);
}
/// Log this event when the user has added item to cart
///
/// See: https://developers.facebook.com/docs/reference/androidsdk/current/facebook/com/facebook/appevents/appeventsconstants.html/#eventnameaddedtowishlist
Future<void> logAddToWishlist({
Map<String, dynamic>? content,
@required String? id,
@required String? type,
@required String? currency,
@required double? price,
}) {
return logEvent(
name: eventNameAddedToWishlist,
parameters: {
paramNameContent: content != null ? json.encode(content) : null,
paramNameContentId: id,
paramNameContentType: type,
paramNameCurrency: currency,
},
valueToSum: price,
);
}
/// Re-enables auto logging of app events after user consent
/// if disabled for GDPR-compliance.
///
/// See: https://developers.facebook.com/docs/app-events/gdpr-compliance
Future<void> setAutoLogAppEventsEnabled(bool enabled) {
return _channel.invokeMethod<void>('setAutoLogAppEventsEnabled', enabled);
}
/// Set Data Processing Options
/// This is needed for California Consumer Privacy Act (CCPA) compliance
///
/// See: https://developers.facebook.com/docs/marketing-apis/data-processing-options
Future<void> setDataProcessingOptions(
List<String> options, {
int? country,
int? state,
}) {
final args = <String, dynamic>{
'options': options,
'country': country,
'state': state,
};
return _channel.invokeMethod<void>('setDataProcessingOptions', args);
}
Future<void> logPurchase({
required double amount,
required String currency,
Map<String, dynamic>? parameters,
}) {
final args = <String, dynamic>{
'amount': amount,
'currency': currency,
'parameters': parameters,
};
return _channel.invokeMethod<void>('logPurchase', _filterOutNulls(args));
}
Future<void> logInitiatedCheckout({
required double totalPrice,
required String currency,
required String contentType,
required String contentId,
required int numItems,
bool paymentInfoAvailable = false,
}) {
return logEvent(
name: eventNameInitiatedCheckout,
valueToSum: totalPrice,
parameters: {
paramNameContentType: contentType,
paramNameContentId: contentId,
paramNameNumItems: numItems,
paramNameCurrency: currency,
paramNamePaymentInfoAvailable: paymentInfoAvailable ? paramValueYes : paramValueNo,
},
);
}
/// Sets the Advert Tracking propeety for iOS advert tracking
/// an iOS 14+ feature, android should just return a success.
Future<void> setAdvertiserTracking({
required bool enabled,
}) {
final args = <String, dynamic>{'enabled': enabled};
return _channel.invokeMethod<void>('setAdvertiserTracking', args);
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
// PRIVATE METHODS BELOW HERE
/// Creates a new map containing all of the key/value pairs from [parameters]
/// except those whose value is `null`.
Map<String, dynamic> _filterOutNulls(Map<String, dynamic> parameters) {
final Map<String, dynamic> filtered = <String, dynamic>{};
parameters.forEach((String key, dynamic value) {
if (value != null) {
filtered[key] = value;
}
});
return filtered;
}
}

View File

@ -0,0 +1,119 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'wave.dart';
const double _twoPi = math.pi * 2.0;
const double _epsilon = .001;
const double _sweep = _twoPi - _epsilon;
class LiquidCircularProgressIndicator extends ProgressIndicator {
///The width of the border, if this is set [borderColor] must also be set.
final double borderWidth;
///The color of the border, if this is set [borderWidth] must also be set.
final Color borderColor;
///The widget to show in the center of the progress indicator.
final Widget? center;
///The direction the liquid travels.
final Axis direction;
LiquidCircularProgressIndicator({
Key? key,
double value = 0.5,
Color? backgroundColor,
Animation<Color>? valueColor,
required this.borderWidth,
required this.borderColor,
this.center,
this.direction = Axis.vertical,
}) : super(
key: key,
value: value,
backgroundColor: backgroundColor,
valueColor: valueColor,
);
Color _getBackgroundColor(BuildContext context) => backgroundColor ?? Theme.of(context).backgroundColor;
Color _getValueColor(BuildContext context) => valueColor?.value ?? Theme.of(context).accentColor;
@override
State<StatefulWidget> createState() => _LiquidCircularProgressIndicatorState();
}
class _LiquidCircularProgressIndicatorState extends State<LiquidCircularProgressIndicator> {
@override
Widget build(BuildContext context) {
return ClipPath(
clipper: _CircleClipper(),
child: CustomPaint(
painter: _CirclePainter(
color: widget._getBackgroundColor(context),
),
foregroundPainter: _CircleBorderPainter(
color: widget.borderColor,
width: widget.borderWidth,
),
child: Stack(
children: [
Wave(
value: widget.value!,
color: widget._getValueColor(context),
direction: widget.direction,
),
if (widget.center != null) Center(child: widget.center),
],
),
),
);
}
}
class _CirclePainter extends CustomPainter {
final Color color;
_CirclePainter({required this.color});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
canvas.drawArc(Offset.zero & size, 0, _sweep, false, paint);
}
@override
bool shouldRepaint(_CirclePainter oldDelegate) => color != oldDelegate.color;
}
class _CircleBorderPainter extends CustomPainter {
final Color color;
final double width;
_CircleBorderPainter({required this.color, required this.width});
@override
void paint(Canvas canvas, Size size) {
final borderPaint = Paint()
..color = color
..style = PaintingStyle.stroke
..strokeWidth = width;
final newSize = Size(size.width - width, size.height - width);
canvas.drawArc(Offset(width / 2, width / 2) & newSize, 0, _sweep, false, borderPaint);
}
@override
bool shouldRepaint(_CircleBorderPainter oldDelegate) => color != oldDelegate.color || width != oldDelegate.width;
}
class _CircleClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
final path = Path()..addArc(Offset.zero & size, 0, _sweep);
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

View File

@ -0,0 +1,101 @@
import 'package:flutter/material.dart';
import 'wave.dart';
class LiquidCustomProgressIndicator extends ProgressIndicator {
///The widget to show in the center of the progress indicator.
final Widget? center;
///The direction the liquid travels.
final Axis direction;
///The path used to draw the shape of the progress indicator. The size of the progress indicator is controlled by the bounds of this path.
final Path shapePath;
LiquidCustomProgressIndicator({
Key? key,
double value = 0.5,
Color? backgroundColor,
Animation<Color>? valueColor,
this.center,
required this.direction,
required this.shapePath,
}) : super(
key: key,
value: value,
backgroundColor: backgroundColor,
valueColor: valueColor,
);
Color _getBackgroundColor(BuildContext context) => backgroundColor ?? Theme.of(context).backgroundColor;
Color _getValueColor(BuildContext context) => valueColor?.value ?? Theme.of(context).accentColor;
@override
State<StatefulWidget> createState() => _LiquidCustomProgressIndicatorState();
}
class _LiquidCustomProgressIndicatorState extends State<LiquidCustomProgressIndicator> {
@override
Widget build(BuildContext context) {
final pathBounds = widget.shapePath.getBounds();
return SizedBox(
width: pathBounds.width + pathBounds.left,
height: pathBounds.height + pathBounds.top,
child: ClipPath(
clipper: _CustomPathClipper(
path: widget.shapePath,
),
child: CustomPaint(
painter: _CustomPathPainter(
color: widget._getBackgroundColor(context),
path: widget.shapePath,
),
child: Stack(
children: <Widget>[
Positioned.fill(
left: pathBounds.left,
top: pathBounds.top,
child: Wave(
value: widget.value!,
color: widget._getValueColor(context),
direction: widget.direction,
),
),
if (widget.center != null) Center(child: widget.center),
],
),
),
),
);
}
}
class _CustomPathPainter extends CustomPainter {
final Color color;
final Path path;
_CustomPathPainter({required this.color, required this.path});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(_CustomPathPainter oldDelegate) => color != oldDelegate.color || path != oldDelegate.path;
}
class _CustomPathClipper extends CustomClipper<Path> {
final Path path;
_CustomPathClipper({required this.path});
@override
Path getClip(Size size) {
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

View File

@ -0,0 +1,152 @@
import 'package:flutter/material.dart';
import 'wave.dart';
class LiquidLinearProgressIndicator extends ProgressIndicator {
///The width of the border, if this is set [borderColor] must also be set.
final double? borderWidth;
///The color of the border, if this is set [borderWidth] must also be set.
final Color? borderColor;
///The radius of the border.
final double? borderRadius;
///The widget to show in the center of the progress indicator.
final Widget? center;
///The direction the liquid travels.
final Axis direction;
LiquidLinearProgressIndicator({
Key? key,
double value = 0.5,
Color? backgroundColor,
Animation<Color>? valueColor,
this.borderWidth,
this.borderColor,
this.borderRadius,
this.center,
this.direction = Axis.horizontal,
}) : super(
key: key,
value: value,
backgroundColor: backgroundColor,
valueColor: valueColor,
) {
if (borderWidth != null && borderColor == null || borderColor != null && borderWidth == null) {
throw ArgumentError("borderWidth and borderColor should both be set.");
}
}
Color _getBackgroundColor(BuildContext context) => backgroundColor ?? Theme.of(context).backgroundColor;
Color _getValueColor(BuildContext context) => valueColor?.value ?? Theme.of(context).accentColor;
@override
State<StatefulWidget> createState() => _LiquidLinearProgressIndicatorState();
}
class _LiquidLinearProgressIndicatorState extends State<LiquidLinearProgressIndicator> {
@override
Widget build(BuildContext context) {
return ClipPath(
clipper: _LinearClipper(
radius: widget.borderRadius!,
),
child: CustomPaint(
painter: _LinearPainter(
color: widget._getBackgroundColor(context),
radius: widget.borderRadius!,
),
foregroundPainter: _LinearBorderPainter(
color: widget.borderColor!,
width: widget.borderWidth!,
radius: widget.borderRadius!,
),
child: Stack(
children: <Widget>[
Wave(
value: widget.value!,
color: widget._getValueColor(context),
direction: widget.direction,
),
if (widget.center != null) Center(child: widget.center),
],
),
),
);
}
}
class _LinearPainter extends CustomPainter {
final Color color;
final double radius;
_LinearPainter({required this.color, required this.radius});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, size.width, size.height),
Radius.circular(radius),
),
paint);
}
@override
bool shouldRepaint(_LinearPainter oldDelegate) => color != oldDelegate.color;
}
class _LinearBorderPainter extends CustomPainter {
final Color color;
final double width;
final double? radius;
_LinearBorderPainter({
required this.color,
required this.width,
required this.radius,
});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..style = PaintingStyle.stroke
..strokeWidth = width;
final alteredRadius = radius ?? 0;
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(width / 2, width / 2, size.width - width, size.height - width),
Radius.circular(alteredRadius - width),
),
paint);
}
@override
bool shouldRepaint(_LinearBorderPainter oldDelegate) =>
color != oldDelegate.color || width != oldDelegate.width || radius != oldDelegate.radius;
}
class _LinearClipper extends CustomClipper<Path> {
final double? radius;
_LinearClipper({required this.radius});
@override
Path getClip(Size size) {
final path = Path()
..addRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, size.width, size.height),
Radius.circular(radius ?? 0),
),
);
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

View File

@ -0,0 +1,5 @@
library liquid_progress_indicator;
export 'liquid_circular_progress_indicator.dart';
//export 'liquid_linear_progress_indicator.dart';
//export 'liquid_custom_progress_indicator.dart';

View File

@ -0,0 +1,114 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
class Wave extends StatefulWidget {
final double value;
final Color color;
final Axis direction;
const Wave({
Key? key,
required this.value,
required this.color,
required this.direction,
}) : super(key: key);
@override
_WaveState createState() => _WaveState();
}
class _WaveState extends State<Wave> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
);
_animationController.repeat();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
),
builder: (context, child) => ClipPath(
child: Container(
color: widget.color,
),
clipper: _WaveClipper(
animationValue: _animationController.value,
value: widget.value,
direction: widget.direction,
),
),
);
}
}
class _WaveClipper extends CustomClipper<Path> {
final double animationValue;
final double value;
final Axis direction;
_WaveClipper({
required this.animationValue,
required this.value,
required this.direction,
});
@override
Path getClip(Size size) {
if (direction == Axis.horizontal) {
Path path = Path()
..addPolygon(_generateHorizontalWavePath(size), false)
..lineTo(0.0, size.height)
..lineTo(0.0, 0.0)
..close();
return path;
}
Path path = Path()
..addPolygon(_generateVerticalWavePath(size), false)
..lineTo(size.width, size.height)
..lineTo(0.0, size.height)
..close();
return path;
}
List<Offset> _generateHorizontalWavePath(Size size) {
final waveList = <Offset>[];
for (int i = -2; i <= size.height.toInt() + 2; i++) {
final waveHeight = (size.width / 20);
final dx = math.sin((animationValue * 360 - i) % 360 * (math.pi / 180)) * waveHeight + (size.width * value);
waveList.add(Offset(dx, i.toDouble()));
}
return waveList;
}
List<Offset> _generateVerticalWavePath(Size size) {
final waveList = <Offset>[];
for (int i = -2; i <= size.width.toInt() + 2; i++) {
final waveHeight = (size.height / 20);
final dy = math.sin((animationValue * 360 - i) % 360 * (math.pi / 180)) * waveHeight + (size.height - (size.height * value));
waveList.add(Offset(i.toDouble(), dy));
}
return waveList;
}
@override
bool shouldReclip(_WaveClipper oldClipper) => animationValue != oldClipper.animationValue;
}

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'package:aitrainer_app/bloc/test_set_execute/test_set_execute_bloc.dart';
import 'package:aitrainer_app/push_notifications.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
@ -35,6 +36,7 @@ import 'package:aitrainer_app/view/test_set_edit.dart';
import 'package:aitrainer_app/view/test_set_execute.dart';
import 'package:aitrainer_app/view/test_set_new.dart';
import 'package:aitrainer_app/widgets/home.dart';
import 'package:aitrainer_app/library/facebook_app_events/facebook_app_events.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_analytics/observer.dart';
import 'package:flurry/flurry.dart';
@ -56,6 +58,7 @@ import 'bloc/menu/menu_bloc.dart';
import 'bloc/session/session_bloc.dart';
import 'bloc/settings/settings_bloc.dart';
import 'bloc/timer/timer_bloc.dart';
import 'model/cache.dart';
const dsn = 'https://5fac40cbfcfb4c15aa80c7a8638d7310@o418565.ingest.sentry.io/5322520';
@ -84,11 +87,11 @@ Future<Null> _reportError(dynamic error, dynamic stackTrace) async {
}
print('Reporting to Sentry.io...');
final sentryId = await Sentry.captureException(
error,
stackTrace: stackTrace,
);
final String customerId = Cache().userLoggedIn != null ? Cache().userLoggedIn!.customerId.toString() : "0";
final String platform = Platform.isAndroid ? "Android" : "iOS";
final String version = Cache().packageInfo != null ? Cache().packageInfo!.version + "+" + Cache().packageInfo!.buildNumber : "";
final sentryId =
await Sentry.captureException(error, stackTrace: stackTrace, hint: "Platform: $platform, Version: $version, User: $customerId");
print('Capture exception result : SentryId : $sentryId');
}
@ -185,10 +188,12 @@ Future<void> initFlurry() async {
}
class WorkoutTestApp extends StatelessWidget {
static final facebookAppEvents = FacebookAppEvents();
@override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
final FirebaseAnalytics analytics = FirebaseAnalytics();
//facebookAppEvents.setAdvertiserTracking(enabled: true);
initFlurry();
PushNotificationsManager().init();
return MaterialApp(

View File

@ -191,6 +191,7 @@ class Cache with Logging {
DateTime savedPlanDate;
savedPlanDate = format.parse(savedPlanDateString);
print("Saved plan: $savedPlanDate");
final DateTime now = DateTime.now();
final DateTime added = savedPlanDate.add(Duration(days: 1));

View File

@ -3,7 +3,7 @@ enum ExerciseAbility { oneRepMax, endurance, running, mini_test_set, paralell_te
extension ExerciseAbilityExt on ExerciseAbility {
String enumToString() => this.toString().split(".").last;
bool equalsTo(ExerciseAbility ability) => this.toString() == ability.toString();
bool equalsStringTo(String ability) => this.toString() == ability;
bool equalsStringTo(String ability) => this.enumToString() == ability;
String get description {
switch (this) {
case ExerciseAbility.endurance:

View File

@ -22,7 +22,14 @@ class ExercisePlanTemplate {
}
if (json['details'] != null && (json['details']).length > 0) {
List details = json['details'];
final List details = json['details'];
details.sort((a, b) {
if (a['sort'] == null || b['sort'] == null) {
return a['exercisePlanTemplateDetailId'] < b['exercisePlanTemplateDetailId'] ? -1 : 1;
} else {
return a['sort'] < b['sort'] ? -1 : 1;
}
});
details.forEach((element) {
exerciseTypes.add(element['exerciseTypeId']);
});

View File

@ -138,4 +138,8 @@ mixin Common {
),
);
}
static normalizeDecimal(String value) {
return value.replaceFirst(",", ".");
}
}

View File

@ -11,7 +11,7 @@ import 'package:aitrainer_app/widgets/dialog_html.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:liquid_progress_indicator/liquid_progress_indicator.dart';
import 'package:aitrainer_app/library/liquid_progress_indicator/liquid_custom_progress_indicator.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import 'package:rainbow_color/rainbow_color.dart';
@ -598,9 +598,9 @@ class _AnimatedLiquidCustomProgressIndicatorState extends State<_AnimatedLiquidC
vsync: this,
duration: Duration(seconds: 3),
)..addListener(() {
setState(() {
/* setState(() {
print("build animation state change");
});
}); */
});
_animationController.forward();
@ -608,7 +608,7 @@ class _AnimatedLiquidCustomProgressIndicatorState extends State<_AnimatedLiquidC
@override
void initState() {
buildAnimation();
//buildAnimation();
super.initState();
}

View File

@ -14,7 +14,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
import '../bloc/customer_change/customer_change_bloc.dart';
import '../library/dropdown_search.dart';
import '../library/dropdown_search/dropdown_search.dart';
// ignore: must_be_immutable
class CustomerFitnessPage extends StatefulWidget {

View File

@ -136,7 +136,7 @@ class _ExercisePlanDetailAddPage extends State<ExercisePlanDetailAddPage> with T
final bool weightVisible = bloc.exercisePlanRepository.getActualPlanDetail()!.exerciseType!.unitQuantityUnit != null;
String summary = bloc.serie!.toStringAsFixed(0) + " x " + bloc.quantity.toStringAsFixed(0);
if (bloc.quantityUnit > 0) {
summary += " x " + bloc.quantityUnit.toStringAsFixed(0) + " kg";
summary += " x " + bloc.quantityUnit.toStringAsFixed(1) + " kg";
}
final String unit = bloc.exercisePlanRepository.getActualPlanDetail()!.exerciseType!.unit;
return Form(
@ -237,11 +237,15 @@ class _ExercisePlanDetailAddPage extends State<ExercisePlanDetailAddPage> with T
),
),
focusNode: _nodeText3,
initialValue: bloc.quantityUnit.toStringAsFixed(0),
initialValue: bloc.quantityUnit.toStringAsFixed(1),
keyboardType: TextInputType.numberWithOptions(decimal: true),
style: GoogleFonts.archivoBlack(fontSize: 60, color: Colors.yellow[200]),
onChanged: (value) => {
if (value.isNotEmpty) {bloc.add(ExercisePlanCustomAddChangeQuantityUnit(quantity: double.parse(value)))}
if (value.isNotEmpty)
{
value = value.replaceFirst(",", "."),
bloc.add(ExercisePlanCustomAddChangeQuantityUnit(quantity: double.parse(value)))
}
})
: Offstage(),

View File

@ -3,6 +3,7 @@ import 'dart:collection';
import 'package:aitrainer_app/bloc/menu/menu_bloc.dart';
import 'package:aitrainer_app/bloc/test_set_edit/test_set_edit_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/util/trans.dart';
@ -10,6 +11,7 @@ import 'package:aitrainer_app/widgets/app_bar.dart';
import 'package:aitrainer_app/widgets/dialog_common.dart';
import 'package:aitrainer_app/widgets/menu_image.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
@ -25,6 +27,7 @@ class TestSetEdit extends StatelessWidget with Trans {
// ignore: close_sinks
final MenuBloc menuBloc = BlocProvider.of<MenuBloc>(context);
late TestSetEditBloc? bloc;
final bool activeExercisePlan = Cache().activeExercisePlan != null;
setContext(context);
return Scaffold(
@ -65,24 +68,39 @@ class TestSetEdit extends StatelessWidget with Trans {
);
}))),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return DialogCommon(
title: "Start!",
descriptions: t("Enjoy the exercises, good luck with the testing!"),
text: "OK",
onTap: () => {
Navigator.of(context).pop(),
if (bloc != null)
{
bloc!.add(TestSetEditSubmit()),
}
},
onCancel: () => {Navigator.of(context).pop()},
);
}),
onPressed: () {
if (activeExercisePlan) {
showCupertinoDialog(
useRootNavigator: true,
context: context,
builder: (_) => CupertinoAlertDialog(
title: Text(t("You have an active Test Set!") + "\n" + Cache().activeExercisePlan!.name),
content: Column(children: [
Divider(),
Text(t("Do you want to override it?"), style: GoogleFonts.inter(color: Colors.black, fontSize: 16)),
]),
actions: [
TextButton(
child: Text(t("No, bring me there"), textAlign: TextAlign.center),
onPressed: () => {
Navigator.pop(context),
Navigator.pop(context),
Navigator.of(context).pushNamed("testSetExecute"),
},
),
TextButton(
child: Text(t("Yes")),
onPressed: () {
Navigator.pop(context);
startTrainingDialog(bloc);
},
)
],
));
} else {
startTrainingDialog(bloc);
}
},
backgroundColor: Colors.orange[800],
icon: Icon(CustomIcon.clock),
label: Text(
@ -93,6 +111,27 @@ class TestSetEdit extends StatelessWidget with Trans {
);
}
void startTrainingDialog(TestSetEditBloc? bloc) {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return DialogCommon(
title: "Start!",
descriptions: t("Enjoy the exercises, good luck with the testing!"),
text: "OK",
onTap: () => {
Navigator.of(context).pop(),
if (bloc != null)
{
bloc.add(TestSetEditSubmit()),
}
},
onCancel: () => {Navigator.of(context).pop()},
);
});
}
Widget getTestSetWidget(TestSetEditBloc bloc, String templateName) {
return Container(
padding: EdgeInsets.only(left: 10, right: 10),
@ -155,97 +194,95 @@ class TestSetEdit extends StatelessWidget with Trans {
]));
}
List<Widget> imageSliders(List<WorkoutMenuTree> alternatives, MenuBloc menuBloc, WorkoutMenuTree workoutTree, TestSetEditBloc bloc) {
List<Widget> imageSliders(int index, WorkoutMenuTree? workoutTree, TestSetEditBloc bloc) {
final List<Widget> list = [];
if (bloc.exercisePlanDetails[workoutTree.exerciseTypeId] == null) {
list.add(Container(
padding: EdgeInsets.only(top: 25, bottom: 25),
child: ClipRRect(
borderRadius: BorderRadius.circular(24.0),
child: Container(
color: Colors.red[600],
child: Center(
child: Text(
"X",
style: GoogleFonts.archivoBlack(
color: Colors.white,
fontSize: 80,
),
),
)))));
list.add(getImageStack(workoutTree, menuBloc, bloc));
} else {
ExerciseType exerciseType = bloc.exercisePlanDetails[workoutTree.exerciseTypeId];
bool deleted = workoutTree == null;
bloc.displayPlanDetails[index].forEach((value) {
ExerciseType alternative = value;
final WorkoutMenuTree actualWorkoutTree = bloc.menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(exerciseType.exerciseTypeId)!;
list.add(getImageStack(actualWorkoutTree, menuBloc, bloc));
}
final WorkoutMenuTree? workoutTreeAlternative =
bloc.menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(alternative.exerciseTypeId);
alternatives.forEach((element) {
if (workoutTree.exerciseTypeId != element.exerciseTypeId) {
list.add(getImageStack(element, menuBloc, bloc));
}
list.add(getImageStack(workoutTreeAlternative!, bloc, index, deleted));
});
return list;
}
Stack getImageStack(WorkoutMenuTree element, MenuBloc menuBloc, TestSetEditBloc bloc) {
Stack getImageStack(WorkoutMenuTree element, TestSetEditBloc bloc, int index, bool deleted) {
return Stack(alignment: Alignment.topRight, children: [
Stack(alignment: Alignment.bottomLeft, children: [
MenuImage(imageName: element.imageName, workoutTreeId: element.id),
deleted
? Opacity(opacity: 0.2, child: MenuImage(imageName: element.imageName, workoutTreeId: element.id))
: MenuImage(imageName: element.imageName, workoutTreeId: element.id),
Container(
padding: EdgeInsets.only(left: 15, bottom: 15, right: 15),
child: Text(
element.name,
maxLines: 4,
style: GoogleFonts.archivoBlack(color: element.color, fontSize: 16, height: 1.1),
style: GoogleFonts.archivoBlack(color: deleted ? element.color.withOpacity(0.2) : element.color, fontSize: 16, height: 1.1),
),
),
]),
Container(
width: 40,
height: 40,
child: ClipRRect(
borderRadius: BorderRadius.circular(24.0),
child: GestureDetector(
onTap: () => bloc.add(TestSetEditDeleteExerciseType(exerciseTypeId: element.exerciseTypeId)),
child: Container(
color: Colors.red[600],
child: Center(
child: Text(
"X",
style: GoogleFonts.archivoBlack(
color: Colors.white,
fontSize: 28,
),
),
)))))
deleted == false
? Container(
width: 40,
height: 40,
child: ClipRRect(
borderRadius: BorderRadius.circular(24.0),
child: GestureDetector(
onTap: () => bloc.add(TestSetEditDeleteExerciseType(indexKey: index)),
child: Container(
color: Colors.red[600],
child: Center(
child: Text(
"X",
style: GoogleFonts.archivoBlack(
color: Colors.white,
fontSize: 28,
),
),
)))))
: Container(
width: 40,
height: 40,
child: ClipRRect(
borderRadius: BorderRadius.circular(24.0),
child: GestureDetector(
onTap: () => bloc.add(TestSetEditAddExerciseType(indexKey: index)),
child: Container(
color: Colors.yellow[700],
child: Center(
child: Icon(
Icons.undo_sharp,
size: 40,
color: Colors.white,
)),
))))
]);
}
List<Widget> getExerciseTypeWidgets(TestSetEditBloc bloc) {
final List<Widget> widgets = [];
bloc.exerciseTypes.forEach((element) {
final WorkoutMenuTree? workoutTree = bloc.menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(element.exerciseTypeId);
final List<WorkoutMenuTree>? alternativeMenuItems = bloc.menuBloc.menuTreeRepository.getWorkoutTreeAlternatives(workoutTree);
if (workoutTree != null && alternativeMenuItems != null) {
final Widget widget = CarouselSlider(
options: CarouselOptions(
viewportFraction: 0.80,
reverse: false,
enableInfiniteScroll: false,
autoPlay: false,
aspectRatio: 2,
enlargeCenterPage: true,
onPageChanged: (index, reason) =>
bloc.add(TestSetEditChangeExerciseType(index: index, exerciseTypeId: element.exerciseTypeId)),
enlargeStrategy: CenterPageEnlargeStrategy.scale),
items: imageSliders(alternativeMenuItems, bloc.menuBloc, workoutTree, bloc),
);
widgets.add(widget);
bloc.exercisePlanDetails.forEach((key, element) {
WorkoutMenuTree? workoutTree;
if (element != null) {
workoutTree = bloc.menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(element.exerciseTypeId);
}
final Widget widget = CarouselSlider(
options: CarouselOptions(
viewportFraction: 0.80,
reverse: false,
enableInfiniteScroll: false,
autoPlay: false,
aspectRatio: 2,
enlargeCenterPage: true,
onPageChanged: (index, reason) => bloc.add(TestSetEditChangeExerciseType(index: index, indexKey: key)),
enlargeStrategy: CenterPageEnlargeStrategy.scale),
items: imageSliders(key, workoutTree, bloc),
);
widgets.add(widget);
});
return widgets;
}

View File

@ -3,7 +3,6 @@ import 'dart:collection';
import 'package:aitrainer_app/bloc/menu/menu_bloc.dart';
import 'package:aitrainer_app/bloc/test_set_execute/test_set_execute_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/library/fade_in.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
@ -32,7 +31,7 @@ class TestSetExecute extends StatelessWidget with Trans {
executeBloc.add(TestSetExecuteLoad());
setContext(context);
return Scaffold(
appBar: AppBarNav(depth: 1),
appBar: AppBarNav(depth: 0),
body: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
@ -349,11 +348,7 @@ class _ExerciseTileState extends State<ExerciseTile> with Trans {
void initState() {
animation.start();
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
setState(() {
print("animation state completed");
});
}
if (status == AnimationStatus.completed) {}
});
super.initState();
@ -407,7 +402,6 @@ class _ExerciseTileState extends State<ExerciseTile> with Trans {
final bool done = state.equalsTo(ExercisePlanDetailState.finished);
final String countSerie = widget.exercisePlanDetail.exercises == null ? "1" : (widget.exercisePlanDetail.exercises!.length).toString();
final String serie = widget.exercisePlanDetail.exerciseType!.unitQuantityUnit == null ? "/1" : "/4";
setContext(context);
return Container(
color: Colors.transparent,

View File

@ -3,7 +3,7 @@ import 'package:aitrainer_app/util/common.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:liquid_progress_indicator/liquid_progress_indicator.dart';
import 'package:aitrainer_app/library/liquid_progress_indicator/liquid_linear_progress_indicator.dart';
class AppBarProgress extends StatefulWidget implements PreferredSizeWidget {
final int max;

View File

@ -258,10 +258,11 @@ class _BMIState extends State<BMI> with Trans {
),
),
initialValue: widget.exerciseBloc.height.toStringAsFixed(0),
keyboardType: TextInputType.numberWithOptions(decimal: true),
keyboardType: TextInputType.numberWithOptions(decimal: false),
textInputAction: TextInputAction.done,
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]),
onChanged: (value) => {widget.exerciseBloc.add(ExerciseNewHeightChange(value: double.parse(value)))}),
onChanged: (value) =>
{value = value.replaceFirst(",", "."), widget.exerciseBloc.add(ExerciseNewHeightChange(value: double.parse(value)))}),
);
} else {
return Container();
@ -294,11 +295,12 @@ class _BMIState extends State<BMI> with Trans {
borderSide: BorderSide(color: Colors.white12, width: 0.4),
),
),
initialValue: widget.exerciseBloc.weight.toStringAsFixed(0),
initialValue: widget.exerciseBloc.weight.toStringAsFixed(1),
keyboardType: TextInputType.numberWithOptions(decimal: true),
textInputAction: TextInputAction.done,
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]),
onChanged: (value) => {widget.exerciseBloc.add(ExerciseNewWeightChange(value: double.parse(value)))},
onChanged: (value) =>
{value = value.replaceFirst(",", "."), widget.exerciseBloc.add(ExerciseNewWeightChange(value: double.parse(value)))},
),
),
IconButton(

View File

@ -3,7 +3,7 @@ import 'package:aitrainer_app/bloc/exercise_new/exercise_new_bloc.dart';
import 'package:aitrainer_app/util/app_localization.dart';
import 'package:aitrainer_app/model/fitness_state.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/library/dropdown_search.dart';
import 'package:aitrainer_app/library/dropdown_search/dropdown_search.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
@ -213,10 +213,11 @@ class _BMRState extends State<BMR> with Trans {
),
),
initialValue: widget.exerciseBloc.height.toStringAsFixed(0),
keyboardType: TextInputType.numberWithOptions(decimal: true),
keyboardType: TextInputType.numberWithOptions(decimal: false),
textInputAction: TextInputAction.done,
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]),
onChanged: (value) => {widget.exerciseBloc.add(ExerciseNewHeightChange(value: double.parse(value)))}),
onChanged: (value) =>
{value = value.replaceFirst(",", "."), widget.exerciseBloc.add(ExerciseNewHeightChange(value: double.parse(value)))}),
);
} else {
return Container();
@ -361,11 +362,12 @@ class _BMRState extends State<BMR> with Trans {
borderSide: BorderSide(color: Colors.white12, width: 0.4),
),
),
initialValue: widget.exerciseBloc.weight.toStringAsFixed(0),
initialValue: widget.exerciseBloc.weight.toStringAsFixed(1),
keyboardType: TextInputType.numberWithOptions(decimal: true),
textInputAction: TextInputAction.done,
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]),
onChanged: (value) => {widget.exerciseBloc.add(ExerciseNewWeightChange(value: double.parse(value)))},
onChanged: (value) =>
{value = value.replaceFirst(",", "."), widget.exerciseBloc.add(ExerciseNewWeightChange(value: double.parse(value)))},
),
),
IconButton(

View File

@ -107,6 +107,7 @@ class _BottomBarMultipleExercisesState extends State<BottomBarMultipleExercises>
}
List<Widget> getChildren(TestSetExecuteBloc bloc) {
print("Isset? ${widget.isSet}");
final List<Widget> list = [];
if (!widget.isSet! && !bloc.hasBegun()) {
list.add(GestureDetector(
@ -174,7 +175,7 @@ class _BottomBarMultipleExercisesState extends State<BottomBarMultipleExercises>
color: Colors.transparent,
),
Text(
ret['message2'],
t(ret['message2']),
style: GoogleFonts.inter(color: Colors.grey[800]),
),
]))),

View File

@ -227,27 +227,6 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
Divider(
color: Colors.transparent,
),
/* TextButton(
onPressed: () {
widget.onSubmit();
/* showDialog(
context: context,
builder: (BuildContext context) {
return Victory(
victory: true,
);
}); */
},
child: Stack(
alignment: Alignment.center,
children: [
Image.asset('asset/icon/gomb_orange_c.png', width: 140, height: 60),
Text(
t("Save"),
style: TextStyle(fontSize: 16, color: Colors.white),
),
],
)), */
]),
)));
}
@ -276,7 +255,10 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
keyboardType: TextInputType.numberWithOptions(decimal: true),
textInputAction: TextInputAction.done,
style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.yellow[300]),
onChanged: (value) => widget.onUnitQuantityChanged!(value)),
onChanged: (value) => {
value = value.replaceFirst(",", "."),
widget.onUnitQuantityChanged!(value),
}),
]));
}
return row;

View File

@ -152,11 +152,11 @@ class _InputDialogState<Event> extends State<InputDialog<Event>> with Trans {
borderSide: BorderSide(color: Colors.white12, width: 0.4),
),
),
initialValue: widget.initialValue.toStringAsFixed(0),
initialValue: widget.initialValue.toStringAsFixed(1),
keyboardType: TextInputType.numberWithOptions(decimal: true),
textInputAction: TextInputAction.done,
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]),
onChanged: (value) => this.inputValue = double.parse(value),
onChanged: (value) => {value = value.replaceFirst(",", "."), this.inputValue = double.parse(value)},
),
),
SizedBox(

View File

@ -279,9 +279,13 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
automaticallyImplyLeading: false,
backgroundColor: Colors.transparent,
title: Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
SizedBox(
width: 10,
),
!activeExercisePlan
? SizedBox(
width: 50,
)
: SizedBox(
width: 10,
),
GestureDetector(
onTap: () => {
showDialog(
@ -318,18 +322,22 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
}
},
),
activeExercisePlan
Cache().activeExercisePlan != null
? GestureDetector(
onTap: () => showDialog(
context: context,
builder: (BuildContext context) {
return DialogCommon(
title: t("You have an active Test Set!"),
descriptions: t("Press OK to continue"),
descriptions: Cache().activeExercisePlan!.name,
description2: t("Press OK to continue"),
text: "OK",
onTap: () => {
Navigator.of(context).pop(),
Navigator.of(context).pushNamed("testSetExecute"),
if (Cache().activeExercisePlan != null)
{
Navigator.of(context).pushNamed("testSetExecute"),
}
},
onCancel: () => {
Navigator.of(context).pop(),
@ -348,6 +356,11 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
);
}))
: Offstage(),
activeExercisePlan
? SizedBox(
width: 10,
)
: Offstage(),
]));
return sliverAppBar;
}

View File

@ -2,8 +2,7 @@ import 'dart:async';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/library/dropdown_search.dart';
//import 'package:aitrainer_app/library/dropdown_search.dart';
import 'package:aitrainer_app/library/dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';

View File

@ -83,7 +83,12 @@ class _SizeState extends State<SizeWidget> with Trans {
.toDouble() -
45,
child: GestureDetector(
onTap: () => onPressed(widget.exerciseBloc.customerRepository.getPropertyByName("Weight")!),
onTap: () => {
if (widget.exerciseBloc.customerRepository.getPropertyByName("Weight") != null)
{
onPressed(widget.exerciseBloc.customerRepository.getPropertyByName("Weight")!),
}
},
child: Image.asset(
"asset/image/merleg.png",
height: 120,
@ -181,7 +186,6 @@ class _SizeState extends State<SizeWidget> with Trans {
}
void onPressed(Property element) {
print(element.propertyName);
showDialog(
context: context,
builder: (context) => InputDialog(

View File

@ -630,13 +630,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.4.0"
liquid_progress_indicator:
dependency: "direct main"
description:
name: liquid_progress_indicator
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.2"
logging:
dependency: transitive
description:
@ -846,7 +839,7 @@ packages:
name: purchases_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
version: "3.2.1"
quiver:
dependency: transitive
description:

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.1.10+65
version: 1.1.13+66
environment:
sdk: ">=2.12.0 <3.0.0"
@ -46,10 +46,8 @@ dependencies:
flutter_html: ^2.0.0-nullsafety.0
wakelock: ^ 0.4.0
timeline_tile: ^2.0.0
purchases_flutter: ^3.1.0
purchases_flutter: ^3.2.1
package_info: ^2.0.0
liquid_progress_indicator: ^0.3.2
#audioplayer: ^0.8.1
ezanimation: ^0.5.0
confetti: ^0.6.0-nullsafety
crypto: ^3.0.0