diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 99bea11d..dbe05122 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -93,11 +93,29 @@ "@repetitions": { "description": "Repetitions for an exercise set" }, + "reps": "Reps", + "@reps": { + "description": "Shorthand for repetitions, used when space constraints are tighter" + }, "rir": "RiR", "@rir": { "description": "Shorthand for Repetitions In Reserve" }, - "sameRepetitions": "If you do the same repetitions for all sets you can just fill in one value: e.g. for 4 sets just enter one \"10\" for the repetitions, this automatically becomes \"4 x 10\".", + "weightUnit": "Weight unit", + "repetitionUnit": "Repetition unit", + "set": "Set", + "@set": { + "description": "A set in a workout plan" + }, + "setNr": "Set {nr}", + "@setNr": { + "description": "Header in form indicating the number of the current set. Can also be translated as something like 'Set Nr. xy'.", + "type": "text", + "placeholders": { + "nr": {} + } + }, + "sameRepetitions": "If you do the same repetitions and weight for all sets you can just fill in one row. For example for 4 sets just enter one \"10\" for the repetitions, this automatically becomes \"4 x 10\".", "comment": "Comment", "@comment": { "description": "Comment, additional information" @@ -120,7 +138,7 @@ "@newSet": { "description": "Header when adding a new set to a workout day" }, - "selectExercises": "You can search for more than one exercise, they will be grouped together for a superset.", + "selectExercises": "If you want to do a superset you can search for several exercises, they will be grouped together", "gymMode": "Gym mode", "@gymMode": { "description": "Label when starting the gym mode" diff --git a/lib/models/workouts/setting.dart b/lib/models/workouts/setting.dart index 91709eed..63d37ee7 100644 --- a/lib/models/workouts/setting.dart +++ b/lib/models/workouts/setting.dart @@ -8,12 +8,17 @@ part 'setting.g.dart'; @JsonSerializable() class Setting { + static final possibleValues = ['1', '1.5', '2', '2.5', '3', '3.5']; + @JsonKey(required: true) int? id; @JsonKey(required: true, name: 'set') late int setId; + @JsonKey(required: true) + late int order; + @JsonKey(ignore: true) late Exercise exerciseObj; @@ -27,10 +32,10 @@ class Setting { late RepetitionUnit repetitionUnitObj; @JsonKey(required: true) - late int reps; + int? reps; @JsonKey(required: true, fromJson: toNum, toJson: toString) - late num weight; + num? weight; @JsonKey(required: true, name: 'weight_unit') late int weightUnitId; @@ -51,6 +56,7 @@ class Setting { Setting({ this.id, required this.setId, + required this.order, required this.exerciseId, required this.repetitionUnitId, required this.reps, @@ -59,27 +65,32 @@ class Setting { required this.rir, }); - Setting.withData({ - this.id, - required this.setId, - Exercise? exerciseObj, - required this.repetitionUnitId, - required this.reps, - required this.weight, - required this.weightUnitId, - required this.comment, - this.rir = '', - required this.repsText, - }) { - if (exerciseObj != null) { - this.exerciseId = exerciseObj.id; - this.exerciseObj = exerciseObj; - } - } - Setting.empty(); // Boilerplate factory Setting.fromJson(Map json) => _$SettingFromJson(json); Map toJson() => _$SettingToJson(this); + + void setExercise(Exercise exercise) { + exerciseObj = exercise; + exerciseId = exercise.id; + } + + void setWeightUnit(WeightUnit weightUnit) { + weightUnitObj = weightUnit; + weightUnitId = weightUnit.id; + } + + void setRepetitionUnit(RepetitionUnit repetitionUnit) { + repetitionUnitObj = repetitionUnit; + repetitionUnitId = repetitionUnit.id; + } + + void setRir(String rir) { + if (possibleValues.contains(rir)) { + this.rir = rir; + } else { + throw Exception('RiR value not allowed'); + } + } } diff --git a/lib/models/workouts/setting.g.dart b/lib/models/workouts/setting.g.dart index 62be7243..464e6eae 100644 --- a/lib/models/workouts/setting.g.dart +++ b/lib/models/workouts/setting.g.dart @@ -10,6 +10,7 @@ Setting _$SettingFromJson(Map json) { $checkKeys(json, requiredKeys: const [ 'id', 'set', + 'order', 'exercise', 'repetition_unit', 'reps', @@ -21,9 +22,10 @@ Setting _$SettingFromJson(Map json) { return Setting( id: json['id'] as int?, setId: json['set'] as int, + order: json['order'] as int, exerciseId: json['exercise'] as int, repetitionUnitId: json['repetition_unit'] as int, - reps: json['reps'] as int, + reps: json['reps'] as int?, weightUnitId: json['weight_unit'] as int, comment: json['comment'] as String, rir: json['rir'] as String?, @@ -33,6 +35,7 @@ Setting _$SettingFromJson(Map json) { Map _$SettingToJson(Setting instance) => { 'id': instance.id, 'set': instance.setId, + 'order': instance.order, 'exercise': instance.exerciseId, 'repetition_unit': instance.repetitionUnitId, 'reps': instance.reps, diff --git a/lib/screens/form_screen.dart b/lib/screens/form_screen.dart index 510af873..dc9f70c7 100644 --- a/lib/screens/form_screen.dart +++ b/lib/screens/form_screen.dart @@ -41,6 +41,7 @@ class FormScreen extends StatelessWidget { ModalRoute.of(context)!.settings.arguments as FormScreenArguments; return Scaffold( + backgroundColor: Colors.white, appBar: AppBar(title: Text(args.title)), body: args.hasListView ? Padding( diff --git a/lib/widgets/workouts/forms.dart b/lib/widgets/workouts/forms.dart index a51b6eb0..b824b403 100644 --- a/lib/widgets/workouts/forms.dart +++ b/lib/widgets/workouts/forms.dart @@ -215,7 +215,6 @@ class SetFormWidget extends StatefulWidget { late Set _set; SetFormWidget(Day this._day, [Set? set]) { - //this._day = day; this._set = set ?? Set.withData(day: _day.id, sets: 4); } @@ -249,10 +248,20 @@ class _SetFormWidgetState extends State { /// Adds settings to the set void addSettings() { widget._set.settings = []; + int order = 0; for (var exercise in widget._set.exercisesObj) { + order++; for (int loop = 0; loop < widget._set.sets; loop++) { Setting setting = Setting.empty(); - setting.exerciseObj = exercise; + setting.order = order; + setting.setExercise(exercise); + setting.setRepetitionUnit( + Provider.of(context, listen: false).defaultRepetitionUnit, + ); + setting.setWeightUnit( + Provider.of(context, listen: false).defaultWeightUnit, + ); + widget._set.settings.add(setting); } } @@ -302,14 +311,16 @@ class _SetFormWidgetState extends State { this._exercisesController.text = ''; }, validator: (value) { - if (value!.isEmpty) { + if (widget._set.exercisesIds.length == 0) { return AppLocalizations.of(context)!.selectExercise; } return null; }, ), SizedBox(height: 10), - Text(AppLocalizations.of(context)!.nrOfSets(_currentSetSliderValue.round())), + Center( + child: Text(AppLocalizations.of(context)!.nrOfSets(_currentSetSliderValue.round())), + ), Slider( value: _currentSetSliderValue, min: 1, @@ -333,7 +344,10 @@ class _SetFormWidgetState extends State { }); }, ), - Text(AppLocalizations.of(context)!.sameRepetitions), + Text( + AppLocalizations.of(context)!.sameRepetitions, + style: TextStyle(fontSize: 12, color: Colors.black54), + ), ...widget._set.exercisesObj.map((exercise) { final settings = widget._set.settings.where((e) => e.exerciseObj.id == exercise.id).toList(); @@ -367,9 +381,6 @@ class _SetFormWidgetState extends State { // Save remaining settings for (var setting in widget._set.settings) { setting.setId = setDb.id!; - - setting.weightUnitId = workoutProvider.defaultWeightUnit.id; - setting.repetitionUnitId = workoutProvider.defaultRepetitionUnit.id; setting.comment = ''; setting.repsText = 'temp text'; @@ -407,23 +418,71 @@ class ExerciseSetting extends StatelessWidget { this._numberOfSets = sliderValue.round(); } - Widget getRow() { + Widget getRows(BuildContext context) { List out = []; for (var i = 0; i < _numberOfSets; i++) { var setting = _settings[i]; out.add( - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - RepsInputWidget(setting), - SizedBox(width: 4), - if (_detailed) WeightUnitInputWidget(key: Key(i.toString())), - WeightInputWidget(setting), - SizedBox(width: 4), - if (_detailed) RepetitionUnitInputWidget(), - if (_detailed) RiRInputWidget(), - ], - ), + _detailed + ? Column( + //crossAxisAlignment: CrossAxisAlignment.baseline, + //1textBaseline: TextBaseline.alphabetic, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + AppLocalizations.of(context)!.setNr(i + 1), + style: Theme.of(context).textTheme.headline6, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Flexible( + flex: 2, + child: RepsInputWidget(setting, _detailed), + ), + SizedBox(width: 4), + Flexible( + flex: 3, + child: WeightUnitInputWidget(setting, key: Key(i.toString())), + ), + ], + ), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Flexible( + flex: 2, + child: WeightInputWidget(setting, _detailed), + ), + SizedBox(width: 4), + Flexible( + flex: 3, + child: RepetitionUnitInputWidget(setting), + ), + ], + ), + Flexible( + flex: 2, + child: RiRInputWidget(setting), + ), + SizedBox(height: 15), + ], + ) + : Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.alphabetic, + children: [ + Text( + AppLocalizations.of(context)!.setNr(i + 1), + style: TextStyle(fontWeight: FontWeight.bold), + ), + SizedBox(width: 10), + Flexible(child: RepsInputWidget(setting, _detailed)), + SizedBox(width: 4), + Flexible(child: WeightInputWidget(setting, _detailed)), + ], + ), ); } return Column( @@ -435,26 +494,27 @@ class ExerciseSetting extends StatelessWidget { Widget build(BuildContext context) { return Column( children: [ - SizedBox(height: 20), - Text(_exercise.name, style: TextStyle(fontWeight: FontWeight.bold)), + SizedBox(height: 30), + Text( + _exercise.name, + style: Theme.of(context).textTheme.headline6, + ), IconButton( - icon: Icon(Icons.close), + icon: Icon(Icons.delete_outline), onPressed: () { removeExercise(_exercise); }, ), //ExerciseImage(imageUrl: _exercise.images.first.url), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Text(AppLocalizations.of(context)!.repetitions), - if (_detailed) Text(AppLocalizations.of(context)!.unit), - Text(AppLocalizations.of(context)!.weight), - if (_detailed) Text(AppLocalizations.of(context)!.unit), - if (_detailed) Text(AppLocalizations.of(context)!.rir), - ], - ), - getRow(), + if (!_detailed) + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text(AppLocalizations.of(context)!.repetitions), + Text(AppLocalizations.of(context)!.weight), + ], + ), + getRows(context), ], ); } @@ -462,52 +522,77 @@ class ExerciseSetting extends StatelessWidget { class RepsInputWidget extends StatelessWidget { final _repsController = TextEditingController(); - Setting _setting; + final Setting _setting; + final bool _detailed; - RepsInputWidget(this._setting); + RepsInputWidget(this._setting, this._detailed); @override Widget build(BuildContext context) { - return Flexible( - child: TextFormField( - //decoration: InputDecoration(labelText: 'Reps'), - controller: _repsController, - keyboardType: TextInputType.number, - onSaved: (newValue) { - if (newValue != null && newValue != '') { - _setting.reps = int.parse(newValue); - } - }, + return TextFormField( + decoration: InputDecoration( + labelText: _detailed ? AppLocalizations.of(context)!.repetitions : '', + errorMaxLines: 2, ), + controller: _repsController, + keyboardType: TextInputType.number, + validator: (value) { + try { + if (value != "") { + double.parse(value!); + } + } catch (error) { + return AppLocalizations.of(context)!.enterValidNumber; + } + return null; + }, + onSaved: (newValue) { + if (newValue != null && newValue != '') { + _setting.reps = int.parse(newValue); + } + }, ); } } class WeightInputWidget extends StatelessWidget { final _weightController = TextEditingController(); - Setting _setting; + final Setting _setting; + final bool _detailed; - WeightInputWidget(this._setting); + WeightInputWidget(this._setting, this._detailed); @override Widget build(BuildContext context) { - return Flexible( - child: TextFormField( - //decoration: InputDecoration(labelText: 'Weight'), - controller: _weightController, - keyboardType: TextInputType.number, - onSaved: (newValue) { - if (newValue != null && newValue != '') { - _setting.weight = double.parse(newValue); - } - }, + return TextFormField( + decoration: InputDecoration( + labelText: _detailed ? AppLocalizations.of(context)!.weight : '', + errorMaxLines: 2, ), + controller: _weightController, + keyboardType: TextInputType.number, + validator: (value) { + try { + if (value != "") { + double.parse(value!); + } + } catch (error) { + return AppLocalizations.of(context)!.enterValidNumber; + } + return null; + }, + onSaved: (newValue) { + if (newValue != null && newValue != '') { + _setting.weight = double.parse(newValue); + } + }, ); } } class RiRInputWidget extends StatefulWidget { - RiRInputWidget(); + final Setting _setting; + RiRInputWidget(this._setting); @override _RiRInputWidgetState createState() => _RiRInputWidgetState(); @@ -518,19 +603,16 @@ class _RiRInputWidgetState extends State { @override Widget build(BuildContext context) { - return DropdownButton( + return DropdownButtonFormField( + decoration: InputDecoration(labelText: AppLocalizations.of(context)!.rir), value: dropdownValue, - underline: Container( - height: 0, - //color: Colors.deepPurpleAccent, - ), onChanged: (String? newValue) { setState(() { dropdownValue = newValue!; + widget._setting.setRir(newValue); }); }, - items: ['1', '1.5', '2', '2.5', '3', '3.5'] - .map>((String value) { + items: Setting.possibleValues.map>((String value) { return DropdownMenuItem( value: value, child: Text(value), @@ -541,26 +623,26 @@ class _RiRInputWidgetState extends State { } class WeightUnitInputWidget extends StatefulWidget { - WeightUnitInputWidget({Key? key}) : super(key: key); + final Setting _setting; + + WeightUnitInputWidget(this._setting, {Key? key}) : super(key: key); @override _WeightUnitInputWidgetState createState() => _WeightUnitInputWidgetState(); } class _WeightUnitInputWidgetState extends State { - late WeightUnit dropdownValue; - @override Widget build(BuildContext context) { - return DropdownButton( - value: dropdownValue, - underline: Container( - height: 0, - // color: Colors.deepPurpleAccent, - ), + WeightUnit selectedWeightUnit = widget._setting.weightUnitObj; + + return DropdownButtonFormField( + value: selectedWeightUnit, + decoration: InputDecoration(labelText: AppLocalizations.of(context)!.weightUnit), onChanged: (WeightUnit? newValue) { setState(() { - dropdownValue = newValue!; + selectedWeightUnit = newValue!; + widget._setting.setWeightUnit(newValue); }); }, items: Provider.of(context, listen: false) @@ -568,10 +650,7 @@ class _WeightUnitInputWidgetState extends State { .map>((WeightUnit value) { return DropdownMenuItem( value: value, - child: Text( - value.name, - style: TextStyle(fontSize: 12), - ), + child: Text(value.name), ); }).toList(), ); @@ -579,26 +658,26 @@ class _WeightUnitInputWidgetState extends State { } class RepetitionUnitInputWidget extends StatefulWidget { - RepetitionUnitInputWidget(); + final Setting _setting; + RepetitionUnitInputWidget(this._setting); @override _RepetitionUnitInputWidgetState createState() => _RepetitionUnitInputWidgetState(); } class _RepetitionUnitInputWidgetState extends State { - late RepetitionUnit dropdownValue; - @override Widget build(BuildContext context) { - return DropdownButton( - value: dropdownValue, - underline: Container( - height: 0, - // color: Colors.deepPurpleAccent, - ), + RepetitionUnit selectedWeightUnit = widget._setting.repetitionUnitObj; + + return DropdownButtonFormField( + value: selectedWeightUnit, + decoration: InputDecoration(labelText: AppLocalizations.of(context)!.repetitionUnit), + isDense: true, onChanged: (RepetitionUnit? newValue) { setState(() { - dropdownValue = newValue!; + selectedWeightUnit = newValue!; + widget._setting.setRepetitionUnit(newValue); }); }, items: Provider.of(context, listen: false) @@ -606,10 +685,7 @@ class _RepetitionUnitInputWidgetState extends State { .map>((RepetitionUnit value) { return DropdownMenuItem( value: value, - child: Text( - value.name, - style: TextStyle(fontSize: 12), - ), + child: Text(value.name), ); }).toList(), );