Reuse AddExerciseTextArea in the image form

This commit is contained in:
Roland Geider
2025-10-08 14:05:50 +02:00
parent 0add2a6bd1
commit c63057fe35
14 changed files with 207 additions and 186 deletions

View File

@@ -145,4 +145,4 @@ const String API_RESULTS_PAGE_SIZE = '100';
const INTERPOLATION_MARKER = 123;
/// Creative Commons license IDs
const CC_BY_SA_4_ID = 4;
const CC_BY_SA_4_ID = 2;

View File

@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
/// The amount of characters an exercise description needs to have
const MIN_CHARS_DESCRIPTION = 40;
/// The amount of characters an exercise name needs to have
const MIN_CHARS_NAME = 5;
const MAX_CHARS_NAME = 40;
String? validateName(String? name, BuildContext context) {
if (name!.isEmpty) {
return AppLocalizations.of(context).enterValue;
}
if (name.length < MIN_CHARS_NAME || name.length > MAX_CHARS_NAME) {
return AppLocalizations.of(
context,
).enterCharacters(MIN_CHARS_NAME.toString(), MAX_CHARS_NAME.toString());
}
return null;
}
String? validateAuthorName(String? name, BuildContext context) {
if (name!.isEmpty) {
return AppLocalizations.of(context).enterValue;
}
return null;
}
String? validateExerciseDescription(String? name, BuildContext context) {
if (name!.isEmpty) {
return AppLocalizations.of(context).enterValue;
}
if (name.length < MIN_CHARS_DESCRIPTION) {
return AppLocalizations.of(context).enterMinCharacters(MIN_CHARS_DESCRIPTION.toString());
}
return null;
}

View File

