mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
Add exercise set placeholder form
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
42
lib/widgets/workouts/exercises.dart
Normal file
42
lib/widgets/workouts/exercises.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user