From ea745c1ceca1265f928849d360e65f3906618815 Mon Sep 17 00:00:00 2001 From: Brandon Presley Date: Fri, 10 Nov 2023 23:43:06 +1300 Subject: [PATCH] Make CRUD work for Plans --- lib/database.dart | 2 +- lib/database.g.dart | 71 ++++++++++++------------ lib/days.dart | 42 ++++++++------ lib/edit_plan.dart | 85 +++++++++++++++-------------- lib/exercises.dart | 60 ++++++++++++++++++++ lib/{gym_set.dart => gym_sets.dart} | 0 lib/plans.dart | 2 +- lib/plans_page.dart | 8 +-- 8 files changed, 173 insertions(+), 97 deletions(-) create mode 100644 lib/exercises.dart rename lib/{gym_set.dart => gym_sets.dart} (100%) diff --git a/lib/database.dart b/lib/database.dart index ffa4bb8..e05e84e 100644 --- a/lib/database.dart +++ b/lib/database.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'package:fmassive/gym_set.dart'; +import 'package:fmassive/gym_sets.dart'; import 'package:fmassive/main.dart'; import 'package:fmassive/plans.dart'; import 'package:moor/ffi.dart'; diff --git a/lib/database.g.dart b/lib/database.g.dart index 2670b88..b37f84f 100644 --- a/lib/database.g.dart +++ b/lib/database.g.dart @@ -1248,8 +1248,8 @@ class $GymSetsTable extends GymSets with TableInfo<$GymSetsTable, GymSet> { class Plan extends DataClass implements Insertable { final int id; final String days; - final String workouts; - Plan({required this.id, required this.days, required this.workouts}); + final String? exercises; + Plan({required this.id, required this.days, this.exercises}); factory Plan.fromData(Map data, GeneratedDatabase db, {String? prefix}) { final effectivePrefix = prefix ?? ''; @@ -1258,8 +1258,8 @@ class Plan extends DataClass implements Insertable { .mapFromDatabaseResponse(data['${effectivePrefix}id'])!, days: const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}days'])!, - workouts: const StringType() - .mapFromDatabaseResponse(data['${effectivePrefix}workouts'])!, + exercises: const StringType() + .mapFromDatabaseResponse(data['${effectivePrefix}exercises']), ); } @override @@ -1267,7 +1267,9 @@ class Plan extends DataClass implements Insertable { final map = {}; map['id'] = Variable(id); map['days'] = Variable(days); - map['workouts'] = Variable(workouts); + if (!nullToAbsent || exercises != null) { + map['exercises'] = Variable(exercises); + } return map; } @@ -1275,7 +1277,9 @@ class Plan extends DataClass implements Insertable { return PlansCompanion( id: Value(id), days: Value(days), - workouts: Value(workouts), + exercises: exercises == null && nullToAbsent + ? const Value.absent() + : Value(exercises), ); } @@ -1285,7 +1289,7 @@ class Plan extends DataClass implements Insertable { return Plan( id: serializer.fromJson(json['id']), days: serializer.fromJson(json['days']), - workouts: serializer.fromJson(json['workouts']), + exercises: serializer.fromJson(json['exercises']), ); } @override @@ -1294,69 +1298,68 @@ class Plan extends DataClass implements Insertable { return { 'id': serializer.toJson(id), 'days': serializer.toJson(days), - 'workouts': serializer.toJson(workouts), + 'exercises': serializer.toJson(exercises), }; } - Plan copyWith({int? id, String? days, String? workouts}) => Plan( + Plan copyWith({int? id, String? days, String? exercises}) => Plan( id: id ?? this.id, days: days ?? this.days, - workouts: workouts ?? this.workouts, + exercises: exercises ?? this.exercises, ); @override String toString() { return (StringBuffer('Plan(') ..write('id: $id, ') ..write('days: $days, ') - ..write('workouts: $workouts') + ..write('exercises: $exercises') ..write(')')) .toString(); } @override - int get hashCode => Object.hash(id, days, workouts); + int get hashCode => Object.hash(id, days, exercises); @override bool operator ==(Object other) => identical(this, other) || (other is Plan && other.id == this.id && other.days == this.days && - other.workouts == this.workouts); + other.exercises == this.exercises); } class PlansCompanion extends UpdateCompanion { final Value id; final Value days; - final Value workouts; + final Value exercises; const PlansCompanion({ this.id = const Value.absent(), this.days = const Value.absent(), - this.workouts = const Value.absent(), + this.exercises = const Value.absent(), }); PlansCompanion.insert({ this.id = const Value.absent(), required String days, - required String workouts, - }) : days = Value(days), - workouts = Value(workouts); + this.exercises = const Value.absent(), + }) : days = Value(days); static Insertable custom({ Expression? id, Expression? days, - Expression? workouts, + Expression? exercises, }) { return RawValuesInsertable({ if (id != null) 'id': id, if (days != null) 'days': days, - if (workouts != null) 'workouts': workouts, + if (exercises != null) 'exercises': exercises, }); } PlansCompanion copyWith( - {Value? id, Value? days, Value? workouts}) { + {Value? id, Value? days, Value? exercises}) { return PlansCompanion( id: id ?? this.id, days: days ?? this.days, - workouts: workouts ?? this.workouts, + exercises: exercises ?? this.exercises, ); } @@ -1369,8 +1372,8 @@ class PlansCompanion extends UpdateCompanion { if (days.present) { map['days'] = Variable(days.value); } - if (workouts.present) { - map['workouts'] = Variable(workouts.value); + if (exercises.present) { + map['exercises'] = Variable(exercises.value); } return map; } @@ -1380,7 +1383,7 @@ class PlansCompanion extends UpdateCompanion { return (StringBuffer('PlansCompanion(') ..write('id: $id, ') ..write('days: $days, ') - ..write('workouts: $workouts') + ..write('exercises: $exercises') ..write(')')) .toString(); } @@ -1403,13 +1406,13 @@ class $PlansTable extends Plans with TableInfo<$PlansTable, Plan> { late final GeneratedColumn days = GeneratedColumn( 'days', aliasedName, false, type: const StringType(), requiredDuringInsert: true); - final VerificationMeta _workoutsMeta = const VerificationMeta('workouts'); + final VerificationMeta _exercisesMeta = const VerificationMeta('exercises'); @override - late final GeneratedColumn workouts = GeneratedColumn( - 'workouts', aliasedName, false, - type: const StringType(), requiredDuringInsert: true); + late final GeneratedColumn exercises = GeneratedColumn( + 'exercises', aliasedName, true, + type: const StringType(), requiredDuringInsert: false); @override - List get $columns => [id, days, workouts]; + List get $columns => [id, days, exercises]; @override String get aliasedName => _alias ?? 'plans'; @override @@ -1428,11 +1431,9 @@ class $PlansTable extends Plans with TableInfo<$PlansTable, Plan> { } else if (isInserting) { context.missing(_daysMeta); } - if (data.containsKey('workouts')) { - context.handle(_workoutsMeta, - workouts.isAcceptableOrUnknown(data['workouts']!, _workoutsMeta)); - } else if (isInserting) { - context.missing(_workoutsMeta); + if (data.containsKey('exercises')) { + context.handle(_exercisesMeta, + exercises.isAcceptableOrUnknown(data['exercises']!, _exercisesMeta)); } return context; } diff --git a/lib/days.dart b/lib/days.dart index b0cd238..9cf5467 100644 --- a/lib/days.dart +++ b/lib/days.dart @@ -2,15 +2,15 @@ import 'package:flutter/material.dart'; class Days extends StatefulWidget { final ValueChanged onChanged; + final String days; - const Days({required this.onChanged, super.key}); + const Days({required this.onChanged, required this.days, super.key}); @override createState() => _DaysState(); } class _DaysState extends State { - final List _selections = List.generate(7, (_) => false); final List _days = [ 'Monday', 'Tuesday', @@ -20,14 +20,19 @@ class _DaysState extends State { 'Saturday', 'Sunday' ]; + late List _selections; + + @override + void initState() { + super.initState(); + final dayList = widget.days.split(','); + _selections = _days.map((day) => dayList.contains(day)).toList(); + } String _getSelectedDaysString() { List selectedDays = []; - for (int i = 0; i < _selections.length; i++) { - if (_selections[i]) { - selectedDays.add(_days[i]); - } - } + for (int i = 0; i < _selections.length; i++) + if (_selections[i]) selectedDays.add(_days[i]); return selectedDays.join(","); } @@ -40,14 +45,19 @@ class _DaysState extends State { @override Widget build(BuildContext context) { - return Column( - children: List.generate(7, (index) { - return SwitchListTile( - title: Text(_days[index]), - value: _selections[index], - onChanged: (value) => _updateSelections(index), - ); - }), - ); + return Column(children: [ + Text('Days', style: Theme.of(context).textTheme.headlineSmall), + Expanded( + child: ListView( + children: List.generate(7, (index) { + return SwitchListTile( + title: Text(_days[index]), + value: _selections[index], + onChanged: (value) => _updateSelections(index), + ); + }), + ), + ), + ]); } } diff --git a/lib/edit_plan.dart b/lib/edit_plan.dart index 0273f1c..3cbde43 100644 --- a/lib/edit_plan.dart +++ b/lib/edit_plan.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/material.dart' as material; import 'package:fmassive/database.dart'; import 'package:fmassive/days.dart'; +import 'package:fmassive/exercises.dart'; import 'package:fmassive/main.dart'; import 'package:moor_flutter/moor_flutter.dart'; @@ -15,28 +16,31 @@ class EditPlanPage extends StatefulWidget { } class _EditPlanPageState extends State { - final TextEditingController _daysController = TextEditingController(); - final TextEditingController _workoutsController = TextEditingController(); late PlansCompanion plan; - final daysNode = FocusNode(); - final workoutsNode = FocusNode(); + List names = []; + + Future> getDistinctNames() async { + final names = await (db.gymSets.selectOnly(distinct: true) + ..addColumns([db.gymSets.name])) + .get(); + return names.map((name) => name.read(db.gymSets.name)).toList(); + } @override void initState() { super.initState(); + + getDistinctNames().then((value) { + setState(() { + names = value; + }); + }); + plan = widget.plan; - _daysController.text = plan.days.value; - _workoutsController.text = plan.workouts.value; - if (plan.id.present) - workoutsNode.requestFocus(); - else - daysNode.requestFocus(); } @override dispose() { - daysNode.dispose(); - workoutsNode.dispose(); super.dispose(); } @@ -59,38 +63,11 @@ class _EditPlanPageState extends State { padding: const EdgeInsets.all(16.0), child: material.Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Days(onChanged: (days) { - setState(() { - days = days; - }); - }), - TextFormField( - controller: _workoutsController, - focusNode: workoutsNode, - onTap: () { - _workoutsController.selection = TextSelection( - baseOffset: 0, - extentOffset: _workoutsController.text.length); - }, - decoration: const InputDecoration(labelText: 'Workouts'), - onChanged: (value) { - setState(() { - plan = plan.copyWith(workouts: Value(value)); - }); - }, - ), - ], + children: getChildren, ), ), floatingActionButton: FloatingActionButton( onPressed: () async { - if (_daysController.text.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Please enter days'))); - daysNode.requestFocus(); - return; - } if (plan.id.present) await db.update(db.plans).replace(plan); else @@ -102,4 +79,32 @@ class _EditPlanPageState extends State { ), )); } + + List get getChildren { + return [ + Expanded( + child: Days( + onChanged: (days) { + setState(() { + plan = plan.copyWith(days: Value(days)); + }); + }, + days: plan.days.value, + ), + ), + names.isNotEmpty + ? Expanded( + child: Exercises( + onChanged: (exercises) { + setState(() { + plan = plan.copyWith(exercises: Value(exercises)); + }); + }, + exercises: plan.exercises.value, + names: names, + ), + ) + : const CircularProgressIndicator(), + ]; + } } diff --git a/lib/exercises.dart b/lib/exercises.dart new file mode 100644 index 0000000..276e658 --- /dev/null +++ b/lib/exercises.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; + +class Exercises extends StatefulWidget { + final ValueChanged onChanged; + final String? exercises; + final List names; + + const Exercises( + {required this.onChanged, + required this.exercises, + super.key, + required this.names}); + + @override + createState() => _ExercisesState(); +} + +class _ExercisesState extends State { + late List _selections; + + @override + initState() { + super.initState(); + final exercises = widget.exercises?.split(','); + _selections = + widget.names.map((name) => exercises?.contains(name) ?? false).toList(); + } + + String _getExercises() { + List exercises = []; + for (int i = 0; i < _selections.length; i++) + if (_selections[i]) exercises.add(widget.names[i] ?? ''); + return exercises.join(","); + } + + void _updateSelections(int index) { + setState(() { + _selections[index] = !_selections[index]; + widget.onChanged(_getExercises()); + }); + } + + @override + Widget build(BuildContext context) { + return Column(children: [ + Text('Exercises', style: Theme.of(context).textTheme.headlineSmall), + Expanded( + child: ListView( + children: List.generate(widget.names.length, (index) { + return SwitchListTile( + title: Text(widget.names[index] ?? ''), + value: _selections[index], + onChanged: (value) => _updateSelections(index), + ); + }), + ), + ), + ]); + } +} diff --git a/lib/gym_set.dart b/lib/gym_sets.dart similarity index 100% rename from lib/gym_set.dart rename to lib/gym_sets.dart diff --git a/lib/plans.dart b/lib/plans.dart index cda4dcc..869b15f 100644 --- a/lib/plans.dart +++ b/lib/plans.dart @@ -3,5 +3,5 @@ import 'package:moor/moor.dart'; class Plans extends Table { IntColumn get id => integer().autoIncrement()(); TextColumn get days => text()(); - TextColumn get workouts => text()(); + TextColumn get exercises => text().nullable()(); } diff --git a/lib/plans_page.dart b/lib/plans_page.dart index a90cb90..3825875 100644 --- a/lib/plans_page.dart +++ b/lib/plans_page.dart @@ -73,8 +73,8 @@ class _PlansPageState extends State<_PlansPage> { itemBuilder: (context, index) { return ListTile( title: Text(plans[index].days.replaceAll(',', ', ')), - subtitle: - Text(plans[index].workouts.replaceAll(',', ', ')), + subtitle: Text( + plans[index].exercises?.replaceAll(',', ', ') ?? ''), onTap: () async { await Navigator.push( context, @@ -93,8 +93,8 @@ class _PlansPageState extends State<_PlansPage> { context, MaterialPageRoute( builder: (context) => const EditPlanPage( - plan: - PlansCompanion(days: Value(''), workouts: Value(''))), + plan: PlansCompanion( + days: Value(''), exercises: Value(''))), ), ); },