Polish the crud workflow

This commit is contained in:
Roland Geider
2024-11-16 19:50:28 +01:00
parent a4f334e73d
commit f7461ab0e9
13 changed files with 299 additions and 2070 deletions

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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",

View File

@@ -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';
}

View File

@@ -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,
};

View File

@@ -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) {

View File

@@ -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();
}
},
),
],
),
),
],
),
);
}
}

View File

@@ -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

View File

@@ -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(

View File

@@ -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(

View File

@@ -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(

View File

@@ -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