import 'dart:collection'; import 'package:aitrainer_app/bloc/exercise_control/exercise_control_bloc.dart'; import 'package:aitrainer_app/bloc/timer/timer_bloc.dart'; import 'package:aitrainer_app/bloc/tutorial/tutorial_bloc.dart'; import 'package:aitrainer_app/library/custom_icon_icons.dart'; import 'package:aitrainer_app/util/app_language.dart'; import 'package:aitrainer_app/model/cache.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_html.dart'; import 'package:aitrainer_app/widgets/number_picker.dart'; import 'package:aitrainer_app/widgets/timer_widget.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; class ExerciseControlPage extends StatefulWidget { _ExerciseControlPage createState() => _ExerciseControlPage(); } class _ExerciseControlPage extends State with Trans { final ScrollController _controller = ScrollController(); double offset = 0; @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { LinkedHashMap arguments = ModalRoute.of(context)!.settings.arguments as LinkedHashMap; final ExerciseRepository exerciseRepository = arguments['exerciseRepository']; final bool readonly = arguments['readonly']; setContext(context); // ignore: close_sinks TimerBloc timerBloc = BlocProvider.of(context); return BlocProvider( create: (context) => ExerciseControlBloc(exerciseRepository: exerciseRepository, readonly: readonly, timerBloc: timerBloc), //..add(ExerciseControlLoad()), child: BlocConsumer(listener: (context, state) { if (state is ExerciseControlError) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); } }, builder: (context, state) { final exerciseBloc = BlocProvider.of(context); if (state is ExerciseControlReady) { _controller.animateTo(exerciseBloc.scrollOffset, duration: Duration(milliseconds: 300), curve: Curves.easeIn); } return ModalProgressHUD( child: getControlForm(exerciseBloc, timerBloc), inAsyncCall: state is ExerciseControlLoading, opacity: 0.5, color: Colors.black54, progressIndicator: CircularProgressIndicator(), ); })); } Form getControlForm(ExerciseControlBloc exerciseBloc, TimerBloc timerBloc) { this.offset = exerciseBloc.scrollOffset; String exerciseName = AppLanguage().appLocal == Locale("en") ? exerciseBloc.exerciseRepository.exerciseType!.name : exerciseBloc.exerciseRepository.exerciseType!.nameTranslation; return Form( child: Scaffold( resizeToAvoidBottomInset: true, appBar: AppBarNav(depth: 1), body: Container( height: double.infinity, width: double.infinity, alignment: Alignment.center, decoration: BoxDecoration( image: DecorationImage( image: Cache().userLoggedIn!.sex == "m" ? AssetImage("asset/image/WT_Results_for_men.jpg") : AssetImage("asset/image/WT_Results_for_female.jpg"), fit: BoxFit.cover, alignment: Alignment.topCenter, ), ), child: Stack(children: [ Container( padding: const EdgeInsets.only(top: 10, left: 25, right: 25), child: SingleChildScrollView( scrollDirection: Axis.vertical, controller: _controller, child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( exerciseName, style: GoogleFonts.archivoBlack( fontWeight: FontWeight.bold, fontSize: 24, color: Colors.white, shadows: [ Shadow( offset: Offset(2.0, 2.0), blurRadius: 6.0, color: Colors.black54, ), Shadow( offset: Offset(-3.0, 3.0), blurRadius: 12.0, color: Colors.black54, ), ], ), overflow: TextOverflow.fade, textAlign: TextAlign.center, maxLines: 2, softWrap: true, ), Divider( color: Colors.transparent, ), Divider( color: Colors.transparent, ), Divider( color: Colors.transparent, ), Divider( color: Colors.transparent, ), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Text(t("Your 1RM:"), style: GoogleFonts.inter( color: Colors.yellow[300], fontSize: 18, shadows: [ Shadow( offset: Offset(-2.0, -2.0), blurRadius: 12.0, color: Colors.black54, ), Shadow( offset: Offset(-3.0, 3.0), blurRadius: 12.0, color: Colors.black54, ), ], )), Text( " " + exerciseBloc.initialRM.toStringAsFixed(0) + " " + exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit!, style: GoogleFonts.inter( color: Colors.yellow[300], fontSize: 18, fontWeight: FontWeight.bold, shadows: [ Shadow( offset: Offset(-2.0, -2.0), blurRadius: 6.0, color: Colors.black54, ), Shadow( offset: Offset(-3.0, 3.0), blurRadius: 6.0, color: Colors.black54, ), ], ), ), SizedBox(width: 10), GestureDetector( onTap: () => { showDialog( context: context, builder: (BuildContext context) { return DialogHTML( title: t("OneRepMax"), htmlData: t("OneRepMax_desc"), ); }) }, child: Icon(CustomIcon.question, color: Colors.yellow[300])) ], ), Divider(), Row(mainAxisAlignment: MainAxisAlignment.start, children: [ Flexible( child: Text(t("Why do you need Exercise Control?"), style: GoogleFonts.inter( color: Colors.yellow[300], fontSize: 18, shadows: [ Shadow( offset: Offset(2.0, 2.0), blurRadius: 6.0, color: Colors.black54, ), Shadow( offset: Offset(-3.0, 3.0), blurRadius: 12.0, color: Colors.black54, ), ], ))), SizedBox(width: 10), GestureDetector( onTap: () => { showDialog( context: context, builder: (BuildContext context) { return DialogHTML( title: t("Control Exercise:"), htmlData: t("controlexercise_desc"), ); }) }, child: Icon(CustomIcon.question, color: Colors.yellow[300])) ]), Divider(), numberPickForm(exerciseBloc, 1), Divider(), numberPickForm(exerciseBloc, 2), Divider(), numberPickForm(exerciseBloc, 3), SizedBox( height: 80, ) ]), )), TimerWidget( bloc: timerBloc, ), ])), //bottomNavigationBar: BottomNavigator(bottomNavIndex: 1), ), ); } List getButton(int step, ExerciseControlBloc exerciseBloc) { List widgets = []; if (step < exerciseBloc.step) { widgets.add(Icon( CustomIcon.check_circle, color: Color(0xffb4f500), size: 36, )); } else { widgets.add(Icon( CustomIcon.question, color: Colors.grey[700], size: 36, )); } return widgets; } Widget numberPickForm(ExerciseControlBloc exerciseBloc, int step) { final String strTimes = step == 2 ? exerciseBloc.origQuantity.toStringAsFixed(0) : "max."; //print("step $step, quantity ${exerciseBloc.origQuantity}"); String title = (step + 1).toString() + "/4 " + t("Control Exercise:"); LinkedHashMap args = LinkedHashMap(); final TutorialBloc tutorialBloc = BlocProvider.of(context); List listWidgets = [ Text( title, style: GoogleFonts.inter(color: Colors.yellow[300], fontSize: 18, fontWeight: FontWeight.bold), ), GestureDetector( onTap: () => showDialog( context: context, builder: (BuildContext context) { return UnitQuantityControl( exerciseBloc: exerciseBloc, step: step, ); }), child: RichText( text: TextSpan( style: GoogleFonts.inter( fontSize: 16, fontWeight: FontWeight.normal, color: Colors.yellow[300], ), children: [ TextSpan(text: t("Please repeat with ")), TextSpan( text: exerciseBloc.unitQuantity.toStringAsFixed(0) + " " + exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit!, style: GoogleFonts.inter( decoration: TextDecoration.underline, fontSize: 16, fontWeight: FontWeight.bold, color: Colors.yellow[100], ), ), TextSpan(text: t("hu_with") + " "), TextSpan( text: strTimes + " ", style: GoogleFonts.archivoBlack( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.yellow[100], )), TextSpan( text: t( "times!", )), ]), )), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ NumberPickerWidget( minValue: 0, maxValue: 200, initalValue: exerciseBloc.origQuantity.round(), unit: t("reps"), color: Colors.yellow[50]!, onChange: (value) => {exerciseBloc.add(ExerciseControlQuantityChange(quantity: value.toDouble(), step: step))}), TextButton( style: TextButton.styleFrom( padding: EdgeInsets.all(0), primary: Colors.white, onSurface: Colors.blueAccent, ), onPressed: () { if (tutorialBloc.isActive) { if (!tutorialBloc.checkAction("Save")) { return; } } exerciseBloc.add(ExerciseControlSubmit(step: step)); if (step == 3) { Navigator.of(context).pop(); args['exerciseRepository'] = exerciseBloc.exerciseRepository; Navigator.of(context).pushNamed('evaluationPage', arguments: args); } }, child: step == exerciseBloc.step ? 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), ), ], ) : Stack( alignment: Alignment.center, children: getButton(step, exerciseBloc), )), ], ), ]; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: listWidgets, ); } } class UnitQuantityControl extends StatefulWidget { final ExerciseControlBloc exerciseBloc; final int step; const UnitQuantityControl({required this.exerciseBloc, required this.step}); @override _UnitQuantityControlState createState() => _UnitQuantityControlState(); } class _UnitQuantityControlState extends State with Trans { double? changedValue; @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: [ 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_results_background.jpg'), fit: BoxFit.cover, alignment: Alignment.center, ), ), child: Column(mainAxisSize: MainAxisSize.min, children: [ Text( t("Change the weight to"), textAlign: TextAlign.center, style: GoogleFonts.archivoBlack( fontSize: 24, color: Colors.yellow[100], shadows: [ 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: 20, ), NumberPickerWidget( minValue: (widget.exerciseBloc.unitQuantity - 30).round(), maxValue: (widget.exerciseBloc.unitQuantity + 30).round(), initalValue: widget.exerciseBloc.unitQuantity.round(), unit: t("kg"), color: Colors.yellow[50]!, onChange: (value) => { setState(() { changedValue = value; }) }), Align( alignment: Alignment.center, child: GestureDetector( onTap: () => { if (changedValue == null) { changedValue = widget.exerciseBloc.unitQuantity, }, //print("Changed new value $changedValue"), widget.exerciseBloc.add(ExerciseControlUnitQuantityChange(quantity: changedValue!.toDouble(), step: widget.step)), Navigator.of(context).pop(), }, child: Stack( alignment: Alignment.center, children: [ Image.asset('asset/icon/gomb_orange_c.png', width: 100, height: 45), Text( t("OK"), style: TextStyle(fontSize: 16, color: Colors.white), ), ], ))), ])), GestureDetector( onTap: () => Navigator.of(context).pop(), child: CircleAvatar( backgroundColor: Colors.transparent, radius: 28, child: Text( "X", style: GoogleFonts.archivoBlack(fontSize: 32, color: Colors.white54), ), )), ]); } }