import 'dart:async'; import 'package:aitrainer_app/bloc/tutorial/tutorial_bloc.dart'; import 'package:aitrainer_app/library/custom_icon_icons.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/time_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; import 'package:keyboard_actions/keyboard_actions_config.dart'; import 'package:stop_watch_timer/stop_watch_timer.dart'; import 'package:wakelock/wakelock.dart'; import 'dialog_common.dart'; import 'dialog_html.dart'; // ignore: must_be_immutable class ExerciseSave extends StatefulWidget { final ValueChanged onQuantityChanged; final ValueChanged? onUnitQuantityChanged; final VoidCallback? onSubmit; final bool hasUnitQuantity; final String? unitQuantityUnit; final String unit; final String exerciseName; final String exerciseDescription; final String exerciseTask; final int exerciseTypeId; final double? weight; final int? repeats; final int? set; final int? exerciseNr; final int? originalQuantity; ExerciseSave( {required this.onQuantityChanged, this.onUnitQuantityChanged, this.onSubmit, required this.hasUnitQuantity, this.unitQuantityUnit, required this.unit, required this.exerciseName, required this.exerciseDescription, required this.exerciseTask, required this.exerciseTypeId, this.weight, this.repeats, this.set, this.exerciseNr, this.originalQuantity}); @override _ExerciseSaveState createState() => _ExerciseSaveState(); } class _ExerciseSaveState extends State with Trans { final FocusNode _nodeText1 = FocusNode(); final FocusNode _nodeText2 = FocusNode(); final _controller1 = TextEditingController(); final _controller2 = TextEditingController(); final StopWatchTimer stopWatchTimer = StopWatchTimer( isLapHours: false, ); @override Widget build(BuildContext context) { setContext(context); return getExerciseWidget(); } @override initState() { super.initState(); _controller1.text = widget.weight == null ? "0" : widget.weight! % widget.weight!.round() == 0 ? widget.weight!.toStringAsFixed(0) : widget.weight!.toStringAsFixed(1); _controller2.text = widget.repeats == null ? "12" : widget.repeats!.toStringAsFixed(0); _nodeText1.addListener(() { if (_nodeText1.hasFocus) { _controller1.selection = TextSelection(baseOffset: 0, extentOffset: _controller1.text.length); } }); _nodeText2.addListener(() { if (_nodeText2.hasFocus) { _controller2.selection = TextSelection(baseOffset: 0, extentOffset: _controller2.text.length); } }); if (widget.unit == "second") { stopWatchTimer.rawTime.listen((value) => { if (value > 0) { widget.onQuantityChanged((value / 1000)), } }); } SchedulerBinding.instance!.addPostFrameCallback((_) { final TutorialBloc bloc = BlocProvider.of(context); if (bloc.actualCheck == "directTest") { Timer( Duration(milliseconds: 2000), () => { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return DialogCommon( title: t("Attention"), descriptions: t(widget.exerciseTask), text: "OK", onTap: () => Navigator.of(context).pop(), onCancel: () => Navigator.of(context).pop(), ); }) }); } }); } @override dispose() { _controller1.dispose(); stopWatchTimer.dispose(); super.dispose(); } KeyboardActionsConfig _buildConfig(BuildContext context) { return KeyboardActionsConfig( keyboardActionsPlatform: KeyboardActionsPlatform.ALL, keyboardBarColor: Colors.grey[200], keyboardSeparatorColor: Colors.black26, nextFocus: true, actions: [ KeyboardActionsItem(focusNode: _nodeText2, toolbarButtons: [ (node) { return GestureDetector( onTap: () => node.unfocus(), child: Container( padding: EdgeInsets.all(8.0), color: Colors.orange[500], child: Text( t("Done"), style: TextStyle(color: Colors.white), ), ), ); } ]), KeyboardActionsItem( focusNode: _nodeText1, toolbarButtons: [ //button 2 (node) { return GestureDetector( onTap: () => node.unfocus(), child: Container( color: Colors.orange, padding: EdgeInsets.all(8.0), child: Text( t("Done"), style: TextStyle(color: Colors.white), ), ), ); } ], ), ], ); } String getTimeGoal(int? originalQuantity) { if (originalQuantity == null) { return ""; } else if (originalQuantity == -1) { return t("Goal") + ": Max"; } else { int mins = (originalQuantity / 60).floor(); int secs = originalQuantity % 60; String seconds = ""; if (secs > 0) { seconds = secs.toStringAsFixed(0) + " " + t("secs"); } return t("Goal") + ": " + mins.toStringAsFixed(0) + " " + t("mins") + " " + seconds; } } Widget getExerciseWidget() { return KeyboardActions( config: _buildConfig(context), child: Container( child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Column(mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( widget.exerciseName, style: GoogleFonts.archivoBlack( fontWeight: FontWeight.bold, fontSize: 24, color: Colors.white, 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, ), ], ), overflow: TextOverflow.fade, maxLines: 4, softWrap: true, textAlign: TextAlign.center, ), SizedBox( height: 15, ), InkWell( child: Text( t("Exercise descripton") + " ยป", style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[200]), ), onTap: () => { showDialog( context: context, builder: (BuildContext context) { return DialogHTML(title: widget.exerciseName, htmlData: '

' + widget.exerciseDescription + '

