Brandon Presley
19f9dd9b3b
Our app is so fast these will look like bugs to flicker in front of the user.
192 lines
6.2 KiB
Dart
192 lines
6.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/material.dart' as material;
|
|
import 'package:flutter/services.dart';
|
|
import 'package:fmassive/database.dart';
|
|
import 'package:fmassive/edit_plan.dart';
|
|
import 'package:fmassive/main.dart';
|
|
import 'package:moor_flutter/moor_flutter.dart';
|
|
|
|
class StartPlan extends StatefulWidget {
|
|
final PlansCompanion plan;
|
|
|
|
const StartPlan({required this.plan, super.key});
|
|
|
|
@override
|
|
createState() => _StartPlanState();
|
|
}
|
|
|
|
class _StartPlanState extends State<StartPlan> {
|
|
late List<String> exercises;
|
|
List<int> counts = [];
|
|
List<int> totals = [];
|
|
int selectedExercise = 0;
|
|
final repsController = TextEditingController();
|
|
final repsNode = FocusNode();
|
|
final weightController = TextEditingController();
|
|
final weightNode = FocusNode();
|
|
|
|
Future<void> getTotals() async {
|
|
final query = await (db.selectOnly(db.gymSets)
|
|
..addColumns([db.gymSets.name, db.gymSets.sets])
|
|
..where(db.gymSets.name.isIn(exercises))
|
|
..groupBy([db.gymSets.name, db.gymSets.sets]))
|
|
.map((row) =>
|
|
MapEntry(row.read(db.gymSets.name), row.read(db.gymSets.sets)))
|
|
.get();
|
|
final map = Map.fromIterables(
|
|
query.map((entry) => entry.key), query.map((entry) => entry.value));
|
|
setState(() {
|
|
totals = [];
|
|
for (var exercise in exercises) {
|
|
totals.add(map[exercise] ?? 0);
|
|
}
|
|
});
|
|
print("totals=$totals");
|
|
}
|
|
|
|
Future<void> getCounts() async {
|
|
var countExp = db.gymSets.name.count();
|
|
final today = DateTime.now().toIso8601String().split('T')[0];
|
|
final query = await (db.selectOnly(db.gymSets)
|
|
..addColumns([countExp, db.gymSets.name])
|
|
..where(db.gymSets.created.contains(today))
|
|
..groupBy([db.gymSets.name]))
|
|
.map((row) => MapEntry(row.read(db.gymSets.name), row.read(countExp)))
|
|
.get();
|
|
final map = Map.fromIterables(
|
|
query.map((entry) => entry.key), query.map((entry) => entry.value));
|
|
setState(() {
|
|
counts = [];
|
|
for (var exercise in exercises) {
|
|
counts.add(map[exercise] ?? 0);
|
|
}
|
|
});
|
|
print("counts=$counts");
|
|
}
|
|
|
|
Future<void> focus(int index) async {
|
|
final name = exercises[index];
|
|
final sets = await (db.gymSets.select()
|
|
..where((gymSet) => gymSet.name.contains(name))
|
|
..orderBy([
|
|
(u) => OrderingTerm(expression: u.created, mode: OrderingMode.desc),
|
|
])
|
|
..limit(1))
|
|
.get();
|
|
final firstSet = sets.first;
|
|
repsController.text = firstSet.reps.toString();
|
|
repsController.selection = TextSelection(
|
|
baseOffset: 0, extentOffset: firstSet.reps.toString().length);
|
|
weightController.text = firstSet.weight.toString();
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
exercises = widget.plan.exercises.value.split(',');
|
|
repsNode.requestFocus();
|
|
|
|
getCounts();
|
|
getTotals();
|
|
|
|
focus(selectedExercise);
|
|
}
|
|
|
|
@override
|
|
dispose() {
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
List<Widget> actions = [];
|
|
if (widget.plan.id.present)
|
|
actions.add(IconButton(
|
|
onPressed: () async {
|
|
await Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => EditPlanPage(plan: widget.plan),
|
|
),
|
|
);
|
|
},
|
|
icon: const Icon(Icons.edit)));
|
|
|
|
if (totals.isEmpty || counts.isEmpty) return Container();
|
|
|
|
return SafeArea(
|
|
child: Scaffold(
|
|
appBar: AppBar(title: const Text('Start plan'), actions: actions),
|
|
body: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: material.Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Expanded(
|
|
child: ListView.builder(
|
|
itemBuilder: ((context, index) {
|
|
return ListTile(
|
|
title: Text(exercises[index]),
|
|
subtitle: Text("${counts[index]}/${totals[index]}"),
|
|
onTap: () {
|
|
setState(() {
|
|
selectedExercise = index;
|
|
focus(index);
|
|
});
|
|
},
|
|
leading: Radio<int>(
|
|
value: index,
|
|
groupValue: selectedExercise,
|
|
onChanged: (value) {
|
|
print("onChanged $value");
|
|
if (value == null) return;
|
|
setState(() {
|
|
selectedExercise = value;
|
|
focus(index);
|
|
});
|
|
},
|
|
),
|
|
);
|
|
}),
|
|
itemCount: exercises.length,
|
|
),
|
|
),
|
|
TextFormField(
|
|
decoration: const InputDecoration(labelText: 'Reps'),
|
|
controller: repsController,
|
|
focusNode: repsNode,
|
|
onTap: () {
|
|
repsController.selection = TextSelection(
|
|
baseOffset: 0, extentOffset: repsController.text.length);
|
|
},
|
|
),
|
|
TextFormField(
|
|
decoration: const InputDecoration(labelText: 'Weight'),
|
|
controller: weightController,
|
|
focusNode: weightNode,
|
|
onTap: () {
|
|
weightController.selection = TextSelection(
|
|
baseOffset: 0, extentOffset: weightController.text.length);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
floatingActionButton: FloatingActionButton(
|
|
onPressed: () async {
|
|
final gymSet = GymSetsCompanion(
|
|
created: Value(DateTime.now().toIso8601String()),
|
|
name: Value(exercises[selectedExercise]),
|
|
reps: Value(int.tryParse(repsController.text) ?? 0),
|
|
weight: Value(double.tryParse(weightController.text) ?? 0));
|
|
await db.into(db.gymSets).insert(gymSet);
|
|
const platform = MethodChannel('com.massive/android');
|
|
platform.invokeMethod('timer', [180000]);
|
|
await getCounts();
|
|
},
|
|
child: const Icon(Icons.check),
|
|
),
|
|
));
|
|
}
|
|
}
|