Add exercise set placeholder form

This commit is contained in:
Roland Geider
2020-12-29 13:22:47 +01:00
parent 3c505db785
commit 373cfb0dfd
3 changed files with 124 additions and 31 deletions

View File

@@ -57,7 +57,10 @@ class Exercises extends WgerBaseProvider with ChangeNotifier {
}
Exercise findById(int id) {
return _exercises.firstWhere((exercise) => exercise.id == id);
return _exercises.firstWhere((exercise) => exercise.id == id, orElse: () {
log('Could not find exercise with ID $id');
return null;
});
}
Future<void> fetchAndSetCategories() async {
@@ -118,7 +121,7 @@ class Exercises extends WgerBaseProvider with ChangeNotifier {
final response = await client.get(makeUrl(
_exercisesUrlPath,
query: {'language': '1', 'limit': '1000'}, // TODO: read the language ID from the locale
query: {'limit': '1000'},
));
final extractedData = json.decode(response.body) as Map<String, dynamic>;
@@ -147,7 +150,7 @@ class Exercises extends WgerBaseProvider with ChangeNotifier {
///
/// We could do this locally, but the server has better text searching capabilities
/// with postgresql.
Future<List> searchIngredient(String name) async {
Future<List> searchExercise(String name) async {
// Send the request
final response = await client.get(
makeUrl(_exerciseSearchPath, query: {'term': name}),
@@ -163,6 +166,10 @@ class Exercises extends WgerBaseProvider with ChangeNotifier {
}
// Process the response
return json.decode(utf8.decode(response.bodyBytes))['suggestions'] as List<dynamic>;
final result = json.decode(utf8.decode(response.bodyBytes))['suggestions'] as List<dynamic>;
for (var entry in result) {
entry['exercise_obj'] = findById(entry['data']['id']);
}
return result;
}
}

View File

@@ -0,0 +1,42 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* Copyright (C) 2020 wger Team
*
* wger Workout Manager is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:flutter/material.dart';
class ExerciseImage extends StatelessWidget {
const ExerciseImage({
Key key,
@required this.imageUrl,
}) : super(key: key);
final String imageUrl;
@override
Widget build(BuildContext context) {
return imageUrl != null
? FadeInImage(
placeholder: AssetImage('assets/images/placeholder.png'),
image: NetworkImage(imageUrl),
fit: BoxFit.cover,
)
: Image(
image: AssetImage('assets/images/placeholder.png'),
color: Color.fromRGBO(255, 255, 255, 0.3),
colorBlendMode: BlendMode.modulate);
}
}

View File

@@ -20,13 +20,15 @@ import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:provider/provider.dart';
import 'package:wger/locale/locales.dart';
import 'package:wger/models/exercises/exercise.dart';
import 'package:wger/models/workouts/day.dart';
import 'package:wger/models/workouts/workout_plan.dart';
import 'package:wger/providers/auth.dart';
import 'package:wger/providers/exercises.dart';
import 'package:wger/providers/workout_plans.dart';
import 'package:wger/widgets/workouts/exercises.dart';
class DayFormWidget extends StatelessWidget {
class DayFormWidget extends StatefulWidget {
final WorkoutPlan workout;
DayFormWidget({
@@ -39,6 +41,12 @@ class DayFormWidget extends StatelessWidget {
final TextEditingController dayController;
final Map<String, dynamic> _dayData;
@override
_DayFormWidgetState createState() => _DayFormWidgetState();
}
class _DayFormWidgetState extends State<DayFormWidget> {
final _form = GlobalKey<FormState>();
@override
@@ -55,9 +63,9 @@ class DayFormWidget extends StatelessWidget {
),
TextFormField(
decoration: InputDecoration(labelText: AppLocalizations.of(context).description),
controller: dayController,
controller: widget.dayController,
onSaved: (value) {
_dayData['description'] = value;
widget._dayData['description'] = value;
},
validator: (value) {
const minLenght = 5;
@@ -82,9 +90,9 @@ class DayFormWidget extends StatelessWidget {
_form.currentState.save();
try {
Provider.of<WorkoutPlans>(context, listen: false)
.addDay(Day(description: dayController.text, daysOfWeek: [1]), workout);
dayController.clear();
Provider.of<WorkoutPlans>(context, listen: false).addDay(
Day(description: widget.dayController.text, daysOfWeek: [1]), widget.workout);
widget.dayController.clear();
Navigator.of(context).pop();
} catch (error) {
await showDialog(
@@ -123,6 +131,7 @@ class SetFormWidget extends StatefulWidget {
class _SetFormWidgetState extends State<SetFormWidget> {
double _currentSetSliderValue = 4;
List<Exercise> _exercises = [];
// Form stuff
final GlobalKey<FormState> _formKey = GlobalKey();
@@ -141,27 +150,16 @@ class _SetFormWidgetState extends State<SetFormWidget> {
decoration: InputDecoration(labelText: AppLocalizations.of(context).ingredient),
),
suggestionsCallback: (pattern) async {
return await Provider.of<Exercises>(context, listen: false)
.searchIngredient(pattern);
return await Provider.of<Exercises>(context, listen: false).searchExercise(pattern);
},
itemBuilder: (context, suggestion) {
// TODO: this won't work if the server uses e.g. AWS to serve
// the static files
String serverUrl = Provider.of<Auth>(context, listen: false).serverUrl;
print(suggestion);
print(serverUrl);
print('$serverUrl${suggestion["data"]["image"]}');
return ListTile(
leading: Container(
width: 45,
child: suggestion['data']['image'] != null
? FadeInImage(
placeholder: AssetImage('assets/images/placeholder.png'),
image: NetworkImage(serverUrl + suggestion['data']['image']),
fit: BoxFit.cover,
)
: Image(
image: AssetImage('assets/images/placeholder.png'),
color: Color.fromRGBO(255, 255, 255, 0.3),
colorBlendMode: BlendMode.modulate),
child: ExerciseImage(imageUrl: suggestion['data']['image']),
),
title: Text(suggestion['value']),
subtitle: Text(suggestion['data']['id'].toString()),
@@ -171,20 +169,21 @@ class _SetFormWidgetState extends State<SetFormWidget> {
return suggestionsBox;
},
onSuggestionSelected: (suggestion) {
print(suggestion);
//mealItem.ingredientId = suggestion['data']['id'];
//this._ingredientController.text = suggestion['value'];
final e = Provider.of<Exercises>(context, listen: false)
.findById(suggestion['data']['id']);
setState(() {
_exercises.add(e);
});
},
validator: (value) {
if (value.isEmpty) {
return 'Please select an ingredient';
}
//if (mealItem.ingredientId == null) {
// return 'Please select an ingredient';
//}
return null;
},
),
Text('You can search for more than one exercise, they will be grouped '
'together for a superset.'),
Text('Number of sets:'),
Slider(
value: _currentSetSliderValue,
@@ -198,6 +197,10 @@ class _SetFormWidgetState extends State<SetFormWidget> {
});
},
),
Text('If you do the same repetitions for all sets you can just enter '
'one value: e.g. for 4 sets just enter one "10" for the repetitions, '
'this automatically becomes "4 x 10".'),
..._exercises.map((e) => ExerciseSet(e, _currentSetSliderValue)).toList(),
ElevatedButton(child: Text('Save'), onPressed: () {}),
],
),
@@ -205,3 +208,44 @@ class _SetFormWidgetState extends State<SetFormWidget> {
);
}
}
class ExerciseSet extends StatelessWidget {
Exercise _exercise;
int _numberOfSets;
ExerciseSet(Exercise exercise, double sliderValue) {
this._exercise = exercise;
this._numberOfSets = sliderValue.round();
}
Widget getSets() {
List<Widget> out = [];
for (var i = 1; i <= _numberOfSets; i++) {
out.add(Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Reps'),
Text('Unit'),
Text('Weight'),
Text('RiR'),
],
));
}
return Column(
children: out,
);
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(_exercise.name, style: TextStyle(fontWeight: FontWeight.bold)),
//ExerciseImage(imageUrl: _exercise.images.first.url),
Divider(),
getSets(),
Padding(padding: EdgeInsets.symmetric(vertical: 5))
],
);
}
}