'); }) }, ), Divider( color: Colors.transparent, ), Text( t(widget.exerciseTask), style: GoogleFonts.inter( fontSize: 14, color: Colors.orange, 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: 12.0, color: Colors.black54, ), ], ), maxLines: 3, textAlign: TextAlign.center, overflow: TextOverflow.fade, softWrap: true, ), Divider( color: Colors.transparent, ), widget.unit == "second" ? Text( getTimeGoal(widget.originalQuantity), style: GoogleFonts.inter( fontSize: 24, color: Colors.yellow[400], 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: 12.0, color: Colors.black54, ), ], ), textAlign: TextAlign.center, ) : Offstage(), columnQuantityUnit(), Divider( color: Colors.transparent, ), columnQuantity(), Divider( color: Colors.transparent, ), widget.hasUnitQuantity ? Text( widget.set == null || widget.exerciseNr == null ? t("Step") + ": " + "1/4" : t("Step") + ": " + "${widget.exerciseNr}/${widget.set}", style: GoogleFonts.inter( fontSize: 22, color: Colors.white, fontWeight: FontWeight.bold, ), maxLines: 3, textAlign: TextAlign.center, overflow: TextOverflow.fade, softWrap: true, ) : Offstage(), Divider( color: Colors.transparent, ), Divider( color: Colors.transparent, ), ]), ))); } Widget columnQuantityUnit() { Widget row = Padding(padding: const EdgeInsets.only(top: 10, left: 55, right: 55), child: Column()); if (widget.hasUnitQuantity) { row = Padding( padding: const EdgeInsets.only(top: 10, left: 55, right: 55), child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ TextFormField( focusNode: _nodeText1, controller: _controller1, decoration: InputDecoration( contentPadding: EdgeInsets.only(left: 25, top: 5, bottom: 5), labelText: t(widget.unitQuantityUnit!), labelStyle: GoogleFonts.inter(fontSize: 20, color: Colors.yellow[50]), fillColor: Colors.black38, filled: true, border: OutlineInputBorder( gapPadding: 8.0, borderRadius: BorderRadius.circular(12.0), borderSide: BorderSide(color: Colors.white12, width: 0.4), ), ), keyboardType: TextInputType.numberWithOptions(decimal: true), textInputAction: TextInputAction.done, style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.yellow[300]), onChanged: (value) { if (value.isNotEmpty) { value = value.replaceFirst(",", "."); value = value.replaceAll(RegExp(r'[^0-9.]'), ""); widget.onUnitQuantityChanged!(double.parse(value)); } }), ])); } return row; } Widget columnQuantity() { if (widget.unit == "second") { return Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Padding( padding: const EdgeInsets.only(top: 10, left: 55, right: 55), child: StreamBuilder( stream: stopWatchTimer.rawTime, initialData: stopWatchTimer.rawTime.value, builder: (context, snap) { final value = snap.data; final displayTime = StopWatchTimer.getDisplayTime(value!, hours: false); return Column(children: [ Padding( padding: const EdgeInsets.all(8), child: Text( displayTime, style: const TextStyle(fontSize: 35, fontFamily: 'Helvetica', fontWeight: FontWeight.bold, color: Colors.white), ), ), ]); })), Padding( padding: const EdgeInsets.only(top: 10, left: 25, right: 25), child: Column( children: [ Padding( padding: const EdgeInsets.only(bottom: 0), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: IconButton( padding: const EdgeInsets.all(2), color: Colors.white70, onPressed: () async { stopWatchTimer.onExecute.add(StopWatchExecute.start); Wakelock.enable(); // prevent sleep the phone }, icon: Icon(CustomIcon.play_1), iconSize: 40, ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: IconButton( padding: const EdgeInsets.all(2), iconSize: 40, color: Colors.white70, onPressed: () async { stopWatchTimer.onExecute.add(StopWatchExecute.stop); Wakelock.disable(); }, icon: Icon(CustomIcon.stop), ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: IconButton( padding: const EdgeInsets.all(2), iconSize: 40, color: Colors.white70, onPressed: () async { stopWatchTimer.onExecute.add(StopWatchExecute.reset); }, icon: Icon(CustomIcon.creative_commons_zero), ), ), ], ), ), ], ), ), Divider(), Divider(), Text(t("Or type the time manually:"), style: GoogleFonts.inter(color: Colors.white)), TimePickerWidget( onChange: (value) => { widget.onQuantityChanged(value), }, ) ]); } Widget row = Container( padding: const EdgeInsets.only(top: 10, left: 55, right: 55), child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ TextFormField( focusNode: _nodeText2, controller: _controller2, decoration: InputDecoration( contentPadding: EdgeInsets.only(left: 25, top: 5, bottom: 5), labelText: t(widget.unit), labelStyle: GoogleFonts.inter(fontSize: 20, color: Colors.orange[50], decorationColor: Colors.black12), fillColor: Colors.black38, filled: true, border: OutlineInputBorder( gapPadding: 8.0, borderRadius: BorderRadius.circular(12.0), borderSide: BorderSide(color: Colors.black26, width: 0.4), ), ), keyboardType: TextInputType.number, textInputAction: TextInputAction.next, style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.orange[200]), onChanged: (value) { if (value.isNotEmpty) { value = value.replaceFirst(",", "."); value = value.replaceAll(RegExp(r'[^0-9.]'), ""); widget.onQuantityChanged(double.parse(value)); } }, ), ])); return row; } }