WT 1.1.22 reengineering: Training Plan execution, registration

This commit is contained in:
bossanyit
2021-08-25 01:05:03 +02:00
parent 6b25fdfe0e
commit cebd83648b
51 changed files with 2605 additions and 663 deletions
+170
View File
@@ -0,0 +1,170 @@
import 'package:aitrainer_app/util/trans.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
// ignore: must_be_immutable
class DialogTrialWidget extends StatefulWidget {
final String title, description;
final Widget widget;
final VoidCallback onTap;
final VoidCallback? onCancel;
DialogTrialWidget({Key? key, required this.title, required this.description, required this.widget, required this.onTap, this.onCancel})
: super(key: key);
@override
_DialogTrialWidgetState createState() {
return _DialogTrialWidgetState();
}
}
class _DialogTrialWidgetState extends State<DialogTrialWidget> with Trans {
@override
Widget build(BuildContext context) {
setContext(context);
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(31),
),
elevation: 0,
backgroundColor: Colors.transparent,
child: contentBox(context),
);
}
contentBox(context) {
return Stack(alignment: AlignmentDirectional.topStart, children: [
Stack(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 20, top: 24, right: 20, bottom: 30),
margin: EdgeInsets.only(top: 30),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
boxShadow: [BoxShadow(color: Colors.black, offset: Offset(0, 10), blurRadius: 10)],
image: DecorationImage(
image: AssetImage('asset/image/WT_black_G_background.jpg'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
height: 5,
),
Stack(
alignment: AlignmentDirectional.topEnd,
children: [
Text(
widget.title,
textAlign: TextAlign.center,
style: GoogleFonts.archivoBlack(
fontSize: 20,
color: Colors.yellow[400],
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,
),
],
),
),
],
),
SizedBox(
height: 35,
),
Text(
widget.description,
style: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.bold,
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,
),
],
),
textAlign: TextAlign.center,
),
SizedBox(
height: 15,
),
widget.widget,
SizedBox(
height: 52,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: widget.onCancel,
child: Stack(
alignment: Alignment.center,
children: [
Image.asset('asset/icon/gomb_lila_b.png', width: 100, height: 45),
Text(
t("Nem"),
style: TextStyle(fontSize: 16, color: Colors.white),
),
],
)),
GestureDetector(
onTap: widget.onTap,
child: Stack(
alignment: Alignment.center,
children: [
Image.asset('asset/icon/gomb_orange_c.png', width: 100, height: 45),
Text(
t("Igen"),
style: TextStyle(fontSize: 16, color: Colors.white),
),
],
))
],
),
],
),
),
],
),
GestureDetector(
onTap: () {
if (widget.onCancel == null) {
Navigator.of(context).pop();
} else {
widget.onCancel!();
}
},
child: CircleAvatar(
backgroundColor: Colors.transparent,
radius: 28,
child: Text(
"X",
style: GoogleFonts.archivoBlack(fontSize: 32, color: Colors.white54),
),
)),
]);
}
@override
void dispose() {
super.dispose();
}
}
+2 -2
View File
@@ -98,7 +98,7 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
}
SchedulerBinding.instance!.addPostFrameCallback((_) {
final TutorialBloc bloc = BlocProvider.of<TutorialBloc>(context);
/* //final TutorialBloc bloc = BlocProvider.of<TutorialBloc>(context);
if (bloc.actualCheck == "directTest") {
Timer(
Duration(milliseconds: 2000),
@@ -116,7 +116,7 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
);
})
});
}
} */
});
}
+13 -2
View File
@@ -4,10 +4,11 @@ import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/util/app_language.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/view/customer_goal_page.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:aitrainer_app/widgets/dialog_common.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
@@ -37,6 +38,15 @@ class _HomePageState extends State<AitrainerHome> with Logging, Trans {
SchedulerBinding.instance!.addPostFrameCallback((_) {
runDelayedEvent();
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('-- FirebaseMessaging: Got a message whilst in the foreground!');
print('-- FirebaseMessaging: Message data: ${message.data}');
if (message.notification != null) {
print('-- FirebaseMessaging: Message also contained a notification: ${message.notification}');
}
});
}
Future runDelayedEvent() async {
@@ -58,6 +68,7 @@ class _HomePageState extends State<AitrainerHome> with Logging, Trans {
final appcastURL = "https://raw.githubusercontent.com/bossanyit/appcast/main/android_rss.xml";
final cfg = AppcastConfiguration(url: appcastURL, supportedOS: ['android']);
print("Packageinfo ${Cache().packageInfo}");
return Scaffold(
key: _scaffoldKey,
body: UpgradeAlert(
@@ -101,7 +112,7 @@ class _HomePageState extends State<AitrainerHome> with Logging, Trans {
if (Cache().startPage == 'login') {
return LoginPage();
} else if (Cache().startPage == 'registration') {
return RegistrationPage();
return CustomerGoalPage();
} else {
return MenuPage(parent: 0);
}
+25 -1
View File
@@ -3,16 +3,19 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:aitrainer_app/library/image_cache.dart' as wt;
import 'package:aitrainer_app/library/transparent_image.dart';
import 'package:flutter_html/shims/dart_ui_real.dart';
// ignore: must_be_immutable
class MenuImage extends StatelessWidget {
final int? workoutTreeId;
final String imageName;
bool filter;
double radius;
MenuImage({
required this.workoutTreeId,
required this.imageName,
this.radius = 24,
this.filter = false,
});
@override
@@ -23,10 +26,16 @@ class MenuImage extends StatelessWidget {
String? imageString = this.getImage(workoutTreeId!, imageName);
Widget? widget;
if (imageString != null) {
print("MemoryImage $workoutTreeId - $imageName");
widget = ClipRRect(
borderRadius: BorderRadius.circular(24.0),
child: Container(
color: Colors.transparent,
padding: EdgeInsets.zero,
color: Colors.black38,
foregroundDecoration: BoxDecoration(
color: Colors.black38,
backgroundBlendMode: BlendMode.darken,
),
child: FadeInImage(
fadeInDuration: Duration(milliseconds: 100),
image: MemoryImage(base64Decode(imageString)),
@@ -35,10 +44,18 @@ class MenuImage extends StatelessWidget {
));
} else {
if (imageName.contains("https")) {
print("https image $workoutTreeId - $imageName");
if (!wt.ImageCache().existsImageInMap(workoutTreeId!, imageName)) {
widget = ClipRRect(
borderRadius: BorderRadius.circular(radius),
child: Container(
padding: EdgeInsets.zero,
foregroundDecoration: filter
? BoxDecoration(
color: Colors.black38,
backgroundBlendMode: BlendMode.darken,
)
: null,
color: Colors.transparent,
child: FadeInImage(
fadeInDuration: Duration(milliseconds: 500),
@@ -51,7 +68,14 @@ class MenuImage extends StatelessWidget {
widget = ClipRRect(
borderRadius: BorderRadius.circular(radius),
child: Container(
padding: EdgeInsets.zero,
color: Colors.transparent,
foregroundDecoration: filter
? BoxDecoration(
color: Colors.black38,
backgroundBlendMode: BlendMode.darken,
)
: null,
child: Image.asset(imageName),
));
}
+107 -39
View File
@@ -1,11 +1,12 @@
import 'dart:collection';
import 'dart:ui';
import 'package:aitrainer_app/bloc/training_plan/training_plan_bloc.dart';
import 'package:aitrainer_app/bloc/tutorial/tutorial_bloc.dart';
import 'package:aitrainer_app/model/exercise_ability.dart';
import 'package:aitrainer_app/bloc/menu/menu_bloc.dart';
import 'package:aitrainer_app/repository/training_plan_repository.dart';
import 'package:aitrainer_app/util/enums.dart';
import 'package:aitrainer_app/util/track.dart';
import 'package:aitrainer_app/widgets/dialog_trial.dart';
import 'package:aitrainer_app/widgets/menu_image.dart';
import 'package:aitrainer_app/widgets/menu_search_bar.dart';
import 'package:aitrainer_app/util/app_language.dart';
@@ -15,9 +16,9 @@ import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/dialog_common.dart';
import 'package:aitrainer_app/widgets/tutorial_widget.dart';
import 'package:badges/badges.dart';
import 'package:ezanimation/ezanimation.dart';
import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
@@ -39,7 +40,7 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
final double baseWidth = 312;
final double baseHeight = 675.2;
late MenuBloc menuBloc;
late TutorialBloc tutorialBloc;
//late TutorialBloc tutorialBloc;
final scrollController = ScrollController();
final bool activeExercisePlan = Cache().activeExercisePlan != null;
final EzAnimation animation = EzAnimation(35.0, 10.0, Duration(seconds: 2), reverseCurve: Curves.linear);
@@ -59,41 +60,11 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
/// We require the initializers to run after the loading screen is rendered
SchedulerBinding.instance!.addPostFrameCallback((_) {
menuBloc.add(MenuCreate());
//runDelayedEvent();
});
super.initState();
}
Future runDelayedEvent() async {
bool isFirst = false;
await Future.delayed(Duration(milliseconds: 600), () async {
if (Cache().userLoggedIn != null) {
if (Cache().userLoggedIn!.sex == "m") {
tutorialBloc.tutorialName = ActivityDone.tutorialBasicChestPress.toStr();
} else {
tutorialBloc.tutorialName = ActivityDone.tutorialBasicLegPress.toStr();
}
}
if (!tutorialBloc.isTutorialDone()) {
if (tutorialBloc.isActive == false && tutorialBloc.canActivate) {
tutorialBloc.canActivate = true;
tutorialBloc.isActive = true;
tutorialBloc.menuBloc = menuBloc;
tutorialBloc.add(TutorialLoad());
tutorialBloc.init();
isFirst = true;
}
}
});
final bool canActivate = tutorialBloc.activateTutorial();
if (canActivate) {
if (!isFirst) {
TutorialWidget().tip(context);
}
}
}
@override
bool didUpdateWidget(MenuPageWidget oldWidget) {
super.didUpdateWidget(oldWidget);
@@ -102,10 +73,68 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
return true;
}
Future runDelayedEvent() async {
await Future.delayed(Duration(milliseconds: 3000), () async {
if (Cache().userLoggedIn != null) {
await initDynamicLinks();
}
if (Cache().canTrial()) {
showDialog(
context: context,
builder: (BuildContext context) {
return DialogTrialWidget(
title: "10 days Premium for free",
description: "Would you like to try all premium functions for 10 days, without any subscription or bank card data?",
widget: Column(children: [
Text(
"If you click to 'Yes', all premium functions will be available right now.",
style: GoogleFonts.inter(color: Colors.white),
),
Divider(),
Text(
"If you click to 'No', you can use all basic functions, and you will loose the oppurtunity to try the premium functions for free.",
style: GoogleFonts.inter(color: Colors.white),
),
]),
onCancel: () => {
Navigator.of(context).pop(),
menuBloc.add(MenuStartTrial(start: DateTime.parse("1900-01-01 00:00:00"))),
},
onTap: () => {Navigator.of(context).pop(), menuBloc.add(MenuStartTrial(start: DateTime.now()))},
);
});
}
});
}
Future<void> initDynamicLinks() async {
FirebaseDynamicLinks.instance.onLink(onSuccess: (PendingDynamicLinkData? dynamicLink) async {
final Uri? deepLink = dynamicLink?.link;
print("DeepLink: $deepLink");
if (deepLink != null) {
// ignore: unawaited_futures
final String deepLinkPath = deepLink.path.replaceFirst("/", "");
Navigator.pushNamed(context, deepLinkPath);
}
}, onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
});
final PendingDynamicLinkData? data = await FirebaseDynamicLinks.instance.getInitialLink();
final Uri? deepLink = data?.link;
print("Pending DeepLink: $deepLink");
if (deepLink != null) {
// ignore: unawaited_futures
final String deepLinkPath = deepLink.path.replaceFirst("/", "");
Navigator.pushNamed(context, deepLinkPath);
}
}
@override
Widget build(BuildContext context) {
menuBloc = BlocProvider.of<MenuBloc>(context);
tutorialBloc = BlocProvider.of<TutorialBloc>(context);
//tutorialBloc = BlocProvider.of<TutorialBloc>(context);
setContext(context);
double cWidth = MediaQuery.of(context).size.width;
double cHeight = MediaQuery.of(context).size.height;
@@ -181,6 +210,10 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
});
}
_columnChildren.add(SizedBox(
height: 50,
));
SliverList sliverList = SliverList(
delegate: SliverChildListDelegate(_columnChildren),
);
@@ -292,7 +325,7 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
}
},
),
Cache().myTrainingPlan != null
/* Cache().myTrainingPlan != null
? GestureDetector(
onTap: () => showDialog(
context: context,
@@ -326,7 +359,7 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
),
);
}))
: Offstage(),
: Offstage(), */
/* activeExercisePlan
? SizedBox(
width: 10,
@@ -376,24 +409,59 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
}
void menuClick(WorkoutMenuTree workoutTree, MenuBloc menuBloc) {
if (tutorialBloc.isActive) {
/* if (tutorialBloc.isActive) {
final String checkText = workoutTree.nameEnglish;
if (!tutorialBloc.checkAction(checkText)) {
return;
}
}
} */
print("ability: ${menuBloc.ability} tree: $workoutTree parent: ${workoutTree.parent}");
if (workoutTree.child == false) {
if (menuBloc.ability != null && ExerciseAbility.mini_test_set.equalsTo(menuBloc.ability!) && workoutTree.parent != 0) {
HashMap args = HashMap();
args['templateName'] = workoutTree.nameEnglish;
args['templateNameTranslation'] = workoutTree.name;
Navigator.of(context).pushNamed('testSetEdit', arguments: args);
menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id));
} else if (menuBloc.ability != null && ExerciseAbility.training.equalsTo(menuBloc.ability!) && workoutTree.parent != 0) {
HashMap<String, dynamic> args = HashMap();
args['parentName'] = workoutTree.internalName;
Navigator.of(context).pushNamed("myTrainingPlanActivate", arguments: args);
menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id));
} else if (workoutTree.internalName == "training_execute") {
/* Cache().myTrainingPlan = null;
Cache().deleteMyTrainingPlan(); */
if (Cache().myTrainingPlan != null) {
final TrainingPlanBloc bloc = BlocProvider.of<TrainingPlanBloc>(context);
bloc.setMyPlan(Cache().myTrainingPlan);
Navigator.of(context).pushNamed("myTrainingPlanExecute");
} else {
showDialog(
context: context,
builder: (BuildContext context) {
return DialogCommon(
title: t("No selected Training Plan"),
descriptions: t("Based on your initial data, we will generate the personalized training plan for you."),
text: "OK",
onTap: () {
TrainingPlanRepository trainingPlanRepository = TrainingPlanRepository();
trainingPlanRepository.generateTrainingPlan();
final TrainingPlanBloc bloc = BlocProvider.of<TrainingPlanBloc>(context);
bloc.setMyPlan(Cache().myTrainingPlan);
Future.delayed(Duration(milliseconds: 1000), () async {
Navigator.of(context).pushNamed("myTrainingPlanExecute");
});
},
onCancel: () => {
Navigator.of(context).pop(),
},
);
});
}
} else {
menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id));
}
menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id));
} else {
menuBloc.add(MenuClickExercise(exerciseTypeId: workoutTree.id));
+14 -3
View File
@@ -11,6 +11,9 @@ class NumberPickerWidget extends StatefulWidget {
final String unit;
final Color color;
double? fontSize;
double? diameterRatio;
double? itemExtent;
FontWeight? fontWeight;
NumberPickerWidget(
{Key? key,
@@ -19,10 +22,16 @@ class NumberPickerWidget extends StatefulWidget {
required this.initalValue,
required this.unit,
this.fontSize,
this.diameterRatio,
this.itemExtent,
this.fontWeight,
required this.color,
required this.onChange})
: super(key: key) {
fontSize = fontSize ?? 20;
diameterRatio = diameterRatio ?? 0.85;
itemExtent = itemExtent ?? 30;
fontWeight = fontWeight ?? FontWeight.normal;
}
@override
_NumberPickerWidgetState createState() => _NumberPickerWidgetState();
@@ -59,7 +68,7 @@ class _NumberPickerWidgetState extends State<NumberPickerWidget> with Trans {
scrollController: _scrollController,
useMagnifier: true,
magnification: 1.2,
diameterRatio: 0.85,
diameterRatio: widget.diameterRatio!,
backgroundColor: Colors.transparent,
selectionOverlay: Container(),
onSelectedItemChanged: (x) {
@@ -72,8 +81,10 @@ class _NumberPickerWidgetState extends State<NumberPickerWidget> with Trans {
widget.onChange(value);
},
children: List.generate(
widget.maxValue, (index) => Text('$index ' + widget.unit, style: TextStyle(color: widget.color, fontSize: widget.fontSize))),
itemExtent: 30,
widget.maxValue,
(index) => Text('$index ' + widget.unit,
style: TextStyle(color: widget.color, fontSize: widget.fontSize, fontWeight: widget.fontWeight))),
itemExtent: widget.itemExtent!,
);
}