mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
Add logic to add days to workout plan
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user