workouttest_app/lib/view/training_plan_activate_page.dart
2021-07-05 22:10:32 +02:00

619 lines
24 KiB
Dart

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/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:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_html/style.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<String, dynamic> args = ModalRoute.of(context)!.settings.arguments as HashMap<String, dynamic>;
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<TrainingPlanBloc, TrainingPlanState>(
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).pushNamed("login"),
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<TrainingPlanBloc>(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<Widget> _getTreeChildren(TrainingPlanBloc bloc) {
final List<TrainingPlan> 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<Widget> 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) {
listWidget.add(Container(
margin: const EdgeInsets.only(left: 4.0),
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: _getChildList(element, bloc),
)));
});
return listWidget;
}
List<Widget> _getChildList(TrainingPlan plan, TrainingPlanBloc bloc) {
List<Widget> list = [];
bool restricted = (!plan.free && !Cache().hasPurchased);
list.add(Card(
margin: EdgeInsets.only(left: 10, top: 5),
color: Colors.white60,
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>[
Shadow(
offset: Offset(2.0, 2.0),
blurRadius: 3.0,
color: Colors.black54,
),
],
),
))
: Offstage(),
getPlanDetails(plan, bloc),
ElevatedButton(
style: ElevatedButton.styleFrom(
onPrimary: Colors.white,
primary: 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>[
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) {
return SfDataGrid(
headerRowHeight: 30,
rowHeight: 60,
source: TrainingPlanDetailSource(
plan: plan,
menuBloc: bloc.menuBloc,
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: [
GridTextColumn(
columnWidthMode: ColumnWidthMode.lastColumnFill,
maximumWidth: 160,
columnName: 'exerciseImage',
label: Container(
color: Colors.green[50],
padding: EdgeInsets.only(left: 8.0),
alignment: Alignment.centerLeft,
child: Text(
t('Exercise'),
textAlign: TextAlign.start,
overflow: TextOverflow.ellipsis,
))),
GridTextColumn(
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,
))),
GridTextColumn(
maximumWidth: 40,
columnName: 'set',
label: Container(
color: Colors.green[50],
padding: EdgeInsets.symmetric(horizontal: 2.0),
alignment: Alignment.centerLeft,
child: Text(
t('Set'),
overflow: TextOverflow.ellipsis,
))),
GridTextColumn(
maximumWidth: 50,
columnName: 'repeats',
label: Container(
color: Colors.green[50],
padding: EdgeInsets.symmetric(horizontal: 2.0),
alignment: Alignment.centerLeft,
child: Text(
t('Reps'),
overflow: TextOverflow.ellipsis,
))),
GridTextColumn(
maximumWidth: 60,
columnName: 'weight',
label: Container(
color: Colors.green[50],
padding: EdgeInsets.symmetric(horizontal: 2.0),
alignment: Alignment.centerLeft,
child: Text(
t('Weight'),
overflow: TextOverflow.ellipsis,
))),
GridTextColumn(
maximumWidth: 50,
columnName: 'day',
label: Container(
color: Colors.green[50],
padding: EdgeInsets.symmetric(horizontal: 8.0),
alignment: Alignment.centerLeft,
child: Text(
t('Day'),
overflow: TextOverflow.ellipsis,
))),
],
);
}
}
class TrainingPlanDetailSource extends DataGridSource {
final TrainingPlan plan;
final MenuBloc menuBloc;
final VoidCallback onDropsetTap;
final VoidCallback onWeightTap;
final VoidCallback onRepeatTap;
TrainingPlanDetailSource({
required this.plan,
required this.menuBloc,
required this.onDropsetTap,
required this.onWeightTap,
required this.onRepeatTap,
}) {
if (plan.details != null) {
dataGridRows = plan.details!.map((dataGridRow) {
WorkoutMenuTree? menuTree = menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(dataGridRow.exerciseTypeId);
if (menuTree == null) {
return DataGridRow(cells: []);
}
return DataGridRow(cells: [
DataGridCell<Widget>(
columnName: 'exerciseImage',
value: MenuImage(
imageName: menuTree.imageName,
workoutTreeId: menuTree.id,
radius: 8,
)),
DataGridCell<String>(columnName: 'exerciseName', value: menuTree.name),
DataGridCell<int>(columnName: 'set', value: dataGridRow.set),
DataGridCell<int>(columnName: 'reps', value: dataGridRow.repeats),
DataGridCell<double>(columnName: 'weight', value: dataGridRow.weight),
DataGridCell<String>(columnName: 'day', value: dataGridRow.day),
]);
}).toList();
}
}
List<DataGridRow> dataGridRows = [];
@override
List<DataGridRow> get rows => dataGridRows;
@override
DataGridRowAdapter? buildRow(DataGridRow row) {
if (row.getCells().isEmpty) {
return null;
}
String name = row.getCells()[1].value;
return DataGridRowAdapter(
color: Colors.white60,
cells: row.getCells().map<Widget>((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: 10, color: Colors.yellow[600], fontWeight: FontWeight.bold, shadows: <Shadow>[
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.indigo[300],
))
: 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.indigo[600],
))
: Text(dataGridCell.value.toString(),
style: GoogleFonts.inter(
fontSize: 14,
color: Colors.indigo,
fontWeight: FontWeight.bold,
)));
}).toList());
}
}