Add logic to add days to workout plan

This commit is contained in:
Roland Geider
2020-11-27 11:48:39 +01:00
parent f6e74f1016
commit 2a3dcb6ff6
9 changed files with 302 additions and 51 deletions

View File

@@ -27,20 +27,52 @@ class AppLocalizations {
String get newWorkout {
return Intl.message(
'new Workout',
'New Workout',
name: 'newWorkout',
desc: 'Header when adding a new workout',
);
}
String get newDay {
return Intl.message(
'New Day',
name: 'newDay',
desc: 'Header when adding a new day to a workout',
);
}
String get description {
return Intl.message(
'description',
'Description',
name: 'description',
desc: 'Description of a workout, nutritional plan, etc.',
);
}
String get save {
return Intl.message(
'Save',
name: 'save',
desc: 'Saving a new entry in the DB',
);
}
String get cancel {
return Intl.message(
'Cancel',
name: 'cancel',
desc: 'Cancelling an action',
);
}
String get add {
return Intl.message(
'Add',
name: 'add',
desc: 'Label for a button etc.',
);
}
String get labelWorkoutPlan {
return Intl.message(
'Workout plan',

View File

@@ -15,8 +15,9 @@ WeightEntry _$WeightEntryFromJson(Map<String, dynamic> json) {
);
}
Map<String, dynamic> _$WeightEntryToJson(WeightEntry instance) => <String, dynamic>{
Map<String, dynamic> _$WeightEntryToJson(WeightEntry instance) =>
<String, dynamic>{
'id': instance.id,
'weight': instance.weight,
'weight': toString(instance.weight),
'date': instance.date?.toIso8601String(),
};

View File

@@ -1,5 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:wger/models/workouts/set.dart';
import 'package:wger/models/workouts/workout_plan.dart';
part 'day.g.dart';
@@ -11,30 +13,36 @@ class Day {
@JsonKey(required: true)
final String description;
@JsonKey(required: true, name: 'days_of_week')
@JsonKey(required: false, name: 'day')
List<int> daysOfWeek = [];
@JsonKey(required: false)
//@JsonKey(required: false)
List<Set> sets = [];
@JsonKey(required: true, name: 'training')
int workoutId;
//@JsonKey(required: true, name: 'training')
WorkoutPlan workout;
Day({
this.id,
this.description,
@required this.description,
this.daysOfWeek,
this.sets,
});
String getDayName(int weekDay) {
Map<int, String> weekdays = {
1: 'Monday',
2: 'Tuesday',
3: 'Wednesday',
4: 'Thursday',
5: 'Friday',
6: 'Saturday',
7: 'Sunday',
};
static const Map<int, String> weekdays = {
1: 'Monday',
2: 'Tuesday',
3: 'Wednesday',
4: 'Thursday',
5: 'Friday',
6: 'Saturday',
7: 'Sunday',
};
String getDayName(int weekDay) {
return weekdays[weekDay];
}

View File

@@ -7,20 +7,26 @@ part of 'day.dart';
// **************************************************************************
Day _$DayFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'description', 'days_of_week']);
$checkKeys(json, requiredKeys: const ['id', 'description', 'training']);
return Day(
id: json['id'] as int,
description: json['description'] as String,
daysOfWeek: (json['days_of_week'] as List)?.map((e) => e as int)?.toList(),
daysOfWeek: (json['day'] as List)?.map((e) => e as int)?.toList(),
sets: (json['sets'] as List)
?.map((e) => e == null ? null : Set.fromJson(e as Map<String, dynamic>))
?.toList(),
);
)
..workoutId = json['training'] as int
..workout = json['workout'] == null
? null
: WorkoutPlan.fromJson(json['workout'] as Map<String, dynamic>);
}
Map<String, dynamic> _$DayToJson(Day instance) => <String, dynamic>{
'id': instance.id,
'description': instance.description,
'days_of_week': instance.daysOfWeek,
'day': instance.daysOfWeek,
'sets': instance.sets,
'training': instance.workoutId,
'workout': instance.workout,
};

View File

@@ -19,8 +19,8 @@ class WorkoutPlan {
List<Day> days = [];
WorkoutPlan({
@required this.id,
@required this.creationDate,
this.id,
this.creationDate,
@required this.description,
this.days,
});

View File

@@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
@@ -12,8 +13,10 @@ import 'package:wger/providers/auth.dart';
class WorkoutPlans with ChangeNotifier {
static const workoutPlansUrl = '/api/v2/workout/';
static const daysUrl = '/api/v2/day/';
String _url;
String _urlDays;
Auth _auth;
List<WorkoutPlan> _entries = [];
@@ -21,6 +24,7 @@ class WorkoutPlans with ChangeNotifier {
this._auth = auth;
this._entries = entries;
this._url = auth.serverUrl + workoutPlansUrl;
this._urlDays = auth.serverUrl + daysUrl;
}
List<WorkoutPlan> get items {
@@ -31,6 +35,9 @@ class WorkoutPlans with ChangeNotifier {
return _entries.firstWhere((workoutPlan) => workoutPlan.id == id);
}
/*
* Workouts
*/
Future<void> fetchAndSetWorkouts() async {
final response = await http.get(
_url,
@@ -124,12 +131,12 @@ class WorkoutPlans with ChangeNotifier {
// Days
days.add(Day(
id: entry['obj']['id'],
description: entry['obj']['description'],
sets: sets,
daysOfWeek: [1, 3],
//daysOfWeek: entry['obj']['day'],
));
id: entry['obj']['id'],
description: entry['obj']['description'],
sets: sets,
daysOfWeek: [1, 3]
//daysOfWeek: entry['obj']['day'],
));
}
workout.days = days;
notifyListeners();
@@ -190,4 +197,34 @@ class WorkoutPlans with ChangeNotifier {
}
existingWorkout = null;
}
/*
* Days
*/
Future<Day> addDay(Day day, WorkoutPlan workout) async {
/*
* Saves a new day instance to the DB and adds it to the given workout
*/
day.workoutId = workout.id;
try {
final response = await http.post(
_urlDays,
headers: {
'Authorization': 'Token ${_auth.token}',
'Content-Type': 'application/json; charset=UTF-8',
},
body: json.encode(day.toJson()),
);
// Create the day
day = Day.fromJson(json.decode(response.body));
day.sets = [];
workout.days.insert(0, day);
notifyListeners();
return day;
} catch (error) {
log(error.missingKeys.toString());
throw error;
}
}
}

View File

@@ -36,9 +36,10 @@ class AuthScreen extends StatelessWidget {
height: deviceSize.height,
width: deviceSize.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(padding: EdgeInsets.symmetric(vertical: 20)),
Image(
image: AssetImage('assets/images/logo.png'),
width: 120,

View File

@@ -67,9 +67,9 @@ class AppDrawer extends StatelessWidget {
leading: Icon(Icons.exit_to_app),
title: Text('Logout'),
onTap: () {
Provider.of<Auth>(context, listen: false).logout();
Navigator.of(context).pop();
Navigator.of(context).pushReplacementNamed('/');
Provider.of<Auth>(context, listen: false).logout();
},
),
],

View File

@@ -1,46 +1,212 @@
/*
* This file is part of wger Workout Manager.
*
* 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
*/
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:wger/locale/locales.dart';
import 'package:wger/models/workouts/day.dart';
import 'package:wger/models/workouts/workout_plan.dart';
import 'package:wger/providers/workout_plans.dart';
import 'package:wger/widgets/workouts/day.dart';
class WorkoutPlansDetail extends StatelessWidget {
class DayCheckbox extends StatefulWidget {
String name;
DayCheckbox(this.name);
@override
_DayCheckboxState createState() => _DayCheckboxState();
}
class _DayCheckboxState extends State<DayCheckbox> {
bool _isSelected = false;
@override
Widget build(BuildContext context) {
return CheckboxListTile(
title: Text(widget.name),
value: _isSelected,
onChanged: (bool newValue) {
setState(() {
_isSelected = newValue;
});
},
);
}
}
class WorkoutPlansDetail extends StatefulWidget {
WorkoutPlan _workoutPlan;
WorkoutPlansDetail(this._workoutPlan);
@override
_WorkoutPlansDetailState createState() => _WorkoutPlansDetailState();
}
class _WorkoutPlansDetailState extends State<WorkoutPlansDetail> {
final dayController = TextEditingController();
Map<String, dynamic> _dayData = {
'description': '',
'daysOfWeek': [1],
};
Widget showDaySheet(BuildContext outerContext, WorkoutPlan workout) {
showModalBottomSheet(
context: outerContext,
builder: (BuildContext context) {
return Container(
child: DayFormWidget(
dayController: dayController,
dayData: _dayData,
workout: workout,
),
);
},
);
}
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Text(
DateFormat('dd.MM.yyyy').format(_workoutPlan.creationDate).toString(),
DateFormat('dd.MM.yyyy').format(widget._workoutPlan.creationDate).toString(),
style: Theme.of(context).textTheme.headline6,
),
),
if (_workoutPlan.description != null)
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Text(
_workoutPlan.description,
style: Theme.of(context).textTheme.headline5,
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Text(
widget._workoutPlan.description,
style: Theme.of(context).textTheme.headline5,
),
_workoutPlan.days != null
? Expanded(
child: ListView.builder(
itemCount: _workoutPlan.days.length,
itemBuilder: (context, index) {
Day workoutDay = _workoutPlan.days[index];
return WorkoutDayWidget(workoutDay);
},
),
)
: Text('No days'),
),
Expanded(
child: ListView.builder(
itemCount: widget._workoutPlan.days.length,
itemBuilder: (context, index) {
Day workoutDay = widget._workoutPlan.days[index];
return WorkoutDayWidget(workoutDay);
},
),
),
Column(
children: [
ElevatedButton(
child: Text(AppLocalizations.of(context).add),
onPressed: () {
showDaySheet(context, widget._workoutPlan);
}),
],
),
],
),
);
}
}
class DayFormWidget extends StatelessWidget {
final WorkoutPlan workout;
DayFormWidget({
Key key,
@required this.dayController,
@required Map<String, dynamic> dayData,
@required this.workout,
}) : _dayData = dayData,
super(key: key);
final TextEditingController dayController;
final Map<String, dynamic> _dayData;
final _form = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(20),
child: Form(
key: _form,
child: Column(
children: [
Text(
AppLocalizations.of(context).newDay,
style: Theme.of(context).textTheme.headline6,
),
TextFormField(
decoration: InputDecoration(labelText: AppLocalizations.of(context).description),
controller: dayController,
onSaved: (value) {
_dayData['description'] = value;
},
validator: (value) {
const minLenght = 5;
const maxLenght = 100;
if (value.isEmpty || value.length < minLenght || value.length > maxLenght) {
return 'Please enter between $minLenght and $maxLenght characters.';
}
return null;
},
),
TextFormField(
decoration: InputDecoration(labelText: 'TODO: Checkbox for days'),
enabled: false,
),
//...Day().weekdays.values.map((dayName) => DayCheckbox(dayName)).toList(),
ElevatedButton(
child: Text(AppLocalizations.of(context).save),
onPressed: () async {
if (!_form.currentState.validate()) {
return;
}
_form.currentState.save();
try {
Provider.of<WorkoutPlans>(context, listen: false)
.addDay(Day(description: dayController.text, daysOfWeek: [1]), workout);
//dayController.text = '';
//Navigator.of(context).pop();
} catch (error) {
await showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text('An error occurred!'),
content: Text('Something went wrong.'),
actions: <Widget>[
FlatButton(
child: Text('Okay'),
onPressed: () {
Navigator.of(ctx).pop();
},
)
],
),
);
}
//dayController.text = '';
//Navigator.of(context).pop();
},
),
],
),
),
);
}
}