mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
Polish the crud workflow
This commit is contained in:
@@ -35,14 +35,14 @@ const TESTSERVER_PASSWORD = 'flutteruser';
|
||||
const MANIFEST_KEY_CHECK_UPDATE = 'wger.check_min_app_version';
|
||||
|
||||
/// Default weight unit is "kg"
|
||||
const DEFAULT_WEIGHT_UNIT = 1;
|
||||
const WEIGHT_UNIT_KG_ID = 1;
|
||||
|
||||
/// Default impression for a workout session (neutral)
|
||||
const DEFAULT_IMPRESSION = 2;
|
||||
|
||||
// Weight and repetition units for the workout logs
|
||||
const REP_UNIT_REPETITIONS = 1;
|
||||
const REP_UNIT_TILL_FAILURE = 2;
|
||||
const REP_UNIT_REPETITIONS_ID = 1;
|
||||
const REP_UNIT_TILL_FAILURE_ID = 2;
|
||||
|
||||
const WEIGHT_UNIT_KG = 1;
|
||||
const WEIGHT_UNIT_LB = 2;
|
||||
|
||||
@@ -42,7 +42,7 @@ String repText(
|
||||
// rather "8 repetitions". If there is weight we want to output "8 x 50kg",
|
||||
// since the repetitions are implied. If other units are used, we always
|
||||
// print them
|
||||
if (repetitionUnitObj.id != REP_UNIT_REPETITIONS || weight == 0 || weight == null) {
|
||||
if (repetitionUnitObj.id != REP_UNIT_REPETITIONS_ID || weight == 0 || weight == null) {
|
||||
out.add(repetitionUnitObj.name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -706,6 +706,8 @@
|
||||
"images": "Images",
|
||||
"language": "Language",
|
||||
"addExercise": "Add exercise",
|
||||
"addSuperset": "Add superset",
|
||||
"setHasNoExercises": "This set has no exercises yet!",
|
||||
"contributeExercise": "Contribute an exercise",
|
||||
"translation": "Translation",
|
||||
"translateExercise": "Translate this exercise now",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:wger/helpers/consts.dart';
|
||||
import 'package:wger/helpers/json.dart';
|
||||
import 'package:wger/models/exercises/exercise.dart';
|
||||
import 'package:wger/models/workouts/base_config.dart';
|
||||
@@ -68,11 +69,11 @@ class SlotEntry {
|
||||
@JsonKey(required: true, name: 'repetition_rounding', fromJson: stringToNum)
|
||||
late num repetitionRounding;
|
||||
|
||||
@JsonKey(required: true, name: 'reps_configs')
|
||||
late List<BaseConfig> repsConfigs;
|
||||
@JsonKey(required: false, name: 'reps_configs', includeToJson: false, defaultValue: [])
|
||||
late List<BaseConfig> repsConfigs = [];
|
||||
|
||||
@JsonKey(required: true, name: 'max_reps_configs')
|
||||
late List<BaseConfig> maxRepsConfigs;
|
||||
@JsonKey(required: false, name: 'max_reps_configs', includeToJson: false, defaultValue: [])
|
||||
late List<BaseConfig> maxRepsConfigs = [];
|
||||
|
||||
@JsonKey(required: true, name: 'weight_unit')
|
||||
late int weightUnitId;
|
||||
@@ -83,23 +84,23 @@ class SlotEntry {
|
||||
@JsonKey(required: true, name: 'weight_rounding', fromJson: stringToNum)
|
||||
late num weightRounding;
|
||||
|
||||
@JsonKey(required: true, name: 'weight_configs')
|
||||
late List<BaseConfig> weightConfigs;
|
||||
@JsonKey(required: false, name: 'weight_configs', includeToJson: false, defaultValue: [])
|
||||
late List<BaseConfig> weightConfigs = [];
|
||||
|
||||
@JsonKey(required: true, name: 'max_weight_configs')
|
||||
late List<BaseConfig> maxWeightConfigs;
|
||||
@JsonKey(required: false, name: 'max_weight_configs', includeToJson: false, defaultValue: [])
|
||||
late List<BaseConfig> maxWeightConfigs = [];
|
||||
|
||||
@JsonKey(required: true, name: 'set_nr_configs')
|
||||
late List<BaseConfig> nrOfSetsConfigs;
|
||||
@JsonKey(required: false, name: 'set_nr_configs', includeToJson: false, defaultValue: [])
|
||||
late List<BaseConfig> nrOfSetsConfigs = [];
|
||||
|
||||
@JsonKey(required: true, name: 'rir_configs')
|
||||
late List<BaseConfig> rirConfigs;
|
||||
@JsonKey(required: false, name: 'rir_configs', includeToJson: false, defaultValue: [])
|
||||
late List<BaseConfig> rirConfigs = [];
|
||||
|
||||
@JsonKey(required: true, name: 'rest_configs')
|
||||
late List<BaseConfig> restTimeConfigs;
|
||||
@JsonKey(required: false, name: 'rest_configs', includeToJson: false, defaultValue: [])
|
||||
late List<BaseConfig> restTimeConfigs = [];
|
||||
|
||||
@JsonKey(required: true, name: 'max_rest_configs')
|
||||
late List<BaseConfig> maxRestTimeConfigs;
|
||||
@JsonKey(required: false, name: 'max_rest_configs', includeToJson: false, defaultValue: [])
|
||||
late List<BaseConfig> maxRestTimeConfigs = [];
|
||||
|
||||
@JsonKey(required: true)
|
||||
late Object? config;
|
||||
@@ -119,6 +120,30 @@ class SlotEntry {
|
||||
|
||||
SlotEntry.empty();
|
||||
|
||||
SlotEntry.withData({
|
||||
required this.slotId,
|
||||
String? comment,
|
||||
int? order,
|
||||
String? type,
|
||||
required Exercise exercise,
|
||||
int? weightUnitId,
|
||||
num? weightRounding,
|
||||
int? repetitionUnitId,
|
||||
num? repetitionRounding,
|
||||
}) {
|
||||
this.order = order ?? 1;
|
||||
this.comment = comment ?? '';
|
||||
config = null;
|
||||
this.type = type ?? 'normal';
|
||||
exerciseObj = exercise;
|
||||
exerciseId = exercise.id!;
|
||||
this.weightUnitId = weightUnitId ?? WEIGHT_UNIT_KG_ID;
|
||||
this.weightRounding = weightRounding ?? 2.5;
|
||||
|
||||
this.repetitionUnitId = repetitionUnitId ?? REP_UNIT_REPETITIONS_ID;
|
||||
this.repetitionRounding = repetitionRounding ?? 1;
|
||||
}
|
||||
|
||||
get rir {
|
||||
return 'DELETE ME! RIR';
|
||||
}
|
||||
|
||||
@@ -18,16 +18,8 @@ SlotEntry _$SlotEntryFromJson(Map<String, dynamic> json) {
|
||||
'exercise',
|
||||
'repetition_unit',
|
||||
'repetition_rounding',
|
||||
'reps_configs',
|
||||
'max_reps_configs',
|
||||
'weight_unit',
|
||||
'weight_rounding',
|
||||
'weight_configs',
|
||||
'max_weight_configs',
|
||||
'set_nr_configs',
|
||||
'rir_configs',
|
||||
'rest_configs',
|
||||
'max_rest_configs',
|
||||
'config'
|
||||
],
|
||||
);
|
||||
@@ -43,30 +35,38 @@ SlotEntry _$SlotEntryFromJson(Map<String, dynamic> json) {
|
||||
weightRounding: stringToNum(json['weight_rounding'] as String?),
|
||||
comment: json['comment'] as String,
|
||||
)
|
||||
..repsConfigs = (json['reps_configs'] as List<dynamic>)
|
||||
.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
..maxRepsConfigs = (json['max_reps_configs'] as List<dynamic>)
|
||||
.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
..weightConfigs = (json['weight_configs'] as List<dynamic>)
|
||||
.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
..maxWeightConfigs = (json['max_weight_configs'] as List<dynamic>)
|
||||
.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
..nrOfSetsConfigs = (json['set_nr_configs'] as List<dynamic>)
|
||||
.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
..rirConfigs = (json['rir_configs'] as List<dynamic>)
|
||||
.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
..restTimeConfigs = (json['rest_configs'] as List<dynamic>)
|
||||
.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
..maxRestTimeConfigs = (json['max_rest_configs'] as List<dynamic>)
|
||||
.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
..repsConfigs = (json['reps_configs'] as List<dynamic>?)
|
||||
?.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[]
|
||||
..maxRepsConfigs = (json['max_reps_configs'] as List<dynamic>?)
|
||||
?.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[]
|
||||
..weightConfigs = (json['weight_configs'] as List<dynamic>?)
|
||||
?.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[]
|
||||
..maxWeightConfigs = (json['max_weight_configs'] as List<dynamic>?)
|
||||
?.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[]
|
||||
..nrOfSetsConfigs = (json['set_nr_configs'] as List<dynamic>?)
|
||||
?.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[]
|
||||
..rirConfigs = (json['rir_configs'] as List<dynamic>?)
|
||||
?.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[]
|
||||
..restTimeConfigs = (json['rest_configs'] as List<dynamic>?)
|
||||
?.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[]
|
||||
..maxRestTimeConfigs = (json['max_rest_configs'] as List<dynamic>?)
|
||||
?.map((e) => BaseConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[]
|
||||
..config = json['config'];
|
||||
}
|
||||
|
||||
@@ -78,15 +78,7 @@ Map<String, dynamic> _$SlotEntryToJson(SlotEntry instance) => <String, dynamic>{
|
||||
'exercise': instance.exerciseId,
|
||||
'repetition_unit': instance.repetitionUnitId,
|
||||
'repetition_rounding': instance.repetitionRounding,
|
||||
'reps_configs': instance.repsConfigs,
|
||||
'max_reps_configs': instance.maxRepsConfigs,
|
||||
'weight_unit': instance.weightUnitId,
|
||||
'weight_rounding': instance.weightRounding,
|
||||
'weight_configs': instance.weightConfigs,
|
||||
'max_weight_configs': instance.maxWeightConfigs,
|
||||
'set_nr_configs': instance.nrOfSetsConfigs,
|
||||
'rir_configs': instance.rirConfigs,
|
||||
'rest_configs': instance.restTimeConfigs,
|
||||
'max_rest_configs': instance.maxRestTimeConfigs,
|
||||
'config': instance.config,
|
||||
};
|
||||
|
||||
@@ -86,7 +86,7 @@ class RoutinesProvider with ChangeNotifier {
|
||||
|
||||
/// Return the default weight unit (kg)
|
||||
WeightUnit get defaultWeightUnit {
|
||||
return _weightUnits.firstWhere((element) => element.id == DEFAULT_WEIGHT_UNIT);
|
||||
return _weightUnits.firstWhere((element) => element.id == WEIGHT_UNIT_KG_ID);
|
||||
}
|
||||
|
||||
List<RepetitionUnit> get repetitionUnits {
|
||||
@@ -95,7 +95,7 @@ class RoutinesProvider with ChangeNotifier {
|
||||
|
||||
/// Return the default weight unit (reps)
|
||||
RepetitionUnit get defaultRepetitionUnit {
|
||||
return _repetitionUnit.firstWhere((element) => element.id == REP_UNIT_REPETITIONS);
|
||||
return _repetitionUnit.firstWhere((element) => element.id == REP_UNIT_REPETITIONS_ID);
|
||||
}
|
||||
|
||||
List<Routine> getPlans() {
|
||||
@@ -477,7 +477,7 @@ class RoutinesProvider with ChangeNotifier {
|
||||
}
|
||||
|
||||
Future<void> deleteSlot(int slotId) async {
|
||||
final data = await baseProvider.deleteRequest(_slotsUrlPath, slotId);
|
||||
await baseProvider.deleteRequest(_slotsUrlPath, slotId);
|
||||
|
||||
for (final routine in _routines) {
|
||||
for (final day in routine.days) {
|
||||
@@ -508,8 +508,31 @@ class RoutinesProvider with ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<SlotEntry> addSlotEntry(SlotEntry entry) async {
|
||||
final data = await baseProvider.post(
|
||||
entry.toJson(),
|
||||
baseProvider.makeUrl(_slotEntriesUrlPath),
|
||||
);
|
||||
final newEntry = SlotEntry.fromJson(data);
|
||||
newEntry.exerciseObj = (await _exercises.fetchAndSetExercise(newEntry.exerciseId))!;
|
||||
|
||||
for (final routine in _routines) {
|
||||
for (final day in routine.days) {
|
||||
for (final slot in day.slots) {
|
||||
if (slot.id == entry.slotId) {
|
||||
slot.entries.add(newEntry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
Future<void> deleteSlotEntry(int id) async {
|
||||
await baseProvider.deleteRequest(_slotsUrlPath, id);
|
||||
await baseProvider.deleteRequest(_slotEntriesUrlPath, id);
|
||||
for (final routine in _routines) {
|
||||
for (final day in routine.days) {
|
||||
for (final slot in day.slots) {
|
||||
|
||||
@@ -18,19 +18,12 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:wger/helpers/consts.dart';
|
||||
import 'package:wger/models/exercises/exercise.dart';
|
||||
import 'package:wger/models/workouts/day.dart';
|
||||
import 'package:wger/models/workouts/slot.dart';
|
||||
import 'package:wger/models/workouts/slot_entry.dart';
|
||||
import 'package:wger/providers/exercises.dart';
|
||||
import 'package:wger/providers/routines.dart';
|
||||
import 'package:wger/screens/add_exercise_screen.dart';
|
||||
import 'package:wger/widgets/exercises/autocompleter.dart';
|
||||
import 'package:wger/widgets/exercises/images.dart';
|
||||
import 'package:wger/widgets/routines/forms.dart';
|
||||
|
||||
class SlotEntryForm extends StatefulWidget {
|
||||
final SlotEntry entry;
|
||||
@@ -73,7 +66,7 @@ class _SlotEntryFormState extends State<SlotEntryForm> {
|
||||
}
|
||||
|
||||
@override
|
||||
build(BuildContext context) {
|
||||
Widget build(BuildContext context) {
|
||||
final i18n = AppLocalizations.of(context);
|
||||
final languageCode = Localizations.localeOf(context).languageCode;
|
||||
|
||||
@@ -85,6 +78,7 @@ class _SlotEntryFormState extends State<SlotEntryForm> {
|
||||
title: Text(
|
||||
widget.entry.exerciseObj.getExercise(languageCode).name,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
// textAlign: TextAlign.center,
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -132,7 +126,7 @@ class _SlotEntryFormState extends State<SlotEntryForm> {
|
||||
const SizedBox(height: 5),
|
||||
OutlinedButton(
|
||||
key: const Key(SUBMIT_BUTTON_KEY_NAME),
|
||||
child: Text(AppLocalizations.of(context).save),
|
||||
child: Text(i18n.save),
|
||||
onPressed: () async {
|
||||
if (!_form.currentState!.validate()) {
|
||||
return;
|
||||
@@ -147,26 +141,58 @@ class _SlotEntryFormState extends State<SlotEntryForm> {
|
||||
provider.handleConfig(widget.entry, repsController.text, ConfigType.reps);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SlotDetailWidget extends StatelessWidget {
|
||||
class SlotDetailWidget extends StatefulWidget {
|
||||
final Slot slot;
|
||||
|
||||
const SlotDetailWidget(this.slot, {super.key});
|
||||
|
||||
@override
|
||||
build(BuildContext context) => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...slot.entries.map((entry) => SlotEntryForm(entry)),
|
||||
const SizedBox(height: 30),
|
||||
],
|
||||
);
|
||||
State<SlotDetailWidget> createState() => _SlotDetailWidgetState();
|
||||
}
|
||||
|
||||
class _SlotDetailWidgetState extends State<SlotDetailWidget> {
|
||||
bool _addSuperset = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final i18n = AppLocalizations.of(context);
|
||||
final provider = context.read<RoutinesProvider>();
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
...widget.slot.entries.map((entry) => SlotEntryForm(entry)),
|
||||
const SizedBox(height: 10),
|
||||
if (_addSuperset || widget.slot.entries.isEmpty)
|
||||
ExerciseAutocompleter(
|
||||
onExerciseSelected: (exercise) async {
|
||||
final SlotEntry entry = SlotEntry.withData(
|
||||
slotId: widget.slot.id!,
|
||||
order: widget.slot.entries.length + 1,
|
||||
exercise: exercise,
|
||||
);
|
||||
|
||||
_addSuperset = false;
|
||||
await provider.addSlotEntry(entry);
|
||||
},
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_addSuperset = !_addSuperset;
|
||||
});
|
||||
},
|
||||
child: Text(i18n.addSuperset)),
|
||||
const SizedBox(height: 5),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ReorderableSlotList extends StatefulWidget {
|
||||
@@ -185,6 +211,8 @@ class _SlotFormWidgetStateNg extends State<ReorderableSlotList> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final i18n = AppLocalizations.of(context);
|
||||
|
||||
final provider = context.read<RoutinesProvider>();
|
||||
|
||||
final languageCode = Localizations.localeOf(context).languageCode;
|
||||
@@ -213,34 +241,31 @@ class _SlotFormWidgetStateNg extends State<ReorderableSlotList> {
|
||||
itemCount: widget.slots.length,
|
||||
itemBuilder: (context, index) {
|
||||
final slot = widget.slots[index];
|
||||
final isSlotSelected = slot.id == selectedSlotId;
|
||||
final isCurrentSlotSelected = slot.id == selectedSlotId;
|
||||
|
||||
return Card(
|
||||
color: slot.entries.isEmpty ? Theme.of(context).colorScheme.inversePrimary : null,
|
||||
key: ValueKey(slot.id),
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text('Set ${index + 1}'),
|
||||
tileColor: isSlotSelected ? Theme.of(context).highlightColor : null,
|
||||
title: Text(i18n.setNr(index + 1)),
|
||||
tileColor: isCurrentSlotSelected ? Theme.of(context).highlightColor : null,
|
||||
leading: selectedSlotId == null
|
||||
? ReorderableDragStartListener(
|
||||
index: index,
|
||||
child: const Icon(Icons.drag_handle),
|
||||
)
|
||||
: const Icon(Icons.block),
|
||||
// : const Icon(Icons.filter_list_off),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (slot.entries.isEmpty)
|
||||
Card(
|
||||
child: Text('no exercises'),
|
||||
color: Theme.of(context).splashColor,
|
||||
subtitle: slot.entries.isEmpty
|
||||
? Text(i18n.setHasNoExercises)
|
||||
: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...slot.entries
|
||||
.map((e) => Text(e.exerciseObj.getExercise(languageCode).name)),
|
||||
],
|
||||
),
|
||||
...slot.entries
|
||||
.map((e) => Text(e.exerciseObj.getExercise(languageCode).name)),
|
||||
],
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@@ -253,19 +278,21 @@ class _SlotFormWidgetStateNg extends State<ReorderableSlotList> {
|
||||
selectedSlotId = slot.id;
|
||||
}
|
||||
});
|
||||
// widget.onDaySelected(day.id!);
|
||||
},
|
||||
icon:
|
||||
isSlotSelected ? const Icon(Icons.edit_off) : const Icon(Icons.edit),
|
||||
icon: isCurrentSlotSelected
|
||||
? const Icon(Icons.edit_off)
|
||||
: const Icon(Icons.edit),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () => provider.deleteSlot(slot.id!),
|
||||
),
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () async {
|
||||
selectedSlotId = null;
|
||||
await provider.deleteSlot(slot.id!);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (isSlotSelected) SlotDetailWidget(slot),
|
||||
if (isCurrentSlotSelected) SlotDetailWidget(slot),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -290,369 +317,20 @@ class _SlotFormWidgetStateNg extends State<ReorderableSlotList> {
|
||||
ListTile(
|
||||
leading: const Icon(Icons.add),
|
||||
title: Text(
|
||||
AppLocalizations.of(context).newSet,
|
||||
i18n.newSet,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
onTap: () {
|
||||
provider.addSlot(Slot.withData(day: widget.dayId, order: widget.slots.length + 1));
|
||||
onTap: () async {
|
||||
final newSlot = await provider.addSlot(Slot.withData(
|
||||
day: widget.dayId,
|
||||
order: widget.slots.length + 1,
|
||||
));
|
||||
setState(() {
|
||||
selectedSlotId = newSlot.id;
|
||||
});
|
||||
},
|
||||
),
|
||||
if (selectedSlot != null) Text(selectedSlot.id!.toString()),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SlotFormWidget extends StatefulWidget {
|
||||
final Day _day;
|
||||
late final Slot _slot;
|
||||
|
||||
SlotFormWidget(this._day, [Slot? set]) {
|
||||
_slot = set ?? Slot.withData(day: _day.id, order: _day.slots.length);
|
||||
}
|
||||
|
||||
@override
|
||||
_SlotFormWidgetState createState() => _SlotFormWidgetState();
|
||||
}
|
||||
|
||||
class _SlotFormWidgetState extends State<SlotFormWidget> {
|
||||
double _currentSetSliderValue = Slot.DEFAULT_NR_SETS.toDouble();
|
||||
bool _detailed = false;
|
||||
bool _searchEnglish = true;
|
||||
|
||||
// Form stuff
|
||||
final GlobalKey<FormState> _formKey = GlobalKey();
|
||||
final _exercisesController = TextEditingController();
|
||||
|
||||
/// Removes an exercise from the current set
|
||||
void removeExerciseBase(Exercise base) {
|
||||
setState(() {
|
||||
widget._slot.removeExercise(base);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_exercisesController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// Adds an exercise to the current set
|
||||
void addExercise(Exercise base) {
|
||||
setState(() {
|
||||
widget._slot.addExerciseBase(base);
|
||||
addSettings();
|
||||
});
|
||||
}
|
||||
|
||||
/// Adds settings to the set
|
||||
void addSettings() {
|
||||
final workoutProvider = context.read<RoutinesProvider>();
|
||||
|
||||
widget._slot.entries = [];
|
||||
int order = 0;
|
||||
for (final exercise in widget._slot.exercisesObj) {
|
||||
order++;
|
||||
// for (int loop = 0; loop < widget._set.sets; loop++) {
|
||||
final SlotEntry setting = SlotEntry.empty();
|
||||
setting.order = order;
|
||||
setting.exercise = exercise;
|
||||
setting.weightUnit = workoutProvider.defaultWeightUnit;
|
||||
setting.repetitionUnit = workoutProvider.defaultRepetitionUnit;
|
||||
|
||||
widget._slot.entries.add(setting);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
child: Column(
|
||||
//crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(AppLocalizations.of(context).nrOfSets(_currentSetSliderValue.round())),
|
||||
Slider(
|
||||
value: _currentSetSliderValue,
|
||||
min: 1,
|
||||
max: 10,
|
||||
divisions: 10,
|
||||
label: _currentSetSliderValue.round().toString(),
|
||||
onChanged: (double value) {
|
||||
setState(() {
|
||||
// widget._set.sets = value.round();
|
||||
_currentSetSliderValue = value;
|
||||
addSettings();
|
||||
});
|
||||
},
|
||||
inactiveColor: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
if (widget._slot.entries.isNotEmpty)
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context).setUnitsAndRir),
|
||||
value: _detailed,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_detailed = !_detailed;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Card(
|
||||
child: Column(
|
||||
children: [
|
||||
TypeAheadField<Exercise>(
|
||||
key: const Key('field-typeahead'),
|
||||
decorationBuilder: (context, child) {
|
||||
return Material(
|
||||
type: MaterialType.card,
|
||||
elevation: 4,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
controller: _exercisesController,
|
||||
builder: (context, controller, focusNode) {
|
||||
return TextFormField(
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
// autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context).searchExercise,
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
suffixIcon: IconButton(
|
||||
icon: const Icon(Icons.help),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Text(AppLocalizations.of(context).selectExercises),
|
||||
// const SizedBox(height: 10),
|
||||
// Text(AppLocalizations.of(context).sameRepetitions),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
MaterialLocalizations.of(context).closeButtonLabel,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
errorMaxLines: 2,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
validator: (value) {
|
||||
// At least one exercise must be selected
|
||||
if (widget._slot.exercisesIds.isEmpty) {
|
||||
return AppLocalizations.of(context).selectExercise;
|
||||
}
|
||||
|
||||
// At least one setting has to be filled in
|
||||
// if (widget._set.entries
|
||||
// .where((s) => s.weight == null && s.reps == null)
|
||||
// .length ==
|
||||
// widget._set.entries.length) {
|
||||
// return AppLocalizations.of(context).enterRepetitionsOrWeight;
|
||||
// }
|
||||
return null;
|
||||
},
|
||||
);
|
||||
},
|
||||
suggestionsCallback: (pattern) {
|
||||
if (pattern == '') {
|
||||
return null;
|
||||
}
|
||||
return context.read<ExercisesProvider>().searchExercise(
|
||||
pattern,
|
||||
languageCode: Localizations.localeOf(context).languageCode,
|
||||
searchEnglish: _searchEnglish,
|
||||
);
|
||||
},
|
||||
itemBuilder: (
|
||||
BuildContext context,
|
||||
Exercise exerciseSuggestion,
|
||||
) =>
|
||||
ListTile(
|
||||
key: Key('exercise-${exerciseSuggestion.id}'),
|
||||
leading: SizedBox(
|
||||
width: 45,
|
||||
child: ExerciseImageWidget(
|
||||
image: exerciseSuggestion.getMainImage,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
exerciseSuggestion
|
||||
.getExercise(Localizations.localeOf(context).languageCode)
|
||||
.name,
|
||||
),
|
||||
subtitle: Text(
|
||||
'${exerciseSuggestion.category!.name} / ${exerciseSuggestion.equipment.map((e) => e.name).join(', ')}',
|
||||
),
|
||||
),
|
||||
emptyBuilder: (context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(AppLocalizations.of(context).noMatchingExerciseFound),
|
||||
),
|
||||
ListTile(
|
||||
title: OutlinedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(AddExerciseScreen.routeName);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context).contributeExercise),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
transitionBuilder: (context, animation, child) => FadeTransition(
|
||||
opacity: CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
onSelected: (Exercise exerciseSuggestion) {
|
||||
// SuggestionsController.of(context).select(exerciseSuggestion);
|
||||
|
||||
addExercise(exerciseSuggestion);
|
||||
_exercisesController.text = '';
|
||||
},
|
||||
),
|
||||
if (Localizations.localeOf(context).languageCode != LANGUAGE_SHORT_ENGLISH)
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context).searchNamesInEnglish),
|
||||
value: _searchEnglish,
|
||||
onChanged: (_) {
|
||||
setState(() {
|
||||
_searchEnglish = !_searchEnglish;
|
||||
});
|
||||
},
|
||||
dense: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context).comment,
|
||||
errorMaxLines: 2,
|
||||
),
|
||||
keyboardType: TextInputType.text,
|
||||
validator: (value) {
|
||||
const minLength = 0;
|
||||
const maxLength = 200;
|
||||
if (value!.length > maxLength) {
|
||||
return AppLocalizations.of(context).enterCharacters(minLength, maxLength);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (newValue) {
|
||||
widget._slot.comment = newValue!;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
...widget._slot.exercisesObj.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final exercise = entry.value;
|
||||
final showSupersetInfo = (index + 1) < widget._slot.exercisesObj.length;
|
||||
final settings =
|
||||
widget._slot.entries.where((e) => e.exerciseObj.id == exercise.id).toList();
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
ExerciseSetting(
|
||||
exercise,
|
||||
settings,
|
||||
_detailed,
|
||||
_currentSetSliderValue,
|
||||
removeExerciseBase,
|
||||
),
|
||||
if (showSupersetInfo)
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(3.0),
|
||||
child: Text('+'),
|
||||
),
|
||||
if (showSupersetInfo) Text(AppLocalizations.of(context).supersetWith),
|
||||
if (showSupersetInfo)
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(3.0),
|
||||
child: Text('+'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
ElevatedButton(
|
||||
key: const Key(SUBMIT_BUTTON_KEY_NAME),
|
||||
child: Text(AppLocalizations.of(context).save),
|
||||
onPressed: () async {
|
||||
final isValid = _formKey.currentState!.validate();
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
_formKey.currentState!.save();
|
||||
|
||||
final workoutProvider = Provider.of<RoutinesProvider>(
|
||||
context,
|
||||
listen: false,
|
||||
);
|
||||
|
||||
// Save set
|
||||
final Slot setDb = await workoutProvider.addSlot(widget._slot);
|
||||
widget._slot.id = setDb.id;
|
||||
|
||||
// Remove unused settings
|
||||
// widget._set.entries.removeWhere((s) => s.weight == null && s.reps == null);
|
||||
|
||||
// Save remaining settings
|
||||
for (final setting in widget._slot.entries) {
|
||||
setting.slotId = setDb.id!;
|
||||
setting.comment = '';
|
||||
|
||||
final SlotEntry settingDb = await workoutProvider.addSetting(setting);
|
||||
setting.id = settingDb.id;
|
||||
}
|
||||
|
||||
// Add to workout day
|
||||
workoutProvider.fetchComputedSettings(widget._slot);
|
||||
widget._day.slots.add(widget._slot);
|
||||
|
||||
// Close the bottom sheet
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,9 @@ dev_dependencies:
|
||||
freezed: ^2.5.2
|
||||
golden_toolkit: ^0.15.0
|
||||
|
||||
# Script to read out unused translations
|
||||
#translations_cleaner: ^0.0.5
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'dart:ui' as _i17;
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
import 'package:mockito/src/dummies.dart' as _i16;
|
||||
import 'package:wger/models/exercises/exercise.dart' as _i15;
|
||||
import 'package:wger/models/workouts/base_config.dart' as _i8;
|
||||
import 'package:wger/models/workouts/base_config.dart' as _i9;
|
||||
import 'package:wger/models/workouts/day.dart' as _i6;
|
||||
import 'package:wger/models/workouts/day_data.dart' as _i14;
|
||||
import 'package:wger/models/workouts/log.dart' as _i11;
|
||||
@@ -17,7 +17,7 @@ import 'package:wger/models/workouts/repetition_unit.dart' as _i4;
|
||||
import 'package:wger/models/workouts/routine.dart' as _i5;
|
||||
import 'package:wger/models/workouts/session.dart' as _i10;
|
||||
import 'package:wger/models/workouts/slot.dart' as _i7;
|
||||
import 'package:wger/models/workouts/slot_entry.dart' as _i9;
|
||||
import 'package:wger/models/workouts/slot_entry.dart' as _i8;
|
||||
import 'package:wger/models/workouts/weight_unit.dart' as _i3;
|
||||
import 'package:wger/providers/base_provider.dart' as _i2;
|
||||
import 'package:wger/providers/routines.dart' as _i12;
|
||||
@@ -95,8 +95,8 @@ class _FakeSlot_5 extends _i1.SmartFake implements _i7.Slot {
|
||||
);
|
||||
}
|
||||
|
||||
class _FakeBaseConfig_6 extends _i1.SmartFake implements _i8.BaseConfig {
|
||||
_FakeBaseConfig_6(
|
||||
class _FakeSlotEntry_6 extends _i1.SmartFake implements _i8.SlotEntry {
|
||||
_FakeSlotEntry_6(
|
||||
Object parent,
|
||||
Invocation parentInvocation,
|
||||
) : super(
|
||||
@@ -105,8 +105,8 @@ class _FakeBaseConfig_6 extends _i1.SmartFake implements _i8.BaseConfig {
|
||||
);
|
||||
}
|
||||
|
||||
class _FakeSlotEntry_7 extends _i1.SmartFake implements _i9.SlotEntry {
|
||||
_FakeSlotEntry_7(
|
||||
class _FakeBaseConfig_7 extends _i1.SmartFake implements _i9.BaseConfig {
|
||||
_FakeBaseConfig_7(
|
||||
Object parent,
|
||||
Invocation parentInvocation,
|
||||
) : super(
|
||||
@@ -490,6 +490,21 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
returnValueForMissingStub: _i13.Future<void>.value(),
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@override
|
||||
_i13.Future<_i8.SlotEntry> addSlotEntry(_i8.SlotEntry? entry) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#addSlotEntry,
|
||||
[entry],
|
||||
),
|
||||
returnValue: _i13.Future<_i8.SlotEntry>.value(_FakeSlotEntry_6(
|
||||
this,
|
||||
Invocation.method(
|
||||
#addSlotEntry,
|
||||
[entry],
|
||||
),
|
||||
)),
|
||||
) as _i13.Future<_i8.SlotEntry>);
|
||||
|
||||
@override
|
||||
_i13.Future<void> deleteSlotEntry(int? id) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -501,7 +516,7 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@override
|
||||
String getConfigUrl(_i9.ConfigType? type) => (super.noSuchMethod(
|
||||
String getConfigUrl(_i8.ConfigType? type) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getConfigUrl,
|
||||
[type],
|
||||
@@ -516,9 +531,9 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as String);
|
||||
|
||||
@override
|
||||
_i13.Future<_i8.BaseConfig> editConfig(
|
||||
_i8.BaseConfig? config,
|
||||
_i9.ConfigType? type,
|
||||
_i13.Future<_i9.BaseConfig> editConfig(
|
||||
_i9.BaseConfig? config,
|
||||
_i8.ConfigType? type,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -528,7 +543,7 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
type,
|
||||
],
|
||||
),
|
||||
returnValue: _i13.Future<_i8.BaseConfig>.value(_FakeBaseConfig_6(
|
||||
returnValue: _i13.Future<_i9.BaseConfig>.value(_FakeBaseConfig_7(
|
||||
this,
|
||||
Invocation.method(
|
||||
#editConfig,
|
||||
@@ -538,12 +553,12 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
],
|
||||
),
|
||||
)),
|
||||
) as _i13.Future<_i8.BaseConfig>);
|
||||
) as _i13.Future<_i9.BaseConfig>);
|
||||
|
||||
@override
|
||||
_i13.Future<_i8.BaseConfig> addConfig(
|
||||
_i8.BaseConfig? config,
|
||||
_i9.ConfigType? type,
|
||||
_i13.Future<_i9.BaseConfig> addConfig(
|
||||
_i9.BaseConfig? config,
|
||||
_i8.ConfigType? type,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -553,7 +568,7 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
type,
|
||||
],
|
||||
),
|
||||
returnValue: _i13.Future<_i8.BaseConfig>.value(_FakeBaseConfig_6(
|
||||
returnValue: _i13.Future<_i9.BaseConfig>.value(_FakeBaseConfig_7(
|
||||
this,
|
||||
Invocation.method(
|
||||
#addConfig,
|
||||
@@ -563,12 +578,12 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
],
|
||||
),
|
||||
)),
|
||||
) as _i13.Future<_i8.BaseConfig>);
|
||||
) as _i13.Future<_i9.BaseConfig>);
|
||||
|
||||
@override
|
||||
_i13.Future<void> deleteConfig(
|
||||
int? id,
|
||||
_i9.ConfigType? type,
|
||||
_i8.ConfigType? type,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -584,9 +599,9 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
|
||||
@override
|
||||
_i13.Future<void> handleConfig(
|
||||
_i9.SlotEntry? entry,
|
||||
_i8.SlotEntry? entry,
|
||||
String? input,
|
||||
_i9.ConfigType? type,
|
||||
_i8.ConfigType? type,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -612,19 +627,19 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@override
|
||||
_i13.Future<_i9.SlotEntry> addSetting(_i9.SlotEntry? workoutSetting) => (super.noSuchMethod(
|
||||
_i13.Future<_i8.SlotEntry> addSetting(_i8.SlotEntry? workoutSetting) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#addSetting,
|
||||
[workoutSetting],
|
||||
),
|
||||
returnValue: _i13.Future<_i9.SlotEntry>.value(_FakeSlotEntry_7(
|
||||
returnValue: _i13.Future<_i8.SlotEntry>.value(_FakeSlotEntry_6(
|
||||
this,
|
||||
Invocation.method(
|
||||
#addSetting,
|
||||
[workoutSetting],
|
||||
),
|
||||
)),
|
||||
) as _i13.Future<_i9.SlotEntry>);
|
||||
) as _i13.Future<_i8.SlotEntry>);
|
||||
|
||||
@override
|
||||
_i13.Future<dynamic> fetchSessionData() => (super.noSuchMethod(
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'dart:ui' as _i17;
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
import 'package:mockito/src/dummies.dart' as _i16;
|
||||
import 'package:wger/models/exercises/exercise.dart' as _i15;
|
||||
import 'package:wger/models/workouts/base_config.dart' as _i8;
|
||||
import 'package:wger/models/workouts/base_config.dart' as _i9;
|
||||
import 'package:wger/models/workouts/day.dart' as _i6;
|
||||
import 'package:wger/models/workouts/day_data.dart' as _i14;
|
||||
import 'package:wger/models/workouts/log.dart' as _i11;
|
||||
@@ -17,7 +17,7 @@ import 'package:wger/models/workouts/repetition_unit.dart' as _i4;
|
||||
import 'package:wger/models/workouts/routine.dart' as _i5;
|
||||
import 'package:wger/models/workouts/session.dart' as _i10;
|
||||
import 'package:wger/models/workouts/slot.dart' as _i7;
|
||||
import 'package:wger/models/workouts/slot_entry.dart' as _i9;
|
||||
import 'package:wger/models/workouts/slot_entry.dart' as _i8;
|
||||
import 'package:wger/models/workouts/weight_unit.dart' as _i3;
|
||||
import 'package:wger/providers/base_provider.dart' as _i2;
|
||||
import 'package:wger/providers/routines.dart' as _i12;
|
||||
@@ -95,8 +95,8 @@ class _FakeSlot_5 extends _i1.SmartFake implements _i7.Slot {
|
||||
);
|
||||
}
|
||||
|
||||
class _FakeBaseConfig_6 extends _i1.SmartFake implements _i8.BaseConfig {
|
||||
_FakeBaseConfig_6(
|
||||
class _FakeSlotEntry_6 extends _i1.SmartFake implements _i8.SlotEntry {
|
||||
_FakeSlotEntry_6(
|
||||
Object parent,
|
||||
Invocation parentInvocation,
|
||||
) : super(
|
||||
@@ -105,8 +105,8 @@ class _FakeBaseConfig_6 extends _i1.SmartFake implements _i8.BaseConfig {
|
||||
);
|
||||
}
|
||||
|
||||
class _FakeSlotEntry_7 extends _i1.SmartFake implements _i9.SlotEntry {
|
||||
_FakeSlotEntry_7(
|
||||
class _FakeBaseConfig_7 extends _i1.SmartFake implements _i9.BaseConfig {
|
||||
_FakeBaseConfig_7(
|
||||
Object parent,
|
||||
Invocation parentInvocation,
|
||||
) : super(
|
||||
@@ -490,6 +490,21 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
returnValueForMissingStub: _i13.Future<void>.value(),
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@override
|
||||
_i13.Future<_i8.SlotEntry> addSlotEntry(_i8.SlotEntry? entry) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#addSlotEntry,
|
||||
[entry],
|
||||
),
|
||||
returnValue: _i13.Future<_i8.SlotEntry>.value(_FakeSlotEntry_6(
|
||||
this,
|
||||
Invocation.method(
|
||||
#addSlotEntry,
|
||||
[entry],
|
||||
),
|
||||
)),
|
||||
) as _i13.Future<_i8.SlotEntry>);
|
||||
|
||||
@override
|
||||
_i13.Future<void> deleteSlotEntry(int? id) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -501,7 +516,7 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@override
|
||||
String getConfigUrl(_i9.ConfigType? type) => (super.noSuchMethod(
|
||||
String getConfigUrl(_i8.ConfigType? type) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getConfigUrl,
|
||||
[type],
|
||||
@@ -516,9 +531,9 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as String);
|
||||
|
||||
@override
|
||||
_i13.Future<_i8.BaseConfig> editConfig(
|
||||
_i8.BaseConfig? config,
|
||||
_i9.ConfigType? type,
|
||||
_i13.Future<_i9.BaseConfig> editConfig(
|
||||
_i9.BaseConfig? config,
|
||||
_i8.ConfigType? type,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -528,7 +543,7 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
type,
|
||||
],
|
||||
),
|
||||
returnValue: _i13.Future<_i8.BaseConfig>.value(_FakeBaseConfig_6(
|
||||
returnValue: _i13.Future<_i9.BaseConfig>.value(_FakeBaseConfig_7(
|
||||
this,
|
||||
Invocation.method(
|
||||
#editConfig,
|
||||
@@ -538,12 +553,12 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
],
|
||||
),
|
||||
)),
|
||||
) as _i13.Future<_i8.BaseConfig>);
|
||||
) as _i13.Future<_i9.BaseConfig>);
|
||||
|
||||
@override
|
||||
_i13.Future<_i8.BaseConfig> addConfig(
|
||||
_i8.BaseConfig? config,
|
||||
_i9.ConfigType? type,
|
||||
_i13.Future<_i9.BaseConfig> addConfig(
|
||||
_i9.BaseConfig? config,
|
||||
_i8.ConfigType? type,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -553,7 +568,7 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
type,
|
||||
],
|
||||
),
|
||||
returnValue: _i13.Future<_i8.BaseConfig>.value(_FakeBaseConfig_6(
|
||||
returnValue: _i13.Future<_i9.BaseConfig>.value(_FakeBaseConfig_7(
|
||||
this,
|
||||
Invocation.method(
|
||||
#addConfig,
|
||||
@@ -563,12 +578,12 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
],
|
||||
),
|
||||
)),
|
||||
) as _i13.Future<_i8.BaseConfig>);
|
||||
) as _i13.Future<_i9.BaseConfig>);
|
||||
|
||||
@override
|
||||
_i13.Future<void> deleteConfig(
|
||||
int? id,
|
||||
_i9.ConfigType? type,
|
||||
_i8.ConfigType? type,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -584,9 +599,9 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
|
||||
@override
|
||||
_i13.Future<void> handleConfig(
|
||||
_i9.SlotEntry? entry,
|
||||
_i8.SlotEntry? entry,
|
||||
String? input,
|
||||
_i9.ConfigType? type,
|
||||
_i8.ConfigType? type,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -612,19 +627,19 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@override
|
||||
_i13.Future<_i9.SlotEntry> addSetting(_i9.SlotEntry? workoutSetting) => (super.noSuchMethod(
|
||||
_i13.Future<_i8.SlotEntry> addSetting(_i8.SlotEntry? workoutSetting) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#addSetting,
|
||||
[workoutSetting],
|
||||
),
|
||||
returnValue: _i13.Future<_i9.SlotEntry>.value(_FakeSlotEntry_7(
|
||||
returnValue: _i13.Future<_i8.SlotEntry>.value(_FakeSlotEntry_6(
|
||||
this,
|
||||
Invocation.method(
|
||||
#addSetting,
|
||||
[workoutSetting],
|
||||
),
|
||||
)),
|
||||
) as _i13.Future<_i9.SlotEntry>);
|
||||
) as _i13.Future<_i8.SlotEntry>);
|
||||
|
||||
@override
|
||||
_i13.Future<dynamic> fetchSessionData() => (super.noSuchMethod(
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'dart:ui' as _i17;
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
import 'package:mockito/src/dummies.dart' as _i16;
|
||||
import 'package:wger/models/exercises/exercise.dart' as _i15;
|
||||
import 'package:wger/models/workouts/base_config.dart' as _i8;
|
||||
import 'package:wger/models/workouts/base_config.dart' as _i9;
|
||||
import 'package:wger/models/workouts/day.dart' as _i6;
|
||||
import 'package:wger/models/workouts/day_data.dart' as _i14;
|
||||
import 'package:wger/models/workouts/log.dart' as _i11;
|
||||
@@ -17,7 +17,7 @@ import 'package:wger/models/workouts/repetition_unit.dart' as _i4;
|
||||
import 'package:wger/models/workouts/routine.dart' as _i5;
|
||||
import 'package:wger/models/workouts/session.dart' as _i10;
|
||||
import 'package:wger/models/workouts/slot.dart' as _i7;
|
||||
import 'package:wger/models/workouts/slot_entry.dart' as _i9;
|
||||
import 'package:wger/models/workouts/slot_entry.dart' as _i8;
|
||||
import 'package:wger/models/workouts/weight_unit.dart' as _i3;
|
||||
import 'package:wger/providers/base_provider.dart' as _i2;
|
||||
import 'package:wger/providers/routines.dart' as _i12;
|
||||
@@ -95,8 +95,8 @@ class _FakeSlot_5 extends _i1.SmartFake implements _i7.Slot {
|
||||
);
|
||||
}
|
||||
|
||||
class _FakeBaseConfig_6 extends _i1.SmartFake implements _i8.BaseConfig {
|
||||
_FakeBaseConfig_6(
|
||||
class _FakeSlotEntry_6 extends _i1.SmartFake implements _i8.SlotEntry {
|
||||
_FakeSlotEntry_6(
|
||||
Object parent,
|
||||
Invocation parentInvocation,
|
||||
) : super(
|
||||
@@ -105,8 +105,8 @@ class _FakeBaseConfig_6 extends _i1.SmartFake implements _i8.BaseConfig {
|
||||
);
|
||||
}
|
||||
|
||||
class _FakeSlotEntry_7 extends _i1.SmartFake implements _i9.SlotEntry {
|
||||
_FakeSlotEntry_7(
|
||||
class _FakeBaseConfig_7 extends _i1.SmartFake implements _i9.BaseConfig {
|
||||
_FakeBaseConfig_7(
|
||||
Object parent,
|
||||
Invocation parentInvocation,
|
||||
) : super(
|
||||
@@ -490,6 +490,21 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
returnValueForMissingStub: _i13.Future<void>.value(),
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@override
|
||||
_i13.Future<_i8.SlotEntry> addSlotEntry(_i8.SlotEntry? entry) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#addSlotEntry,
|
||||
[entry],
|
||||
),
|
||||
returnValue: _i13.Future<_i8.SlotEntry>.value(_FakeSlotEntry_6(
|
||||
this,
|
||||
Invocation.method(
|
||||
#addSlotEntry,
|
||||
[entry],
|
||||
),
|
||||
)),
|
||||
) as _i13.Future<_i8.SlotEntry>);
|
||||
|
||||
@override
|
||||
_i13.Future<void> deleteSlotEntry(int? id) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -501,7 +516,7 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@override
|
||||
String getConfigUrl(_i9.ConfigType? type) => (super.noSuchMethod(
|
||||
String getConfigUrl(_i8.ConfigType? type) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getConfigUrl,
|
||||
[type],
|
||||
@@ -516,9 +531,9 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as String);
|
||||
|
||||
@override
|
||||
_i13.Future<_i8.BaseConfig> editConfig(
|
||||
_i8.BaseConfig? config,
|
||||
_i9.ConfigType? type,
|
||||
_i13.Future<_i9.BaseConfig> editConfig(
|
||||
_i9.BaseConfig? config,
|
||||
_i8.ConfigType? type,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -528,7 +543,7 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
type,
|
||||
],
|
||||
),
|
||||
returnValue: _i13.Future<_i8.BaseConfig>.value(_FakeBaseConfig_6(
|
||||
returnValue: _i13.Future<_i9.BaseConfig>.value(_FakeBaseConfig_7(
|
||||
this,
|
||||
Invocation.method(
|
||||
#editConfig,
|
||||
@@ -538,12 +553,12 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
],
|
||||
),
|
||||
)),
|
||||
) as _i13.Future<_i8.BaseConfig>);
|
||||
) as _i13.Future<_i9.BaseConfig>);
|
||||
|
||||
@override
|
||||
_i13.Future<_i8.BaseConfig> addConfig(
|
||||
_i8.BaseConfig? config,
|
||||
_i9.ConfigType? type,
|
||||
_i13.Future<_i9.BaseConfig> addConfig(
|
||||
_i9.BaseConfig? config,
|
||||
_i8.ConfigType? type,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -553,7 +568,7 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
type,
|
||||
],
|
||||
),
|
||||
returnValue: _i13.Future<_i8.BaseConfig>.value(_FakeBaseConfig_6(
|
||||
returnValue: _i13.Future<_i9.BaseConfig>.value(_FakeBaseConfig_7(
|
||||
this,
|
||||
Invocation.method(
|
||||
#addConfig,
|
||||
@@ -563,12 +578,12 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
],
|
||||
),
|
||||
)),
|
||||
) as _i13.Future<_i8.BaseConfig>);
|
||||
) as _i13.Future<_i9.BaseConfig>);
|
||||
|
||||
@override
|
||||
_i13.Future<void> deleteConfig(
|
||||
int? id,
|
||||
_i9.ConfigType? type,
|
||||
_i8.ConfigType? type,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -584,9 +599,9 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
|
||||
@override
|
||||
_i13.Future<void> handleConfig(
|
||||
_i9.SlotEntry? entry,
|
||||
_i8.SlotEntry? entry,
|
||||
String? input,
|
||||
_i9.ConfigType? type,
|
||||
_i8.ConfigType? type,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -612,19 +627,19 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@override
|
||||
_i13.Future<_i9.SlotEntry> addSetting(_i9.SlotEntry? workoutSetting) => (super.noSuchMethod(
|
||||
_i13.Future<_i8.SlotEntry> addSetting(_i8.SlotEntry? workoutSetting) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#addSetting,
|
||||
[workoutSetting],
|
||||
),
|
||||
returnValue: _i13.Future<_i9.SlotEntry>.value(_FakeSlotEntry_7(
|
||||
returnValue: _i13.Future<_i8.SlotEntry>.value(_FakeSlotEntry_6(
|
||||
this,
|
||||
Invocation.method(
|
||||
#addSetting,
|
||||
[workoutSetting],
|
||||
),
|
||||
)),
|
||||
) as _i13.Future<_i9.SlotEntry>);
|
||||
) as _i13.Future<_i8.SlotEntry>);
|
||||
|
||||
@override
|
||||
_i13.Future<dynamic> fetchSessionData() => (super.noSuchMethod(
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (C) 2020, 2021 wger Team
|
||||
*
|
||||
* wger Workout Manager is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wger Workout Manager is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:wger/helpers/consts.dart';
|
||||
import 'package:wger/models/workouts/day.dart';
|
||||
import 'package:wger/models/workouts/slot.dart';
|
||||
import 'package:wger/models/workouts/slot_entry.dart';
|
||||
import 'package:wger/providers/base_provider.dart';
|
||||
import 'package:wger/providers/exercises.dart';
|
||||
import 'package:wger/providers/routines.dart';
|
||||
import 'package:wger/widgets/routines/forms/slot.dart';
|
||||
|
||||
import '../../test_data/exercises.dart';
|
||||
import '../../test_data/routines.dart';
|
||||
import 'workout_set_form_test.mocks.dart';
|
||||
|
||||
@GenerateMocks([ExercisesProvider, WgerBaseProvider, RoutinesProvider])
|
||||
void main() {
|
||||
var mockWorkoutPlans = MockRoutinesProvider();
|
||||
final mockBaseProvider = MockWgerBaseProvider();
|
||||
final mockExerciseProvider = MockExercisesProvider();
|
||||
final workoutPlan = getWorkout();
|
||||
|
||||
Day day = Day();
|
||||
|
||||
setUp(() {
|
||||
day = workoutPlan.days.first;
|
||||
mockWorkoutPlans = MockRoutinesProvider();
|
||||
});
|
||||
|
||||
Widget createHomeScreen({locale = 'en'}) {
|
||||
return ChangeNotifierProvider<RoutinesProvider>(
|
||||
create: (context) => RoutinesProvider(
|
||||
mockBaseProvider,
|
||||
mockExerciseProvider,
|
||||
[workoutPlan],
|
||||
),
|
||||
child: ChangeNotifierProvider<ExercisesProvider>(
|
||||
create: (context) => mockExerciseProvider,
|
||||
child: MaterialApp(
|
||||
locale: Locale(locale),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
navigatorKey: GlobalKey<NavigatorState>(),
|
||||
home: Scaffold(body: SlotFormWidget(day)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('Test the widgets on the SetFormWidget', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(createHomeScreen());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
//TODO: why doesn't it find the typeahead?
|
||||
//expect(find.byType(TypeAheadFormField), findsOneWidget);
|
||||
expect(find.byType(Slider), findsOneWidget);
|
||||
expect(find.byKey(const Key(SUBMIT_BUTTON_KEY_NAME)), findsOneWidget);
|
||||
expect(find.byType(ElevatedButton), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Test creating a new set', (WidgetTester tester) async {
|
||||
when(mockWorkoutPlans.addSlot(any)).thenAnswer((_) => Future.value(Slot.empty()));
|
||||
when(mockWorkoutPlans.addSetting(any)).thenAnswer((_) => Future.value(SlotEntry.empty()));
|
||||
when(mockExerciseProvider.searchExercise(
|
||||
any,
|
||||
languageCode: anyNamed('languageCode'),
|
||||
searchEnglish: anyNamed('searchEnglish'),
|
||||
)).thenAnswer((_) => Future.value([getTestExercises().first]));
|
||||
|
||||
await tester.pumpWidget(createHomeScreen());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(find.byKey(const Key('field-typeahead')), 'exercise');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
//await tester.tap(find.byKey(const Key('exercise-1')));
|
||||
await tester.tap(find.byKey(const Key(SUBMIT_BUTTON_KEY_NAME)));
|
||||
|
||||
//verify(mockWorkoutPlans.addSet(any));
|
||||
//verify(mockWorkoutPlans.addSettinbg(any));
|
||||
//verify(mockWorkoutPlans.fetchSmartText(any, any));
|
||||
});
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user