import 'dart:async'; 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/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 { late List exercises; List counts = []; List totals = []; int selectedExercise = 0; final repsController = TextEditingController(); final repsNode = FocusNode(); final weightController = TextEditingController(); final weightNode = FocusNode(); int current = 0; int minutes = 3; int seconds = 30; Timer? timer; void startTimer() { setState(() { current = minutes * 60 * 1000 + seconds * 1000; }); timer = Timer.periodic(const Duration(seconds: 1), (timer) { print("StartPlan: current=$current"); if (current == 0) setState(() { timer.cancel(); }); else setState(() { current -= 1000; }); }); } Future 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 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 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; setState(() { repsController.text = firstSet.reps.toString(); repsController.selection = TextSelection( baseOffset: 0, extentOffset: firstSet.reps.toString().length, ); weightController.text = firstSet.weight.toString(); minutes = firstSet.minutes; seconds = firstSet.seconds; }); } @override void initState() { super.initState(); exercises = widget.plan.exercises.value.split(','); repsNode.requestFocus(); getCounts(); getTotals(); focus(selectedExercise); } @override dispose() { super.dispose(); timer?.cancel(); } @override Widget build(BuildContext context) { if (totals.isEmpty || counts.isEmpty) return Container(); return SafeArea( child: Scaffold( appBar: AppBar(title: const Text('Start plan')), 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( 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, ); }, ), LinearProgressIndicator( value: current / (minutes * 60 * 1000 + seconds * 1000), ), ], ), ), 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', [minutes * 60 * 1000 + seconds * 1000]); startTimer(); await getCounts(); }, child: const Icon(Icons.check), ), ), ); } }