@@ -741,19 +741,19 @@
"@imageDetailsLicenseTitleHint": {
"description": "Hint text for image title field"
},
"imageDetailsSourceLink": "Link to the source website, if available",
"imageDetailsSourceLink": "Link to the source website",
"@imageDetailsSourceLink": {
"description": "Label for source link field"
},
"imageDetailsAuthor": "Author(s)",
"@imageDetailsAuthor": {
"author": "Author(s)",
"@Author": {
"description": "Label for author field"
},
"imageDetailsAuthorHint": "Enter author name",
"@imageDetailsAuthorHint": {
"authorHint": "Enter author name",
"@authorHint": {
"description": "Hint text for author field"
},
"imageDetailsAuthorLink": "Link to author website or profile, if available",
"imageDetailsAuthorLink": "Link to author website or profile",
"@imageDetailsAuthorLink": {
"description": "Label for author link field"
},

View File

@@ -11,7 +11,7 @@ ExerciseCategory _$ExerciseCategoryFromJson(Map<String, dynamic> json) {
return ExerciseCategory(id: (json['id'] as num).toInt(), name: json['name'] as String);
}
ap<String, dynamic> _$ExerciseCategoryToJson(ExerciseCategory instance) => <String, dynamic>{
Map<String, dynamic> _$ExerciseCategoryToJson(ExerciseCategory instance) => <String, dynamic>{
'id': instance.id,
'name': instance.name,
};

View File

@@ -23,6 +23,7 @@ class AddExerciseProvider with ChangeNotifier {
List<ExerciseSubmissionImage> get exerciseImages => [..._exerciseImages];
String author = '';
String? exerciseNameEn;
String? exerciseNameTrans;
String? descriptionEn;

View File

@@ -1,22 +1,22 @@
import 'package:flutter/material.dart';
class AddExerciseTextArea extends StatelessWidget {
const AddExerciseTextArea({
AddExerciseTextArea({
super.key,
required this.onChange,
required this.title,
ValueChanged<String>? onChange,
this.helperText = '',
this.isRequired = true,
this.isMultiline = false,
this.initialValue = '',
this.validator,
this.onSaved,
});
}) : onChange = onChange ?? ((String value) {});
final ValueChanged<String> onChange;
final bool isRequired;
final bool isMultiline;
final String title;
final String helperText;
final String? initialValue;
final FormFieldValidator<String?>? validator;
final FormFieldSetter<String?>? onSaved;
@@ -28,6 +28,7 @@ class AddExerciseTextArea extends StatelessWidget {
return Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
initialValue: initialValue,
keyboardType: isMultiline ? TextInputType.multiline : TextInputType.text,
maxLines: isMultiline ? null : DEFAULT_LINES,
minLines: isMultiline ? MULTILINE_MIN_LINES : DEFAULT_LINES,
@@ -35,12 +36,11 @@ class AddExerciseTextArea extends StatelessWidget {
onSaved: onSaved,
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
),
border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
labelText: title,
alignLabelWithHint: true,
helperText: helperText,
helperMaxLines: 3,
),
onChanged: onChange,
),

View File

@@ -1,8 +1,12 @@
import 'package:flutter/material.dart';
import 'package:wger/core/validators.dart';
import 'package:wger/helpers/exercises/validators.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/models/exercises/exercise_submission_images.dart';
import 'package:wger/widgets/add_exercise/license_info_widget.dart';
import 'add_exercise_text_area.dart';
/// Form for collecting CC BY-SA 4.0 license metadata for exercise images
///
/// This form is displayed after image selection in Step 5 of exercise creation.
@@ -50,6 +54,11 @@ class _ImageDetailsFormState extends State<ImageDetailsForm> {
/// Currently selected image type
ImageType _selectedImageType = ImageType.photo;
@override
void initState() {
super.initState();
}
@override
void dispose() {
_titleController.dispose();
@@ -60,139 +69,78 @@ class _ImageDetailsFormState extends State<ImageDetailsForm> {
super.dispose();
}
/// Validates URL format
///
/// Returns error message if URL is invalid, null if valid or empty
String? _validateUrl(String? value) {
if (value == null || value.trim().isEmpty) {
return null; // Empty is OK (optional field)
}
final trimmedValue = value.trim();
// Check if starts with http:// or https://
if (!trimmedValue.startsWith('http://') && !trimmedValue.startsWith('https://')) {
return AppLocalizations.of(context).invalidUrl;
}
// Try to parse as URI
try {
final uri = Uri.parse(trimmedValue);
if (!uri.hasScheme || !uri.hasAuthority) {
return AppLocalizations.of(context).invalidUrl;
}
} catch (e) {
return AppLocalizations.of(context).invalidUrl;
}
return null;
}
/// Maps UI image type selection to API 'style' field value
///
/// API expects numeric string:
/// - PHOTO = '1'
/// - 3D = '2'
/// - LINE = '3'
/// - LOW-POLY = '4'
/// - OTHER = '5'
String _getStyleValue() {
switch (_selectedImageType) {
case 'PHOTO':
return '1';
case '3D':
return '2';
case 'LINE':
return '3';
case 'LOW-POLY':
return '4';
case 'OTHER':
return '5';
default:
return '1'; // Default to PHOTO if unknown
}
}
@override
Widget build(BuildContext context) {
final i18n = AppLocalizations.of(context);
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
spacing: 8,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
AppLocalizations.of(context).imageDetailsTitle,
style: Theme.of(
context,
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
style: Theme.of(context).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
const SizedBox(height: 8),
_buildImagePreview(),
const SizedBox(height: 24),
// License title field - helps identify the image
_buildTextField(
controller: _titleController,
label: AppLocalizations.of(context).imageDetailsLicenseTitle,
hint: AppLocalizations.of(context).imageDetailsLicenseTitleHint,
),
const SizedBox(height: 16),
// Source URL - where the image was found (license_object_url in API)
_buildTextField(
controller: _sourceLinkController,
label: AppLocalizations.of(context).imageDetailsSourceLink,
hint: 'https://example.com',
keyboardType: TextInputType.url,
validator: _validateUrl,
),
const SizedBox(height: 16),
const SizedBox(height: 8),
// Author name - required for proper CC BY-SA attribution
_buildTextField(
controller: _authorController,
label: AppLocalizations.of(context).imageDetailsAuthor,
hint: AppLocalizations.of(context).imageDetailsAuthorHint,
AddExerciseTextArea(
title: '${AppLocalizations.of(context).author}*',
initialValue: widget.submissionImage.author,
onSaved: (value) => widget.submissionImage.author = value!,
validator: (name) => validateAuthorName(name, context),
),
// License title field - helps identify the image
AddExerciseTextArea(
title: AppLocalizations.of(context).imageDetailsLicenseTitle,
helperText: AppLocalizations.of(context).imageDetailsLicenseTitleHint,
initialValue: widget.submissionImage.title,
onSaved: (value) => widget.submissionImage.title = value,
),
// Source URL - where the image was found (license_object_url in API)
AddExerciseTextArea(
title: AppLocalizations.of(context).imageDetailsSourceLink,
initialValue: widget.submissionImage.sourceUrl,
onSaved: (value) => widget.submissionImage.sourceUrl = value,
validator: (value) => validateUrl(value, i18n, required: false),
),
const SizedBox(height: 16),
// Author's website/profile URL
_buildTextField(
controller: _authorLinkController,
label: AppLocalizations.of(context).imageDetailsAuthorLink,
hint: 'https://example.com/author',
keyboardType: TextInputType.url,
validator: _validateUrl,
AddExerciseTextArea(
title: AppLocalizations.of(context).imageDetailsAuthorLink,
initialValue: widget.submissionImage.authorUrl,
onSaved: (value) => widget.submissionImage.authorUrl = value,
validator: (value) => validateUrl(value, i18n, required: false),
),
const SizedBox(height: 16),
// Original source if this is a derivative work (modified from another image)
_buildTextField(
controller: _originalSourceController,
label: AppLocalizations.of(context).imageDetailsDerivativeSource,
hint: 'https://example.com/original',
keyboardType: TextInputType.url,
AddExerciseTextArea(
title: AppLocalizations.of(context).imageDetailsDerivativeSource,
helperText: AppLocalizations.of(context).imageDetailsDerivativeHelp,
validator: _validateUrl,
initialValue: widget.submissionImage.derivativeSourceUrl,
onSaved: (value) => widget.submissionImage.derivativeSourceUrl = value,
validator: (value) => validateUrl(value, i18n, required: false),
),
const SizedBox(height: 24),
_buildImageTypeSelector(),
const SizedBox(height: 24),
const SizedBox(height: 16),
// License info as separate widget for better optimization
const LicenseInfoWidget(),
const SizedBox(height: 24),
const SizedBox(height: 8),
_buildButtons(),
],
),
),
),
);
}
@@ -292,7 +240,9 @@ class _ImageDetailsFormState extends State<ImageDetailsForm> {
type.label,
style: TextStyle(
fontSize: 11,
color: isSelected ? Colors.blue : Colors.grey.shade700,
color: isSelected
? theme.buttonTheme.colorScheme!.onPrimary
: theme.buttonTheme.colorScheme!.primary,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
textAlign: TextAlign.center,
@@ -322,10 +272,6 @@ class _ImageDetailsFormState extends State<ImageDetailsForm> {
return;
}
// Build details map with API field names
// Style is always included, other fields only if non-empty
final details = <String, String>{'style': _getStyleValue()};
// Add optional fields only if user provided values
final title = _titleController.text.trim();
if (title.isNotEmpty) {

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wger/helpers/consts.dart';
import 'package:wger/helpers/exercises/forms.dart';
import 'package:wger/helpers/exercises/validators.dart';
import 'package:wger/helpers/i18n.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/models/exercises/category.dart';
@@ -9,6 +9,7 @@ import 'package:wger/models/exercises/equipment.dart';
import 'package:wger/models/exercises/muscle.dart';
import 'package:wger/providers/add_exercise.dart';
import 'package:wger/providers/exercises.dart';
import 'package:wger/providers/user.dart';
import 'package:wger/widgets/add_exercise/add_exercise_multiselect_button.dart';
import 'package:wger/widgets/add_exercise/add_exercise_text_area.dart';
import 'package:wger/widgets/exercises/exercises.dart';
@@ -21,6 +22,7 @@ class Step1Basics extends StatelessWidget {
@override
Widget build(BuildContext context) {
final userProvider = context.read<UserProvider>();
final addExerciseProvider = context.read<AddExerciseProvider>();
final exerciseProvider = context.read<ExercisesProvider>();
final categories = exerciseProvider.categories;
@@ -38,21 +40,25 @@ class Step1Basics extends StatelessWidget {
builder: (context, provider, child) => Column(
children: [
AddExerciseTextArea(
onChange: (value) => {},
title: '${AppLocalizations.of(context).name}*',
helperText: AppLocalizations.of(context).baseNameEnglish,
isRequired: true,
validator: (name) => validateName(name, context),
onSaved: (String? name) => addExerciseProvider.exerciseNameEn = name!,
onSaved: (String? name) => addExerciseProvider.exerciseNameEn = name,
),
AddExerciseTextArea(
onChange: (value) => {},
title: AppLocalizations.of(context).alternativeNames,
isMultiline: true,
helperText: AppLocalizations.of(context).oneNamePerLine,
onSaved: (String? alternateName) =>
addExerciseProvider.alternateNamesEn = alternateName!.split('\n'),
),
AddExerciseTextArea(
title: '${AppLocalizations.of(context).author}*',
isMultiline: false,
validator: (name) => validateAuthorName(name, context),
initialValue: userProvider.profile!.username,
onSaved: (String? author) => addExerciseProvider.author = author!,
),
ExerciseCategoryInputWidget<ExerciseCategory>(
key: const Key('category-dropdown'),
entries: categories,

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wger/helpers/exercises/forms.dart';
import 'package:wger/helpers/exercises/validators.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/providers/add_exercise.dart';
import 'package:wger/widgets/add_exercise/add_exercise_text_area.dart';
@@ -23,10 +23,9 @@ class Step3Description extends StatelessWidget {
onChange: (value) => {},
title: '${i18n.description}*',
helperText: i18n.enterTextInLanguage,
isRequired: true,
isMultiline: true,
validator: (name) => validateExerciseDescription(name, context),
onSaved: (String? description) => addExerciseProvider.descriptionEn = description!,
onSaved: (String? description) => addExerciseProvider.descriptionEn = description,
),
],
),

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wger/helpers/exercises/forms.dart';
import 'package:wger/helpers/exercises/validators.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/models/exercises/language.dart';
import 'package:wger/providers/add_exercise.dart';
@@ -59,14 +59,11 @@ class _Step4TranslationState extends State<Step4Translation> {
},
),
AddExerciseTextArea(
onChange: (value) => {},
title: '${i18n.name}*',
isRequired: true,
validator: (name) => validateName(name, context),
onSaved: (String? name) => addExerciseProvider.exerciseNameTrans = name!,
),
AddExerciseTextArea(
onChange: (value) => {},
title: i18n.alternativeNames,
isMultiline: true,
helperText: i18n.oneNamePerLine,
@@ -93,7 +90,6 @@ class _Step4TranslationState extends State<Step4Translation> {
onChange: (value) => {},
title: '${i18n.description}*',
helperText: i18n.enterTextInLanguage,
isRequired: true,
isMultiline: true,
validator: (name) => validateExerciseDescription(name, context),
onSaved: (String? description) =>

View File

@@ -6,6 +6,7 @@ import 'package:provider/provider.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/models/exercises/exercise_submission_images.dart';
import 'package:wger/providers/add_exercise.dart';
import 'package:wger/providers/user.dart';
import 'package:wger/widgets/add_exercise/image_details_form.dart';
import 'package:wger/widgets/add_exercise/mixins/image_picker_mixin.dart';
import 'package:wger/widgets/add_exercise/preview_images.dart';
@@ -33,7 +34,7 @@ class Step5Images extends StatefulWidget {
class _Step5ImagesState extends State<Step5Images> with ExerciseImagePickerMixin {
/// Currently selected image waiting for metadata input
/// When non-null, ImageDetailsForm is displayed instead of image picker
ExerciseSubmissionImage? _currentImageToAddNew;
ExerciseSubmissionImage? _currentImageToAdd;
/// Show dialog to choose between Camera and Gallery
Future<void> _showImageSourceDialog(BuildContext context) async {
@@ -75,6 +76,7 @@ class _Step5ImagesState extends State<Step5Images> with ExerciseImagePickerMixin
///
/// [pickFromCamera] - If true, opens camera; otherwise opens gallery
void _pickAndShowImageDetails(BuildContext context, {bool pickFromCamera = false}) async {
final userProvider = context.read<UserProvider>();
final imagePicker = ImagePicker();
XFile? selectedImage;
@@ -114,7 +116,10 @@ class _Step5ImagesState extends State<Step5Images> with ExerciseImagePickerMixin
// Show metadata collection form for valid image
setState(() {
_currentImageToAddNew = ExerciseSubmissionImage(imageFile: imageFile);
_currentImageToAdd = ExerciseSubmissionImage(
imageFile: imageFile,
author: userProvider.profile?.username ?? '',
);
});
}
}
@@ -135,14 +140,14 @@ class _Step5ImagesState extends State<Step5Images> with ExerciseImagePickerMixin
// Reset form state - image is now visible in preview list
setState(() {
_currentImageToAddNew = null;
_currentImageToAdd = null;
});
}
/// Cancel metadata input and return to image picker
void _cancelImageAdd() {
setState(() {
_currentImageToAddNew = null;
_currentImageToAdd = null;
});
}
@@ -153,7 +158,7 @@ class _Step5ImagesState extends State<Step5Images> with ExerciseImagePickerMixin
child: Column(
children: [
// License notice - shown when not entering metadata
if (_currentImageToAddNew == null)
if (_currentImageToAdd == null)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Text(
@@ -164,15 +169,15 @@ class _Step5ImagesState extends State<Step5Images> with ExerciseImagePickerMixin
),
// Metadata collection form - shown when image is selected
if (_currentImageToAddNew != null)
if (_currentImageToAdd != null)
ImageDetailsForm(
submissionImage: _currentImageToAddNew!,
submissionImage: _currentImageToAdd!,
onAdd: _addImageWithDetails,
onCancel: _cancelImageAdd,
),
// Image picker or preview - shown when not entering metadata
if (_currentImageToAddNew == null)
if (_currentImageToAdd == null)
Consumer<AddExerciseProvider>(
builder: (ctx, provider, __) {
if (provider.exerciseImages.isNotEmpty) {

View File

@@ -11,13 +11,14 @@ class Step6Overview extends StatelessWidget {
final i18n = AppLocalizations.of(context);
return Consumer<AddExerciseProvider>(
builder: (ctx, provider, __) => Column(
builder: (ctx, provider, _) => Column(
spacing: 8,
children: [
Text(i18n.baseData, style: Theme.of(context).textTheme.headlineSmall),
Table(
columnWidths: const {0: FlexColumnWidth(2), 1: FlexColumnWidth(3)},
children: [
TableRow(children: [Text(i18n.author), Text(provider.author)]),
TableRow(children: [Text(i18n.name), Text(provider.exerciseNameEn ?? '...')]),
TableRow(
children: [

View File

@@ -8,13 +8,13 @@ import 'dart:ui' as _i15;
import 'package:http/http.dart' as _i5;
import 'package:mockito/mockito.dart' as _i1;
import 'package:mockito/src/dummies.dart' as _i11;
import 'package:mockito/src/dummies.dart' as _i8;
import 'package:wger/models/exercises/category.dart' as _i13;
import 'package:wger/models/exercises/equipment.dart' as _i8;
import 'package:wger/models/exercises/exercise_submission.dart' as _i10;
import 'package:wger/models/exercises/equipment.dart' as _i9;
import 'package:wger/models/exercises/exercise_submission.dart' as _i11;
import 'package:wger/models/exercises/exercise_submission_images.dart' as _i7;
import 'package:wger/models/exercises/language.dart' as _i12;
import 'package:wger/models/exercises/muscle.dart' as _i9;
import 'package:wger/models/exercises/muscle.dart' as _i10;
import 'package:wger/models/exercises/variation.dart' as _i3;
import 'package:wger/providers/add_exercise.dart' as _i6;
import 'package:wger/providers/auth.dart' as _i4;
@@ -84,6 +84,14 @@ class MockAddExerciseProvider extends _i1.Mock implements _i6.AddExerciseProvide
)
as List<_i7.ExerciseSubmissionImage>);
@override
String get author =>
(super.noSuchMethod(
Invocation.getter(#author),
returnValue: _i8.dummyValue<String>(this, Invocation.getter(#author)),
)
as String);
@override
List<String> get alternateNamesEn =>
(super.noSuchMethod(Invocation.getter(#alternateNamesEn), returnValue: <String>[])
@@ -95,9 +103,9 @@ class MockAddExerciseProvider extends _i1.Mock implements _i6.AddExerciseProvide
as List<String>);
@override
List<_i8.Equipment> get equipment =>
(super.noSuchMethod(Invocation.getter(#equipment), returnValue: <_i8.Equipment>[])
as List<_i8.Equipment>);
List<_i9.Equipment> get equipment =>
(super.noSuchMethod(Invocation.getter(#equipment), returnValue: <_i9.Equipment>[])
as List<_i9.Equipment>);
@override
bool get newVariation =>
@@ -112,25 +120,29 @@ class MockAddExerciseProvider extends _i1.Mock implements _i6.AddExerciseProvide
as _i3.Variation);
@override
List<_i9.Muscle> get primaryMuscles =>
(super.noSuchMethod(Invocation.getter(#primaryMuscles), returnValue: <_i9.Muscle>[])
as List<_i9.Muscle>);
List<_i10.Muscle> get primaryMuscles =>
(super.noSuchMethod(Invocation.getter(#primaryMuscles), returnValue: <_i10.Muscle>[])
as List<_i10.Muscle>);
@override
List<_i9.Muscle> get secondaryMuscles =>
(super.noSuchMethod(Invocation.getter(#secondaryMuscles), returnValue: <_i9.Muscle>[])
as List<_i9.Muscle>);
List<_i10.Muscle> get secondaryMuscles =>
(super.noSuchMethod(Invocation.getter(#secondaryMuscles), returnValue: <_i10.Muscle>[])
as List<_i10.Muscle>);
@override
_i10.ExerciseSubmissionApi get exerciseApiObject =>
_i11.ExerciseSubmissionApi get exerciseApiObject =>
(super.noSuchMethod(
Invocation.getter(#exerciseApiObject),
returnValue: _i11.dummyValue<_i10.ExerciseSubmissionApi>(
returnValue: _i8.dummyValue<_i11.ExerciseSubmissionApi>(
this,
Invocation.getter(#exerciseApiObject),
),
)
as _i10.ExerciseSubmissionApi);
as _i11.ExerciseSubmissionApi);
@override
set author(String? value) =>
super.noSuchMethod(Invocation.setter(#author, value), returnValueForMissingStub: null);
@override
set exerciseNameEn(String? value) => super.noSuchMethod(
@@ -181,7 +193,7 @@ class MockAddExerciseProvider extends _i1.Mock implements _i6.AddExerciseProvide
super.noSuchMethod(Invocation.setter(#category, value), returnValueForMissingStub: null);
@override
set equipment(List<_i8.Equipment>? equipment) =>
set equipment(List<_i9.Equipment>? equipment) =>
super.noSuchMethod(Invocation.setter(#equipment, equipment), returnValueForMissingStub: null);
@override
@@ -197,13 +209,13 @@ class MockAddExerciseProvider extends _i1.Mock implements _i6.AddExerciseProvide
);
@override
set primaryMuscles(List<_i9.Muscle>? muscles) => super.noSuchMethod(
set primaryMuscles(List<_i10.Muscle>? muscles) => super.noSuchMethod(
Invocation.setter(#primaryMuscles, muscles),
returnValueForMissingStub: null,
);
@override
set secondaryMuscles(List<_i9.Muscle>? muscles) => super.noSuchMethod(
set secondaryMuscles(List<_i10.Muscle>? muscles) => super.noSuchMethod(
Invocation.setter(#secondaryMuscles, muscles),
returnValueForMissingStub: null,
);

View File

@@ -8,13 +8,13 @@ import 'dart:ui' as _i16;
import 'package:flutter/material.dart' as _i18;
import 'package:mockito/mockito.dart' as _i1;
import 'package:mockito/src/dummies.dart' as _i14;
import 'package:mockito/src/dummies.dart' as _i13;
import 'package:shared_preferences/shared_preferences.dart' as _i4;
import 'package:wger/database/exercises/exercise_database.dart' as _i5;
import 'package:wger/models/exercises/category.dart' as _i7;
import 'package:wger/models/exercises/equipment.dart' as _i8;
import 'package:wger/models/exercises/exercise.dart' as _i6;
import 'package:wger/models/exercises/exercise_submission.dart' as _i13;
import 'package:wger/models/exercises/exercise_submission.dart' as _i14;
import 'package:wger/models/exercises/exercise_submission_images.dart' as _i12;
import 'package:wger/models/exercises/language.dart' as _i10;
import 'package:wger/models/exercises/muscle.dart' as _i9;
@@ -104,6 +104,14 @@ class MockAddExerciseProvider extends _i1.Mock implements _i11.AddExerciseProvid
)
as List<_i12.ExerciseSubmissionImage>);
@override
String get author =>
(super.noSuchMethod(
Invocation.getter(#author),
returnValue: _i13.dummyValue<String>(this, Invocation.getter(#author)),
)
as String);
@override
List<String> get alternateNamesEn =>
(super.noSuchMethod(Invocation.getter(#alternateNamesEn), returnValue: <String>[])
@@ -142,15 +150,19 @@ class MockAddExerciseProvider extends _i1.Mock implements _i11.AddExerciseProvid
as List<_i9.Muscle>);
@override
_i13.ExerciseSubmissionApi get exerciseApiObject =>
_i14.ExerciseSubmissionApi get exerciseApiObject =>
(super.noSuchMethod(
Invocation.getter(#exerciseApiObject),
returnValue: _i14.dummyValue<_i13.ExerciseSubmissionApi>(
returnValue: _i13.dummyValue<_i14.ExerciseSubmissionApi>(
this,
Invocation.getter(#exerciseApiObject),
),
)
as _i13.ExerciseSubmissionApi);
as _i14.ExerciseSubmissionApi);
@override
set author(String? value) =>
super.noSuchMethod(Invocation.setter(#author, value), returnValueForMissingStub: null);
@override
set exerciseNameEn(String? value) => super.noSuchMethod(