From fa973daa51f2460612dcd6e427077a62fce9c2a7 Mon Sep 17 00:00:00 2001 From: Jesse Date: Wed, 24 Apr 2024 10:07:38 -0400 Subject: [PATCH 1/6] Get a basic set name feature working for workout editor --- lib/l10n/intl_en.arb | 1 + lib/layouts/workout_builder.dart | 23 +++++++++++++++++++++++ lib/utils/workout.dart | 4 ++++ lib/workout.g.dart | 2 ++ 4 files changed, 30 insertions(+) diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index aa73fd1..346a2dc 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -19,6 +19,7 @@ "duplicate": "Duplicate", "durationLeft": "{timeLeft} of {timeTotal} left", "durationWithTime": "Duration: {formattedTime}", + "editSetName" : "Edit set name", "editWorkout": "Edit workout", "enterWorkoutName": "Please enter a name for the workout!", "exercise": "Exercise", diff --git a/lib/layouts/workout_builder.dart b/lib/layouts/workout_builder.dart index 04c16cc..6ccf8fb 100644 --- a/lib/layouts/workout_builder.dart +++ b/lib/layouts/workout_builder.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:fluttertoast/fluttertoast.dart'; +import 'package:prompt_dialog/prompt_dialog.dart'; import 'package:uuid/uuid.dart'; import '../generated/l10n.dart'; @@ -149,6 +150,20 @@ class BuilderPageState extends State { }); } + void _editSetName(int setIndex) async { + var existingName = _workout.sets[setIndex].name ?? ""; + var newName = await prompt( + context, + initialValue: existingName, + maxLength: 15); + if (newName != null && newName != existingName) { + setState(() { + _workout.sets[setIndex].name = newName.isEmpty ? null : newName; + _dirty = true; + }); + } + } + Widget _buildSetList() => ReorderableListView( onReorder: (oldIndex, newIndex) { if (oldIndex < newIndex) { @@ -186,11 +201,19 @@ class BuilderPageState extends State { Expanded( child: ListTile( title: Text( + set.name ?? S.of(context).setIndex(_workout.sets.indexOf(set) + 1), ), subtitle: Text(Utils.formatSeconds(set.duration)), ), ), + IconButton( + icon: const Icon(Icons.edit), + tooltip: S.of(context).editSetName, + onPressed: () { + _editSetName(index); + }, + ), Text(S.of(context).repetitions), NumberStepper( lowerLimit: 1, diff --git a/lib/utils/workout.dart b/lib/utils/workout.dart index 7b07623..db34cf2 100644 --- a/lib/utils/workout.dart +++ b/lib/utils/workout.dart @@ -51,6 +51,7 @@ class Set { String? id, this.repetitions = 1, List? exercises, + this.name }) { this.id = id ?? const Uuid().v4(); this.exercises = exercises ?? [Exercise()]; @@ -59,6 +60,9 @@ class Set { @JsonKey(required: true) int repetitions; + @JsonKey() + late String? name; + @JsonKey() late String id; diff --git a/lib/workout.g.dart b/lib/workout.g.dart index f65dc03..8789c61 100644 --- a/lib/workout.g.dart +++ b/lib/workout.g.dart @@ -39,6 +39,7 @@ Set _$SetFromJson(Map json) { exercises: (json['exercises'] as List?) ?.map((e) => Exercise.fromJson(e as Map)) .toList(), + name: json['name'] as String? ); } @@ -46,6 +47,7 @@ Map _$SetToJson(Set instance) => { 'repetitions': instance.repetitions, 'id': instance.id, 'exercises': instance.exercises.map((e) => e.toJson()).toList(), + 'name' : instance.name }; Exercise _$ExerciseFromJson(Map json) { From 1cb44a082ea5814dab044d0a46d1ddca1f0a2a8a Mon Sep 17 00:00:00 2001 From: Jesse Date: Thu, 25 Apr 2024 11:19:06 -0400 Subject: [PATCH 2/6] Fix a bug where a duplicated set was inserted before the current set This wasn't noticeable until I added the ability to collapse sets, but once that was added, it mattered where exactly the new set was inserted --- lib/layouts/workout_builder.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/layouts/workout_builder.dart b/lib/layouts/workout_builder.dart index 6ccf8fb..af304e3 100644 --- a/lib/layouts/workout_builder.dart +++ b/lib/layouts/workout_builder.dart @@ -57,7 +57,7 @@ class BuilderPageState extends State { var newSet = Set.fromJson(_workout.sets[index].toJson()); newSet.id = const Uuid().v4(); setState(() { - _workout.sets.insert(index, newSet); + _workout.sets.insert(index + 1, newSet); _dirty = true; }); } From 9d777f48ebb545a59c2f66b228e71f826e47f1bf Mon Sep 17 00:00:00 2001 From: Jesse Date: Thu, 25 Apr 2024 11:20:58 -0400 Subject: [PATCH 3/6] Finish work on the set name feature To rename a set, you can long-press on the name of the set in the builder. In order to deal with the new variable length set names, I broke the set name and workout duration into two separate lines in the workout runner. I don't love having to eat up more vertical space that way, but don't have another amazing idea for how to deal with it and I didn't want to shrink down the font size --- lib/layouts/workout_builder.dart | 13 ++- lib/layouts/workout_runner.dart | 145 ++++++++++++++----------------- 2 files changed, 72 insertions(+), 86 deletions(-) diff --git a/lib/layouts/workout_builder.dart b/lib/layouts/workout_builder.dart index af304e3..9900f49 100644 --- a/lib/layouts/workout_builder.dart +++ b/lib/layouts/workout_builder.dart @@ -203,17 +203,16 @@ class BuilderPageState extends State { title: Text( set.name ?? S.of(context).setIndex(_workout.sets.indexOf(set) + 1), + style: const TextStyle( + decoration: TextDecoration.underline, + ), ), subtitle: Text(Utils.formatSeconds(set.duration)), + onLongPress: () { + _editSetName(index); + }, ), ), - IconButton( - icon: const Icon(Icons.edit), - tooltip: S.of(context).editSetName, - onPressed: () { - _editSetName(index); - }, - ), Text(S.of(context).repetitions), NumberStepper( lowerLimit: 1, diff --git a/lib/layouts/workout_runner.dart b/lib/layouts/workout_runner.dart index 00de6e2..cb8c490 100644 --- a/lib/layouts/workout_runner.dart +++ b/lib/layouts/workout_runner.dart @@ -147,92 +147,79 @@ class WorkoutPageState extends State { // left side of footer Expanded( child: ListTile( - title: Text( - S.of(context).exerciseOf( - timetable.currentSet.exercises - .indexOf(timetable.currentExercise) + - 1 + - (timetable.currentReps * - timetable.currentSet.exercises.length), - timetable.currentSet.exercises.length * - timetable.currentSet.repetitions, - ), - ), - subtitle: Text( - S.of(context).repOf( - timetable.currentReps + 1, - timetable.currentSet.repetitions, - ), - ), - ), - ), + title: Text(S.of(context).exerciseOf( + timetable.currentSet.exercises + .indexOf(timetable.currentExercise) + + 1 + + (timetable.currentReps * + timetable.currentSet.exercises.length), + timetable.currentSet.exercises.length * + timetable.currentSet.repetitions)), + subtitle: Text(S.of(context).repOf(timetable.currentReps + 1, + timetable.currentSet.repetitions)), + )), // right side of footer Expanded( - child: ListTile( - title: Text( - S.of(context).setOf( - _workout.sets.indexOf(timetable.currentSet) + 1, - _workout.sets.length, - ), - textAlign: TextAlign.end, - ), - subtitle: Text( - S.of(context).durationLeft( - Utils.formatSeconds( - _workout.duration - timetable.currentSecond + 10, - ), - Utils.formatSeconds(_workout.duration + 10), - ), - textAlign: TextAlign.end, - ), + child: ListTile( + title: Text( + S.of(context).setOf( + _workout.sets.indexOf(timetable.currentSet) + 1, + _workout.sets.length), + textAlign: TextAlign.end, ), - ), + subtitle: Text( + S.of(context).durationLeft( + Utils.formatSeconds( + _workout.duration - timetable.currentSecond + 10), + Utils.formatSeconds(_workout.duration + 10)), + textAlign: TextAlign.end, + ), + )) ], - ), - ), - body: Column( - children: [ - // top card with current exercise - Card( - child: Center( - child: Column( - children: [ - Text( - '${S.of(context).setIndex(_workout.sets.indexOf(timetable.currentSet) + 1)} - ${Utils.formatSeconds(timetable.remainingSeconds)}', - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 48, - fontWeight: FontWeight.bold, + )), + body: Column( + children: [ + // top card with current exercise + Card( + child: Center( + child: Column( + children: [ + Text( + timetable.currentSet.name ?? S.of(context).setIndex(_workout.sets.indexOf(timetable.currentSet) + 1), + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 40, fontWeight: FontWeight.bold), ), - ), - LinearProgressIndicator( - value: timetable.remainingSeconds / - (timetable.currentSecond < 10 - ? 10 - : timetable.currentExercise.duration), - minHeight: 6, - valueColor: AlwaysStoppedAnimation( - Theme.of(context).colorScheme.secondary, + Text( + Utils.formatSeconds(timetable.remainingSeconds), + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 48, fontWeight: FontWeight.bold), ), - ), - Text( - timetable.currentExercise.name, - style: const TextStyle( - fontSize: 48, - fontWeight: FontWeight.bold, + LinearProgressIndicator( + value: timetable.remainingSeconds / + (timetable.currentSecond < 10 + ? 10 + : timetable.currentExercise.duration), + minHeight: 6, + valueColor: AlwaysStoppedAnimation( + Theme.of(context).colorScheme.secondary), ), - textAlign: TextAlign.center, - ), - timetable.nextExercise != null - ? Text( - S.of(context).nextExercise( - timetable.nextExercise?.name ?? '', - ), - style: const TextStyle(fontSize: 24), - textAlign: TextAlign.center, - ) - : Container(), - ], + Text( + timetable.currentExercise.name, + style: const TextStyle( + fontSize: 48, fontWeight: FontWeight.bold), + textAlign: TextAlign.center, + ), + timetable.nextExercise != null + ? Text( + S.of(context).nextExercise( + timetable.nextExercise?.name ?? ''), + style: const TextStyle(fontSize: 24), + textAlign: TextAlign.center, + ) + : Container(), + ], ), ), ), From db1927a714efb42b5e3b411519d341e907dbcd4a Mon Sep 17 00:00:00 2001 From: Jesse Date: Thu, 25 Apr 2024 11:21:37 -0400 Subject: [PATCH 4/6] Add the ability to hide a set's list of exercises in the builder Makes re-organizing sets much easier and lets you focus on just the current set you're making --- lib/layouts/workout_builder.dart | 16 ++++++++++++---- lib/utils/workout.dart | 5 ++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/layouts/workout_builder.dart b/lib/layouts/workout_builder.dart index 9900f49..0be8a52 100644 --- a/lib/layouts/workout_builder.dart +++ b/lib/layouts/workout_builder.dart @@ -254,12 +254,20 @@ class BuilderPageState extends State { Row( children: [ IconButton( - icon: const Icon(Icons.delete), - tooltip: S.of(context).deleteSet, + icon: Icon(set.hidden ? Icons.visibility : Icons.visibility_off), onPressed: () { - _deleteSet(index); + setState(() { + set.hidden = !set.hidden; + _dirty = true; + }); }, ), + IconButton( + icon: const Icon(Icons.delete), + tooltip: S.of(context).deleteSet, + onPressed: () { + _deleteSet(index); + }), IconButton( icon: const Icon(Icons.copy), tooltip: S.of(context).duplicate, @@ -286,7 +294,7 @@ class BuilderPageState extends State { _workout.sets[setIndex].exercises.insert(newIndex, ex); }); }, - children: set.exercises + children: set.hidden ? [] : set.exercises .asMap() .keys .map( diff --git a/lib/utils/workout.dart b/lib/utils/workout.dart index db34cf2..7de7212 100644 --- a/lib/utils/workout.dart +++ b/lib/utils/workout.dart @@ -51,7 +51,8 @@ class Set { String? id, this.repetitions = 1, List? exercises, - this.name + this.name, + this.hidden = false, }) { this.id = id ?? const Uuid().v4(); this.exercises = exercises ?? [Exercise()]; @@ -69,6 +70,8 @@ class Set { @JsonKey(required: true) late List exercises; + bool hidden; + int get duration { var duration = 0; From 4a8f88e42504570f4bf1ddd7e535d067167b467a Mon Sep 17 00:00:00 2001 From: Jesse Clemens Date: Fri, 12 Jul 2024 11:30:59 -0400 Subject: [PATCH 5/6] Cleanup the whitespace and formatting to match main --- lib/layouts/workout_builder.dart | 3 +- lib/layouts/workout_runner.dart | 152 ++++++++++++++++++------------- 2 files changed, 89 insertions(+), 66 deletions(-) diff --git a/lib/layouts/workout_builder.dart b/lib/layouts/workout_builder.dart index 0be8a52..abe4dd9 100644 --- a/lib/layouts/workout_builder.dart +++ b/lib/layouts/workout_builder.dart @@ -267,7 +267,8 @@ class BuilderPageState extends State { tooltip: S.of(context).deleteSet, onPressed: () { _deleteSet(index); - }), + } + ), IconButton( icon: const Icon(Icons.copy), tooltip: S.of(context).duplicate, diff --git a/lib/layouts/workout_runner.dart b/lib/layouts/workout_runner.dart index cb8c490..e7b5b78 100644 --- a/lib/layouts/workout_runner.dart +++ b/lib/layouts/workout_runner.dart @@ -147,79 +147,101 @@ class WorkoutPageState extends State { // left side of footer Expanded( child: ListTile( - title: Text(S.of(context).exerciseOf( - timetable.currentSet.exercises - .indexOf(timetable.currentExercise) + - 1 + - (timetable.currentReps * - timetable.currentSet.exercises.length), - timetable.currentSet.exercises.length * - timetable.currentSet.repetitions)), - subtitle: Text(S.of(context).repOf(timetable.currentReps + 1, - timetable.currentSet.repetitions)), - )), + title: Text( + S.of(context).exerciseOf( + timetable.currentSet.exercises + .indexOf(timetable.currentExercise) + + 1 + + (timetable.currentReps * + timetable.currentSet.exercises.length), + timetable.currentSet.exercises.length * + timetable.currentSet.repetitions, + ), + ), + subtitle: Text( + S.of(context).repOf( + timetable.currentReps + 1, + timetable.currentSet.repetitions, + ), + ), + ), + ), + // right side of footer Expanded( - child: ListTile( - title: Text( - S.of(context).setOf( - _workout.sets.indexOf(timetable.currentSet) + 1, - _workout.sets.length), - textAlign: TextAlign.end, - ), - subtitle: Text( - S.of(context).durationLeft( - Utils.formatSeconds( - _workout.duration - timetable.currentSecond + 10), - Utils.formatSeconds(_workout.duration + 10)), - textAlign: TextAlign.end, + child: ListTile( + title: Text( + S.of(context).setOf( + _workout.sets.indexOf(timetable.currentSet) + 1, + _workout.sets.length, + ), + textAlign: TextAlign.end, + ), + subtitle: Text( + S.of(context).durationLeft( + Utils.formatSeconds( + _workout.duration - timetable.currentSecond + 10, + ), + Utils.formatSeconds(_workout.duration + 10), + ), + textAlign: TextAlign.end, + ), ), - )) + ), ], - )), - body: Column( - children: [ - // top card with current exercise - Card( - child: Center( - child: Column( - children: [ - Text( - timetable.currentSet.name ?? S.of(context).setIndex(_workout.sets.indexOf(timetable.currentSet) + 1), - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 40, fontWeight: FontWeight.bold), + ), + ), + body: Column( + children: [ + // top card with current exercise + Card( + child: Center( + child: Column( + children: [ + Text( + timetable.currentSet.name ?? S.of(context).setIndex(_workout.sets.indexOf(timetable.currentSet) + 1), + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 40, + fontWeight: FontWeight.bold, + ), ), - Text( - Utils.formatSeconds(timetable.remainingSeconds), - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 48, fontWeight: FontWeight.bold), + Text( + Utils.formatSeconds(timetable.remainingSeconds), + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 48, + fontWeight: FontWeight.bold, ), - LinearProgressIndicator( - value: timetable.remainingSeconds / - (timetable.currentSecond < 10 - ? 10 - : timetable.currentExercise.duration), - minHeight: 6, - valueColor: AlwaysStoppedAnimation( - Theme.of(context).colorScheme.secondary), + ), + LinearProgressIndicator( + value: timetable.remainingSeconds / + (timetable.currentSecond < 10 + ? 10 + : timetable.currentExercise.duration), + minHeight: 6, + valueColor: AlwaysStoppedAnimation( + Theme.of(context).colorScheme.secondary, ), - Text( - timetable.currentExercise.name, - style: const TextStyle( - fontSize: 48, fontWeight: FontWeight.bold), - textAlign: TextAlign.center, + ), + Text( + timetable.currentExercise.name, + style: const TextStyle( + fontSize: 48, + fontWeight: FontWeight.bold, ), - timetable.nextExercise != null - ? Text( - S.of(context).nextExercise( - timetable.nextExercise?.name ?? ''), - style: const TextStyle(fontSize: 24), - textAlign: TextAlign.center, - ) - : Container(), - ], + textAlign: TextAlign.center, + ), + timetable.nextExercise != null + ? Text( + S.of(context).nextExercise( + timetable.nextExercise?.name ?? '', + ), + style: const TextStyle(fontSize: 24), + textAlign: TextAlign.center, + ) + : Container(), + ], ), ), ), From 49f3421b2dbeb3a97dcac7dde008d6639f169e4d Mon Sep 17 00:00:00 2001 From: Jesse Clemens Date: Wed, 24 Jul 2024 11:25:17 -0400 Subject: [PATCH 6/6] Add the prompt_dialog dependency Got removed when rebasing --- pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec.yaml b/pubspec.yaml index a0168fb..137714f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,6 +23,7 @@ dependencies: path_provider: '^2.1.3' pref: '^2.8.0' prefs: '^4.1.0+3' + prompt_dialog: '^1.0.16' provider: '^6.1.2' scrollable_positioned_list: '^0.3.8' share_plus: '^9.0.0'