import 'dart:collection'; import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; import 'package:aitrainer_app/bloc/training_plan/training_plan_bloc.dart'; import 'package:aitrainer_app/library/custom_icon_icons.dart'; import 'package:aitrainer_app/library/tree_view.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/training_plan.dart'; import 'package:aitrainer_app/model/training_plan_detail.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; import 'package:aitrainer_app/util/app_language.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/dialog_common.dart'; import 'package:aitrainer_app/widgets/dialog_premium.dart'; import 'package:aitrainer_app/widgets/menu_image.dart'; import 'package:aitrainer_app/widgets/treeview_parent_widget.dart'; import 'package:badges/badges.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:syncfusion_flutter_datagrid/datagrid.dart'; // ignore: must_be_immutable class TrainingPlanActivatePage extends StatelessWidget with Trans { late String parentName; TrainingPlanActivatePage(); @override Widget build(BuildContext context) { setContext(context); final HashMap args = ModalRoute.of(context)!.settings.arguments as HashMap; parentName = args['parentName']; return Scaffold( appBar: AppBarNav( depth: 0, ), body: Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( image: DecorationImage( image: AssetImage('asset/image/WT_black_background.jpg'), fit: BoxFit.cover, alignment: Alignment.center, ), ), child: BlocConsumer( listener: (context, state) { if (state is TrainingPlanError) { showDialog( context: context, builder: (BuildContext context) { return DialogCommon( warning: true, title: t("Warning"), descriptions: t(state.message), text: "OK", onTap: () => Navigator.of(context).pop(), onCancel: () => { Navigator.of(context).pop(), }, ); }); } else if (state is TrainingPlanFinished) { Navigator.of(context).pop(); Navigator.of(context).pushNamed("myTrainingPlanExecute"); } }, builder: (context, state) { final TrainingPlanBloc bloc = BlocProvider.of(context); return ModalProgressHUD( child: getPlans(bloc), inAsyncCall: state is TrainingPlanLoading, opacity: 0.5, color: Colors.black54, progressIndicator: CircularProgressIndicator(), ); }, ), ), ); } Widget getPlans(TrainingPlanBloc bloc) { return TreeView( startExpanded: false, children: _getTreeChildren(bloc), ); } List _getTreeChildren(TrainingPlanBloc bloc) { final List plans = bloc.trainingPlanRepository.getPlansByParent(parentName); String parentDescription = ""; String parentTitle = ""; if (bloc.trainingPlanRepository.parentTree != null) { parentTitle = AppLanguage().appLocal.toString() == "en" ? bloc.trainingPlanRepository.parentTree!.name : bloc.trainingPlanRepository.parentTree!.nameTranslation; if (bloc.trainingPlanRepository.parentTree!.description != null) { parentDescription = bloc.trainingPlanRepository.parentTree!.descriptionTranslation != null ? AppLanguage().appLocal.toString() == "en" ? bloc.trainingPlanRepository.parentTree!.description! : bloc.trainingPlanRepository.parentTree!.descriptionTranslation! : ""; } } List listWidget = []; Card explanation = Card( color: Colors.white60, child: Container( padding: EdgeInsets.only(left: 10, right: 5, top: 12, bottom: 8), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Row( children: [ Icon( Icons.info, color: Colors.orangeAccent, ), Text(" "), Flexible( child: Text( parentTitle, maxLines: 2, style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), )), ], ), Divider( color: Colors.transparent, ), Html( data: parentDescription, //Optional parameters: style: { "p": Style( color: Colors.black, fontSize: FontSize(12), padding: const EdgeInsets.only(left: 20, right: 8, bottom: 4), ), "strong": Style( color: Colors.indigo, fontWeight: FontWeight.bold, fontSize: FontSize(14), ), "h3": Style( color: Colors.yellow[600], fontSize: FontSize(16), textAlign: TextAlign.center, padding: const EdgeInsets.all(12), ), "li": Style( color: Colors.white, fontSize: FontSize(14), padding: const EdgeInsets.only(left: 20, bottom: 10, right: 8), //before: "*", display: Display.LIST_ITEM), "h2": Style( color: Colors.yellow[600], fontWeight: FontWeight.bold, fontSize: FontSize(24), textAlign: TextAlign.center, //padding: const EdgeInsets.all(4), ), "h1": Style( color: Colors.yellow[400], fontWeight: FontWeight.bold, fontSize: FontSize.larger, alignment: Alignment.center, padding: const EdgeInsets.all(4), ), }, ), // ], ))); listWidget.add(explanation); plans.forEach((element) { bool restricted = (!element.free); listWidget.add(Container( margin: const EdgeInsets.only(left: 4.0), child: Badge( elevation: 0, padding: EdgeInsets.all(0), position: BadgePosition.topStart(top: -15, start: -18), animationDuration: Duration(milliseconds: 1500), animationType: BadgeAnimationType.fade, badgeColor: Colors.transparent, showBadge: restricted, badgeContent: IconButton( iconSize: 36, onPressed: () => showDialog( context: context, builder: (BuildContext context) { return DialogCommon( title: t("Premium function"), descriptions: t("This is a premium function, you can reach it only with a valid subscription"), onCancel: () => Navigator.of(context).pop(), onTap: () => Navigator.of(context).pop(), text: '', ); }), icon: Icon( Icons.star, color: Colors.orange[400], )), child: TreeViewChild( startExpanded: false, parent: TreeviewParentWidget( text: element.nameTranslations[AppLanguage().appLocal.toString()] != null ? element.nameTranslations[AppLanguage().appLocal.toString()]! : element.name, fontSize: 18, icon: Icon(Icons.list_sharp), color: Colors.blue[800], ), children: _getDays(element, bloc), )))); }); return listWidget; } List _getDays(TrainingPlan plan, TrainingPlanBloc bloc) { List listWidget = []; bloc.setMyTrainingPlan(plan); bloc.activateTrainingPlanDays(); bloc.trainingPlanDayNames.forEach((value) { listWidget.add(Container( margin: const EdgeInsets.only(left: 4.0), child: TreeViewChild( startExpanded: true, parent: TreeviewParentWidget( text: " * " + t("Training Day") + ": " + value, fontSize: 16, color: Colors.white, icon: Icon(Icons.view_day), backgroundColor: Colors.white24, ), children: _getChildList(plan, bloc, value), ))); }); return listWidget; } List _getChildList(TrainingPlan plan, TrainingPlanBloc bloc, String dayName) { List list = []; bool restricted = (!plan.free && !Cache().hasPurchased); list.add(Card( margin: EdgeInsets.only(left: 10, top: 5), color: Colors.white24, child: Container( padding: EdgeInsets.only(left: 10, right: 10), child: Column(children: [ Html( data: plan.descriptionTranslations[AppLanguage().appLocal.toString()] != null ? plan.descriptionTranslations[AppLanguage().appLocal.toString()]! : plan.description, //Optional parameters: style: { "p": Style( color: Colors.black, padding: const EdgeInsets.all(4), ), "li": Style( color: Colors.white, //padding: const EdgeInsets.all(4), ), "h2": Style( color: Colors.black, fontWeight: FontWeight.bold, fontSize: FontSize.larger, //padding: const EdgeInsets.all(4), ), "h1": Style( color: Colors.black, fontWeight: FontWeight.bold, fontSize: FontSize.larger, alignment: Alignment.center, padding: const EdgeInsets.all(4), ), }, ), plan.details == null || plan.details!.isEmpty ? Container( padding: EdgeInsets.only(bottom: 8), child: Text( t("Soon! Check back later for the plan details"), style: GoogleFonts.inter( color: Colors.orange[800], shadows: [ Shadow( offset: Offset(2.0, 2.0), blurRadius: 3.0, color: Colors.black54, ), ], ), )) : Offstage(), getPlanDetails(plan, bloc, dayName), ElevatedButton( style: ElevatedButton.styleFrom( foregroundColor: Colors.white, backgroundColor: restricted ? Colors.grey[600] : Colors.orange, ), child: Text(t("Start")), onPressed: () { if (Cache().userLoggedIn == null) { showDialog( context: context, builder: (BuildContext context) { return DialogCommon( warning: true, title: t("Warning"), descriptions: t("Please log in"), description2: t("because only that way can we generated the training plan for you."), text: "OK", onTap: () => Navigator.of(context).popAndPushNamed("login"), onCancel: () => { Navigator.of(context).pop(), }, ); }); } else { if (restricted) { showDialog( context: context, builder: (BuildContext context) { return DialogPremium( unlocked: Cache().hasPurchased, unlockRound: 1, unlockedText: t("Enjoy also this premium feature") + " " + t("to activate all available training programs."), function: "Training Programs", onTap: () => {Navigator.of(context).pop()}, onCancel: () => {Navigator.of(context).pop()}, ); }); } else { activate(plan, bloc); } } }, ), /* restricted ? Container( padding: EdgeInsets.only(bottom: 8), child: Text( t("This is a premium function"), style: GoogleFonts.inter( color: Colors.deepOrange[800], fontWeight: FontWeight.bold, shadows: [ Shadow( offset: Offset(2.0, 2.0), blurRadius: 4.0, color: Colors.black54, ), ], ), ), ) : Offstage(), */ ]), ))); return list; } void activate(TrainingPlan plan, TrainingPlanBloc bloc) { if (plan.details == null || plan.details!.isEmpty) { return; } if (Cache().myTrainingPlan != null) { showCupertinoDialog( useRootNavigator: true, context: context, builder: (_) => CupertinoAlertDialog( title: Text(t("You have an active Training Plan")), content: Column(children: [ Divider(), Text( t("Do you want to override it with"), style: (TextStyle(color: Colors.blue)), ), Text( plan.nameTranslations[AppLanguage().appLocal.toString()]! + "?", style: (TextStyle(color: Colors.blue[800], fontWeight: FontWeight.bold)), ), ]), actions: [ TextButton( child: Text(t("No")), onPressed: () => Navigator.pop(context), ), TextButton( child: Text(t("Yes")), onPressed: () { Navigator.pop(context); bloc.add(TrainingPlanActivate(trainingPlanId: plan.trainingPlanId)); }, ) ], )); } else { bloc.add(TrainingPlanActivate(trainingPlanId: plan.trainingPlanId)); } } Widget getPlanDetails(TrainingPlan plan, TrainingPlanBloc bloc, String dayName) { return SfDataGrid( headerRowHeight: 30, rowHeight: 70, columnWidthMode: ColumnWidthMode.lastColumnFill, defaultColumnWidth: 50, source: TrainingPlanDetailSource( plan: plan, menuBloc: bloc.menuBloc, bloc: bloc, dayName: dayName, onWeightTap: () => { showDialog( context: context, builder: (BuildContext context) { return DialogCommon( title: t("Calculated Weight"), descriptions: t("The weight is based on your previuos tests - if they exist."), description2: t("If it does not exist, your very first exercise will be a test."), text: "OK", onTap: () => { Navigator.of(context).pop(), }, onCancel: () => { Navigator.of(context).pop(), }, ); }) }, onRepeatTap: () => { showDialog( context: context, builder: (BuildContext context) { return DialogCommon( title: t("Maximum repeats"), descriptions: t("During the exercise you should try your best:"), description2: t("do it with the possible maximum repeats."), text: "OK", onTap: () => { Navigator.of(context).pop(), }, onCancel: () => { Navigator.of(context).pop(), }, ); }), }, onDropsetTap: () => { showDialog( context: context, builder: (BuildContext context) { return DialogCommon( title: t("Dropset"), descriptions: t("A drop set is an advanced resistance training technique "), description2: t(" in which you focus on completing a set until failure - or the inability to do another repetition."), text: "OK", onTap: () => { Navigator.of(context).pop(), }, onCancel: () => { Navigator.of(context).pop(), }, ); }) }), headerGridLinesVisibility: GridLinesVisibility.both, gridLinesVisibility: GridLinesVisibility.both, columns: [ GridColumn( columnWidthMode: ColumnWidthMode.lastColumnFill, maximumWidth: 130, columnName: 'exerciseImage', label: Container( color: Colors.black38, padding: EdgeInsets.only(left: 8.0), alignment: Alignment.centerLeft, child: Text( t('Exercise'), style: GoogleFonts.inter( color: Colors.white, fontWeight: FontWeight.bold, ), textAlign: TextAlign.start, overflow: TextOverflow.ellipsis, ))), GridColumn( maximumWidth: 0, visible: false, columnName: 'exerciseName', label: Container( color: Colors.green[50], padding: EdgeInsets.symmetric(horizontal: 8.0), alignment: Alignment.centerLeft, child: Text( t('Exercise'), textAlign: TextAlign.start, overflow: TextOverflow.ellipsis, ))), GridColumn( maximumWidth: 60, columnName: 'Set', label: Container( color: Colors.black38, padding: EdgeInsets.symmetric(horizontal: 2.0), alignment: Alignment.centerLeft, child: Text( t('Set'), style: GoogleFonts.inter(color: Colors.white, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, ))), GridColumn( maximumWidth: 100, columnWidthMode: ColumnWidthMode.fill, columnName: 'Repeats', label: Container( color: Colors.black38, padding: EdgeInsets.symmetric(horizontal: 2.0), alignment: Alignment.centerLeft, child: Text( t('Repeats'), style: GoogleFonts.inter(color: Colors.white, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, ))), GridColumn( maximumWidth: 60, columnName: 'Weight', label: Container( color: Colors.black38, padding: EdgeInsets.symmetric(horizontal: 2.0), alignment: Alignment.centerLeft, child: Text( t('Weight_'), style: GoogleFonts.inter(color: Colors.white, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, ))), ], ); } } class TrainingPlanDetailSource extends DataGridSource { final TrainingPlan plan; final TrainingPlanBloc bloc; final MenuBloc menuBloc; final dayName; final VoidCallback onDropsetTap; final VoidCallback onWeightTap; final VoidCallback onRepeatTap; TrainingPlanDetailSource({ required this.plan, required this.menuBloc, required this.bloc, required this.dayName, required this.onDropsetTap, required this.onWeightTap, required this.onRepeatTap, }) { if (plan.details != null) { List details = bloc.trainingPlanDetailSummary(plan, dayName); dataGridRows = details.map((dataGridRow) { WorkoutMenuTree? menuTree = menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(dataGridRow.exerciseTypeId); if (menuTree == null) { return DataGridRow(cells: []); } return DataGridRow(cells: [ DataGridCell( columnName: 'exerciseImage', value: MenuImage( imageName: menuTree.imageName, workoutTreeId: menuTree.id, radius: 8, )), DataGridCell(columnName: 'exerciseName', value: menuTree.name), DataGridCell(columnName: 'set', value: dataGridRow.set), DataGridCell(columnName: 'reps', value: dataGridRow.summary!), DataGridCell(columnName: 'weight', value: dataGridRow.weight), ]); }).toList(); } } List dataGridRows = []; @override List get rows => dataGridRows; @override DataGridRowAdapter? buildRow(DataGridRow row) { if (row.getCells().isEmpty) { return null; } String name = row.getCells()[1].value.toString(); return DataGridRowAdapter( color: Colors.white12, cells: row.getCells().map((dataGridCell) { return Container( alignment: dataGridCell.columnName == "exerciseImage" ? Alignment.centerLeft : Alignment.centerLeft, padding: EdgeInsets.only(top: 2, bottom: 2, left: 4, right: 4), child: dataGridCell.columnName == "exerciseImage" ? Stack(alignment: AlignmentDirectional.bottomStart, children: [ dataGridCell.value, Container( padding: EdgeInsets.only(left: 4, right: 8, bottom: 3), child: Text( name, maxLines: 3, overflow: TextOverflow.ellipsis, style: GoogleFonts.inter(fontSize: 12, color: Colors.white, fontWeight: FontWeight.bold, shadows: [ Shadow( offset: Offset(2.0, 2.0), blurRadius: 6.0, color: Colors.black, ), ]), )) ]) : dataGridCell.columnName == "weight" && (dataGridCell.value == -1 || dataGridCell.value == -2) ? GestureDetector( onTap: () { onWeightTap(); }, child: Icon( CustomIcon.question_circle, color: Colors.white, )) : dataGridCell.columnName == "weight" && dataGridCell.value == -3 ? GestureDetector( onTap: () { onDropsetTap(); }, child: Icon( CustomIcon.question_circle, color: Colors.orange[400], )) : dataGridCell.columnName == "reps" && dataGridCell.value == -1 ? GestureDetector( onTap: () { onRepeatTap(); }, child: Icon( CustomIcon.question_circle, color: Colors.white, )) : Text(dataGridCell.value.toString(), style: GoogleFonts.inter( fontSize: 14, color: Colors.white, fontWeight: FontWeight.bold, ))); }).toList()); } }