528 lines
19 KiB
Dart
528 lines
19 KiB
Dart
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/model/cache.dart';
|
|
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
|
|
import 'package:aitrainer_app/repository/exercise_repository.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/menu_image.dart';
|
|
import 'package:aitrainer_app/widgets/victory_widget.dart';
|
|
import 'package:ezanimation/ezanimation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
|
|
import 'package:timeline_tile/timeline_tile.dart';
|
|
|
|
// ignore: must_be_immutable
|
|
class TestSetExecute extends StatelessWidget with Trans {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// ignore: close_sinks
|
|
final MenuBloc menuBloc = BlocProvider.of<MenuBloc>(context);
|
|
// ignore: close_sinks
|
|
TestSetExecuteBloc executeBloc = BlocProvider.of<TestSetExecuteBloc>(context);
|
|
executeBloc.menuBloc = menuBloc;
|
|
|
|
executeBloc.add(TestSetExecuteLoad());
|
|
setContext(context);
|
|
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<TestSetExecuteBloc, TestSetExecuteState>(listener: (context, state) {
|
|
if (state is TestSetExecuteError) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white))));
|
|
} else if (state is TestSetExecuteFinished) {
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: true,
|
|
builder: (BuildContext context) {
|
|
return Victory(
|
|
victory: true,
|
|
);
|
|
});
|
|
}
|
|
}, builder: (context, state) {
|
|
executeBloc = BlocProvider.of<TestSetExecuteBloc>(context);
|
|
return ModalProgressHUD(
|
|
child: getExercises(executeBloc, context),
|
|
inAsyncCall: state is TestSetExecuteLoading,
|
|
opacity: 0.5,
|
|
color: Colors.black54,
|
|
progressIndicator: CircularProgressIndicator(),
|
|
);
|
|
}),
|
|
),
|
|
floatingActionButton: FloatingActionButton.extended(
|
|
onPressed: () => executeBloc.getNext() != null
|
|
? executeExercise(executeBloc, executeBloc.getNext()!, context)
|
|
: Navigator.of(context).pushNamed('home'),
|
|
backgroundColor: Colors.orange[800],
|
|
icon: Icon(CustomIcon.weight_hanging),
|
|
label: Text(
|
|
t("Next"),
|
|
style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 16),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget getExercises(TestSetExecuteBloc bloc, BuildContext context) {
|
|
return CustomScrollView(slivers: [
|
|
SliverList(delegate: SliverChildListDelegate(getTiles(bloc, context))),
|
|
]);
|
|
}
|
|
|
|
List<Widget> getTiles(TestSetExecuteBloc bloc, BuildContext context) {
|
|
List<Widget> tiles = [];
|
|
tiles.add(getStartTile(bloc));
|
|
tiles.addAll(getExerciseTiles(bloc, context));
|
|
tiles.add(getEndTile());
|
|
return tiles;
|
|
}
|
|
|
|
Widget getStartTile(TestSetExecuteBloc bloc) {
|
|
return TimelineTile(
|
|
alignment: TimelineAlign.manual,
|
|
lineXY: 0.1,
|
|
isFirst: true,
|
|
afterLineStyle: const LineStyle(
|
|
color: Colors.orange,
|
|
thickness: 6,
|
|
),
|
|
indicatorStyle: IndicatorStyle(
|
|
width: 40,
|
|
color: Colors.orange,
|
|
padding: const EdgeInsets.all(8),
|
|
iconStyle: IconStyle(
|
|
color: Colors.white,
|
|
iconData: Icons.insert_emoticon,
|
|
),
|
|
),
|
|
endChild: Container(
|
|
padding: EdgeInsets.only(top: 30),
|
|
constraints: const BoxConstraints(
|
|
minHeight: 120,
|
|
),
|
|
color: Colors.transparent,
|
|
child: RichText(
|
|
text: TextSpan(
|
|
style: GoogleFonts.inter(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
children: [
|
|
TextSpan(
|
|
text: bloc.isFirst() ? t("Start") : t("Continue"),
|
|
style: GoogleFonts.inter(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
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,
|
|
),
|
|
],
|
|
)),
|
|
TextSpan(
|
|
text: t(" your ") + t(bloc.testType),
|
|
style: GoogleFonts.inter(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
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,
|
|
),
|
|
],
|
|
)),
|
|
TextSpan(
|
|
text: "\n",
|
|
style: GoogleFonts.inter(
|
|
fontSize: 16,
|
|
color: Colors.white,
|
|
)),
|
|
TextSpan(
|
|
text: bloc.testName == null ? "" : bloc.testName,
|
|
style: GoogleFonts.inter(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
)),
|
|
TextSpan(
|
|
text: t("\nyour plan is available for 24 hours"),
|
|
style: GoogleFonts.inter(
|
|
fontSize: 16,
|
|
color: Colors.white,
|
|
))
|
|
])),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget getEndTile() {
|
|
return Container(
|
|
color: Colors.transparent,
|
|
child: TimelineTile(
|
|
alignment: TimelineAlign.manual,
|
|
lineXY: 0.1,
|
|
isLast: true,
|
|
beforeLineStyle: const LineStyle(
|
|
color: Colors.orange,
|
|
thickness: 6,
|
|
),
|
|
indicatorStyle: IndicatorStyle(
|
|
width: 40,
|
|
color: Colors.orange,
|
|
padding: const EdgeInsets.all(8),
|
|
iconStyle: IconStyle(
|
|
color: Colors.white,
|
|
iconData: Icons.thumb_up,
|
|
),
|
|
),
|
|
endChild: Container(
|
|
padding: EdgeInsets.only(top: 50),
|
|
constraints: const BoxConstraints(
|
|
minHeight: 120,
|
|
),
|
|
color: Colors.transparent,
|
|
child: RichText(
|
|
text: TextSpan(
|
|
style: GoogleFonts.inter(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
children: [
|
|
TextSpan(
|
|
text: "Finish!",
|
|
style: GoogleFonts.inter(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
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,
|
|
),
|
|
],
|
|
)),
|
|
])),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
List<Widget> getExerciseTiles(TestSetExecuteBloc bloc, BuildContext context) {
|
|
List<Widget> tiles = [];
|
|
if (bloc.exercisePlanDetails != null) {
|
|
bloc.exercisePlanDetails!.forEach((element) {
|
|
//if (element != null && element.exerciseTypeId != null) {
|
|
tiles.add(GestureDetector(
|
|
onDoubleTap: () => print("Execute ${element.exerciseType!.nameTranslation}"),
|
|
onTap: () => element.state.equalsTo(ExercisePlanDetailState.finished)
|
|
? evaluation(bloc, element)
|
|
: executeExercise(bloc, element, context),
|
|
child: ExerciseTile(
|
|
bloc: bloc,
|
|
exercisePlanDetail: element,
|
|
)));
|
|
//}
|
|
});
|
|
}
|
|
|
|
return tiles;
|
|
}
|
|
|
|
void evaluation(TestSetExecuteBloc bloc, ExercisePlanDetail planDetail) {
|
|
if (planDetail.exercises != null && planDetail.exercises!.isNotEmpty) {
|
|
ExerciseRepository exerciseRepository = ExerciseRepository();
|
|
exerciseRepository.start = planDetail.exercises![0].dateAdd;
|
|
exerciseRepository.exerciseList = Cache().getExercises();
|
|
exerciseRepository.actualExerciseList = planDetail.exercises!;
|
|
print("actualEx ${exerciseRepository.actualExerciseList}");
|
|
LinkedHashMap args = LinkedHashMap();
|
|
args['exerciseRepository'] = exerciseRepository;
|
|
args['exercise'] = planDetail.exercises![0];
|
|
args['past'] = true;
|
|
Navigator.of(context).pushNamed('evaluationPage', arguments: args);
|
|
}
|
|
}
|
|
|
|
void executeExercise(TestSetExecuteBloc bloc, ExercisePlanDetail exercisePlanDetail, BuildContext context) {
|
|
ExercisePlanDetail? next = bloc.getNext();
|
|
|
|
if (next != null) {
|
|
final HashMap args = HashMap();
|
|
args['exerciseType'] = next.exerciseType;
|
|
args['exercisePlanDetailId'] = exercisePlanDetail.exercisePlanDetailId;
|
|
args['testSetExecuteBloc'] = bloc;
|
|
String title = "";
|
|
String description = "";
|
|
String description2 = "";
|
|
if (next.exerciseTypeId != exercisePlanDetail.exerciseTypeId) {
|
|
title = t("Stop!");
|
|
description = t("Please continue with the next exercise in the queue:") + next.exerciseType!.nameTranslation;
|
|
description2 = t("Or, you can redifine this exercise queue in the Compact Test menu");
|
|
} else {
|
|
if (exercisePlanDetail.state.equalsTo(ExercisePlanDetailState.inProgress)) {
|
|
final HashMap args = HashMap();
|
|
args['exerciseType'] = exercisePlanDetail.exerciseType;
|
|
args['exercisePlanDetail'] = exercisePlanDetail;
|
|
args['testSetExecuteBloc'] = bloc;
|
|
Navigator.of(context).pushNamed('testSetControl', arguments: args);
|
|
} else {
|
|
Navigator.of(context).pushNamed('testSetNew', arguments: args);
|
|
}
|
|
return;
|
|
}
|
|
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (BuildContext context) {
|
|
return DialogCommon(
|
|
title: title,
|
|
descriptions: description,
|
|
description2: description2,
|
|
text: "OK",
|
|
onTap: () => {Navigator.of(context).pop()},
|
|
onCancel: () => {Navigator.of(context).pop()},
|
|
);
|
|
});
|
|
} else {
|
|
Navigator.of(context).pushNamed('home');
|
|
}
|
|
}
|
|
}
|
|
|
|
// ignore: must_be_immutable
|
|
class ExerciseTile extends StatefulWidget {
|
|
final TestSetExecuteBloc bloc;
|
|
final ExercisePlanDetail exercisePlanDetail;
|
|
|
|
ExerciseTile({required this.bloc, required this.exercisePlanDetail});
|
|
|
|
@override
|
|
_ExerciseTileState createState() => _ExerciseTileState();
|
|
}
|
|
|
|
class _ExerciseTileState extends State<ExerciseTile> with Trans {
|
|
final EzAnimation animation = EzAnimation(1.0, 30.0, Duration(seconds: 3), reverseCurve: Curves.easeIn);
|
|
|
|
@override
|
|
void initState() {
|
|
animation.start();
|
|
animation.addStatusListener((status) {
|
|
if (status == AnimationStatus.completed) {}
|
|
});
|
|
|
|
super.initState();
|
|
}
|
|
|
|
@override
|
|
bool didUpdateWidget(ExerciseTile oldWidget) {
|
|
super.didUpdateWidget(oldWidget);
|
|
animation.start();
|
|
return true;
|
|
}
|
|
|
|
Widget getIndicator(ExercisePlanDetailState state) {
|
|
ExercisePlanDetail? next = widget.bloc.getNext();
|
|
bool actual = false;
|
|
if (next != null) {
|
|
if (next.exerciseTypeId == widget.exercisePlanDetail.exerciseTypeId) {
|
|
actual = true;
|
|
}
|
|
}
|
|
if (state.equalsTo(ExercisePlanDetailState.inProgress)) {
|
|
return ClipRRect(
|
|
borderRadius: BorderRadius.circular(24.0),
|
|
child: Container(
|
|
color: actual ? Colors.green : Colors.orange,
|
|
child: Icon(
|
|
CustomIcon.calendar_2,
|
|
size: 28,
|
|
color: Colors.white,
|
|
)));
|
|
} else if (state.equalsTo(ExercisePlanDetailState.finished)) {
|
|
return ClipRRect(
|
|
borderRadius: BorderRadius.circular(24.0),
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: Icon(
|
|
CustomIcon.ok_circled,
|
|
size: 40,
|
|
color: Colors.green,
|
|
)));
|
|
} else {
|
|
return Image.asset(
|
|
"asset/image/pict_reps_volumen_db.png",
|
|
);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final ExercisePlanDetailState state = widget.exercisePlanDetail.state;
|
|
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,
|
|
child: TimelineTile(
|
|
alignment: TimelineAlign.manual,
|
|
lineXY: 0.1,
|
|
beforeLineStyle: const LineStyle(
|
|
color: Color(0xffb4f500),
|
|
thickness: 6,
|
|
),
|
|
afterLineStyle: const LineStyle(
|
|
color: Color(0xffb4f500),
|
|
thickness: 6,
|
|
),
|
|
indicatorStyle: IndicatorStyle(
|
|
width: 40,
|
|
height: 40,
|
|
indicator: getIndicator(state),
|
|
),
|
|
endChild: Container(
|
|
padding: EdgeInsets.only(left: 10),
|
|
child: Row(children: [
|
|
Container(
|
|
width: 120,
|
|
height: 80,
|
|
child: MenuImage(
|
|
imageName: widget.bloc.getActualImageName(widget.exercisePlanDetail.exerciseType!.exerciseTypeId),
|
|
workoutTreeId: widget.bloc.getActualWorkoutTreeId(widget.exercisePlanDetail.exerciseType!.exerciseTypeId)!,
|
|
)),
|
|
SizedBox(
|
|
width: 10,
|
|
),
|
|
Expanded(
|
|
child: RichText(
|
|
text: TextSpan(
|
|
style: GoogleFonts.inter(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.bold,
|
|
color: done ? Colors.grey[400] : Colors.white,
|
|
),
|
|
children: [
|
|
TextSpan(
|
|
text: widget.exercisePlanDetail.exerciseType!.nameTranslation,
|
|
style: GoogleFonts.inter(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.bold,
|
|
color: done ? Colors.grey[400] : Colors.orange[500],
|
|
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,
|
|
),
|
|
],
|
|
)),
|
|
widget.exercisePlanDetail.exerciseType!.unitQuantityUnit != null
|
|
? TextSpan(
|
|
text: "\n",
|
|
)
|
|
: TextSpan(),
|
|
widget.exercisePlanDetail.exerciseType!.unitQuantityUnit != null
|
|
? TextSpan(
|
|
text: t(widget.exercisePlanDetail.exerciseType!.unitQuantityUnit!) + ": ",
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold))
|
|
: TextSpan(),
|
|
widget.exercisePlanDetail.exerciseType!.unitQuantityUnit != null
|
|
? TextSpan(
|
|
text: t(widget.bloc.getExerciseWeight(widget.exercisePlanDetail)),
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
))
|
|
: TextSpan(),
|
|
TextSpan(
|
|
text: "\n",
|
|
),
|
|
TextSpan(
|
|
text: t(widget.exercisePlanDetail.exerciseType!.unit) + ": ",
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold)),
|
|
TextSpan(
|
|
text: widget.bloc.repeatTimesText(widget.exercisePlanDetail),
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
)),
|
|
TextSpan(
|
|
text: "\n",
|
|
),
|
|
TextSpan(
|
|
text: t("Set") + ": ",
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold)),
|
|
TextSpan(
|
|
text: countSerie + serie,
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
)),
|
|
]),
|
|
)),
|
|
done
|
|
? AnimatedBuilder(
|
|
animation: animation,
|
|
builder: (context, snapshot) {
|
|
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
|
|
Image.asset(
|
|
"asset/image/kupa.png",
|
|
width: animation.value,
|
|
),
|
|
Text("Result", style: GoogleFonts.inter(fontSize: 10, color: Colors.white)),
|
|
]);
|
|
})
|
|
: Offstage()
|
|
]),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|