From d42696df616878de7d41d89d42362721c5334469 Mon Sep 17 00:00:00 2001 From: Joshua Burman Date: Sat, 30 Nov 2024 12:37:38 -0500 Subject: [PATCH 01/11] show rep and set counts --- lib/screens/activities_screen.dart | 4 ++-- lib/widgets/activity_view.dart | 34 ++++++++++++++++++++++++------ lib/widgets/session_card.dart | 12 +++++------ 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/lib/screens/activities_screen.dart b/lib/screens/activities_screen.dart index 2beb613..1e73c57 100644 --- a/lib/screens/activities_screen.dart +++ b/lib/screens/activities_screen.dart @@ -29,13 +29,13 @@ class _ActivitiesScreenState extends State { activityActionSet: Set( type: 'drop_set', total: 3, - rest: 3000, + rest: 300000, reps: Reps( type: 'count', tempo: [2, 3, 5], amounts: [5, 3, 2], weights: [50, 70, 80], - rest: 200))), + rest: 20000))), )); @override diff --git a/lib/widgets/activity_view.dart b/lib/widgets/activity_view.dart index 28f4f7a..e03f18b 100644 --- a/lib/widgets/activity_view.dart +++ b/lib/widgets/activity_view.dart @@ -19,14 +19,34 @@ class ActivityView extends StatefulWidget { class _ActivityViewState extends State { Timer? _periodicTimer; int _tickCount = 0; - int currentSet = 0; - int currentRep = 0; + int _currentSet = 1; + int _currentRep = 1; + String _currentState = 'Rep'; void _startPeriodicTimer() { + _tickCount = widget.activity.actions[0].activityActionSet.rest ~/ 10000; const oneSecond = Duration(seconds: 1); _periodicTimer = Timer.periodic(oneSecond, (Timer timer) { setState(() { - _tickCount++; + if (_tickCount == 0) { + if (_currentSet == + widget.activity.actions[0].activityActionSet.total) { + timer.cancel(); + } + + if (_currentRep == + widget.activity.actions[0].activityActionSet.reps + .amounts[_currentSet]) { + _currentSet++; + _currentRep = 1; + _tickCount = + widget.activity.actions[0].activityActionSet.rest ~/ 10000; + } else { + _currentRep++; + } + } else { + _tickCount--; + } }); }); } @@ -94,14 +114,14 @@ class _ActivityViewState extends State { textAlign: TextAlign.center, '$_tickCount'), ), - const Expanded( + Expanded( flex: 1, child: Padding( - padding: EdgeInsets.only(right: 10), + padding: const EdgeInsets.only(right: 10), child: Text( - style: TextStyle(fontSize: 15), + style: const TextStyle(fontSize: 15), textAlign: TextAlign.right, - 'Set: 1/3 \nRep: 1/5'))), + 'Set: $_currentSet/3 \nRep: $_currentRep/5'))), ]))); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: content); diff --git a/lib/widgets/session_card.dart b/lib/widgets/session_card.dart index ba2eb46..70cdff3 100644 --- a/lib/widgets/session_card.dart +++ b/lib/widgets/session_card.dart @@ -57,13 +57,13 @@ class SessionCard extends StatelessWidget { activityActionSet: Set( type: 'drop_set', total: 3, - rest: 3000, + rest: 300000, reps: Reps( type: 'count', tempo: [2, 3, 5], amounts: [5, 3, 2], weights: [50, 70, 80], - rest: 200))), + rest: 20000))), ], resources: ['https://www.youtube.com/watch?v=bLz0xp1PEm4']), ActivityModel( @@ -94,13 +94,13 @@ class SessionCard extends StatelessWidget { activityActionSet: Set( type: 'drop_set', total: 3, - rest: 3000, + rest: 300000, reps: Reps( type: 'count', tempo: [2, 3, 5], amounts: [5, 3, 2], weights: [50, 70, 80], - rest: 200))), + rest: 20000))), ], resources: ['https://www.youtube.com/watch?v=dyAvbUvY_PU']), ActivityModel( @@ -131,13 +131,13 @@ class SessionCard extends StatelessWidget { activityActionSet: Set( type: 'drop_set', total: 3, - rest: 3000, + rest: 300000, reps: Reps( type: 'count', tempo: [2, 3, 5], amounts: [5, 3, 2], weights: [50, 70, 80], - rest: 200))), + rest: 20000))), ], resources: ['https://www.youtube.com/watch?v=dyAvbUvY_PU']), ], -- 2.47.2 From 932e9cd6a489dfdf81af9f6232350d458398c4b1 Mon Sep 17 00:00:00 2001 From: Joshua Burman Date: Sat, 30 Nov 2024 12:46:19 -0500 Subject: [PATCH 02/11] add provider package --- pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec.yaml b/pubspec.yaml index 063cff2..5e5f2bf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,6 +39,7 @@ dependencies: intl: ^0.18.0 youtube_player_flutter: ^8.1.2 json_annotation: ^4.9.0 + provider: ^6.1.2 flutter_launcher_name: name: "SendTrain" -- 2.47.2 From 9ffa0d178c0493be82ee5d2af8af431438190713 Mon Sep 17 00:00:00 2001 From: Joshua Burman Date: Sun, 1 Dec 2024 01:42:03 -0500 Subject: [PATCH 03/11] added provider class for timer --- lib/main.dart | 9 +++- lib/models/activity_timer_model.dart | 60 ++++++++++++++++++++++ lib/widgets/activity_view.dart | 75 ++++++++++------------------ 3 files changed, 95 insertions(+), 49 deletions(-) create mode 100644 lib/models/activity_timer_model.dart diff --git a/lib/main.dart b/lib/main.dart index 93a8fab..2cdc9b1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:sendtrain/models/activity_timer_model.dart'; import 'package:sendtrain/screens/activities_screen.dart'; import 'package:sendtrain/screens/sessions_screen.dart'; @@ -88,5 +90,10 @@ class _AppState extends State { } void main() { - runApp(const SendTrain()); + runApp( + ChangeNotifierProvider( + create: (context) => ActivityTimerModel(), + child: const SendTrain(), + ), + ); } diff --git a/lib/models/activity_timer_model.dart b/lib/models/activity_timer_model.dart new file mode 100644 index 0000000..246d00c --- /dev/null +++ b/lib/models/activity_timer_model.dart @@ -0,0 +1,60 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:sendtrain/models/activity_model.dart'; + +class ActivityTimerModel with ChangeNotifier { + late int _activityId; + late int _tickCount; + late int _currentSet; + late int _totalSets; + late int _currentRep; + late int _totalReps; + late String _currentState; + late Timer? _periodicTimer; + + bool _active = false; + + int get tickCount => _tickCount; + int get currentSet => _currentSet; + int get currentRep => _currentRep; + int get totalSets => _totalSets; + int get totalReps => _totalReps; + bool get active => _active; + + void setupTimer(ActivityModel activity) { + _activityId = activity.id; + _currentSet = 0; + _currentRep = 0; + _totalSets = activity.actions[0].activityActionSet.total; + _totalReps = activity.actions[0].activityActionSet.reps.amounts[0]; + _tickCount = activity.actions[0].activityActionSet.rest ~/ 10000; + } + + void startTimer(ActivityModel activity) { + _active = true; + _periodicTimer = Timer.periodic(const Duration(seconds: 1), (Timer timer) { + if (_tickCount == 0) { + if (_currentRep + 1 == + activity.actions[0].activityActionSet.reps.amounts[_currentSet]) { + _currentSet++; + if (_currentSet == activity.actions[0].activityActionSet.total) { + timer.cancel(); + _active = false; + setupTimer(activity); + } + _currentRep = 0; + _totalReps = + activity.actions[0].activityActionSet.reps.amounts[_currentSet]; + _tickCount = activity.actions[0].activityActionSet.rest ~/ 10000; + } else { + _currentRep++; + } + } else { + _tickCount--; + } + + notifyListeners(); + }); + } +} diff --git a/lib/widgets/activity_view.dart b/lib/widgets/activity_view.dart index e03f18b..c9920cc 100644 --- a/lib/widgets/activity_view.dart +++ b/lib/widgets/activity_view.dart @@ -1,9 +1,9 @@ -import 'dart:async'; - import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:sendtrain/classes/activity_action.dart'; import 'package:sendtrain/classes/media.dart'; import 'package:sendtrain/models/activity_model.dart'; +import 'package:sendtrain/models/activity_timer_model.dart'; import 'package:sendtrain/widgets/activity_action_view.dart'; import 'package:sendtrain/widgets/media_card.dart'; @@ -17,44 +17,16 @@ class ActivityView extends StatefulWidget { } class _ActivityViewState extends State { - Timer? _periodicTimer; - int _tickCount = 0; - int _currentSet = 1; - int _currentRep = 1; - String _currentState = 'Rep'; - - void _startPeriodicTimer() { - _tickCount = widget.activity.actions[0].activityActionSet.rest ~/ 10000; - const oneSecond = Duration(seconds: 1); - _periodicTimer = Timer.periodic(oneSecond, (Timer timer) { - setState(() { - if (_tickCount == 0) { - if (_currentSet == - widget.activity.actions[0].activityActionSet.total) { - timer.cancel(); - } - - if (_currentRep == - widget.activity.actions[0].activityActionSet.reps - .amounts[_currentSet]) { - _currentSet++; - _currentRep = 1; - _tickCount = - widget.activity.actions[0].activityActionSet.rest ~/ 10000; - } else { - _currentRep++; - } - } else { - _tickCount--; - } - }); - }); - } - @override Widget build(BuildContext context) { ActivityModel activity = widget.activity; + ActivityTimerModel atm = Provider.of(context, listen: false); + + if (atm.active == false) { + atm.setupTimer(activity); + } + var content = [ AppBar( // surfaceTintColor: ThemeData.dark(useMaterial3: true).colorScheme.primary, @@ -101,27 +73,34 @@ class _ActivityViewState extends State { IconButton( iconSize: 30, icon: const Icon(Icons.play_arrow_rounded), - onPressed: _startPeriodicTimer), + onPressed: () => + atm.startTimer(activity)), IconButton( - iconSize: 30, + iconSize: 36, icon: const Icon(Icons.skip_next_rounded), onPressed: () {}) ])), Expanded( - flex: 1, - child: Text( - style: const TextStyle(fontSize: 25), - textAlign: TextAlign.center, - '$_tickCount'), - ), + flex: 1, + child: Consumer( + builder: (context, atm, child) { + return Text( + style: const TextStyle(fontSize: 25), + textAlign: TextAlign.center, + '${atm.tickCount}'); + }, + )), Expanded( flex: 1, child: Padding( padding: const EdgeInsets.only(right: 10), - child: Text( - style: const TextStyle(fontSize: 15), - textAlign: TextAlign.right, - 'Set: $_currentSet/3 \nRep: $_currentRep/5'))), + child: Consumer( + builder: (context, atm, child) { + return Text( + style: const TextStyle(fontSize: 15), + textAlign: TextAlign.right, + 'Set: ${atm.currentSet + 1}/${atm.totalSets}\nRep: ${atm.currentRep + 1}/${atm.totalReps}'); + }))), ]))); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: content); -- 2.47.2 From 29479e8aba3f9c9f24b3640ceb237654f9fd8bd7 Mon Sep 17 00:00:00 2001 From: Joshua Burman Date: Sun, 1 Dec 2024 21:23:12 -0500 Subject: [PATCH 04/11] setting up timer and action management --- lib/models/activity_timer_model.dart | 132 +++++++++++++++++++++----- lib/widgets/activity_action_view.dart | 107 +++++++++++++++------ lib/widgets/activity_view.dart | 102 ++++++++++---------- lib/widgets/session_card.dart | 34 +++---- 4 files changed, 257 insertions(+), 118 deletions(-) diff --git a/lib/models/activity_timer_model.dart b/lib/models/activity_timer_model.dart index 246d00c..6e860f2 100644 --- a/lib/models/activity_timer_model.dart +++ b/lib/models/activity_timer_model.dart @@ -1,7 +1,9 @@ import 'dart:async'; +import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:sendtrain/models/activity_model.dart'; +import 'package:sendtrain/widgets/activity_action_view.dart'; class ActivityTimerModel with ChangeNotifier { late int _activityId; @@ -12,49 +14,135 @@ class ActivityTimerModel with ChangeNotifier { late int _totalReps; late String _currentState; late Timer? _periodicTimer; + late ActivityModel _activity; + // int _viewCount = 0; bool _active = false; + int _currentAction = 0; int get tickCount => _tickCount; int get currentSet => _currentSet; int get currentRep => _currentRep; + int get currentAction => _currentAction; int get totalSets => _totalSets; int get totalReps => _totalReps; bool get active => _active; + void setAction(int actionNum, int setNum, String state) { + _currentAction = actionNum; + _currentSet = setNum; + _currentState = state; + stopTimer(); + + if (_currentState == "rest") { + startTimer(); + } + + notifyListeners(); + } + + void incrementAction() { + _currentAction++; + + if (_currentState == "rest") { + _currentState = "rep"; + _currentSet++; + stopTimer(); + } else { + _currentState = "rest"; + startTimer(); + } + + notifyListeners(); + } + void setupTimer(ActivityModel activity) { + _activity = activity; _activityId = activity.id; _currentSet = 0; _currentRep = 0; _totalSets = activity.actions[0].activityActionSet.total; _totalReps = activity.actions[0].activityActionSet.reps.amounts[0]; _tickCount = activity.actions[0].activityActionSet.rest ~/ 10000; + _currentState = "rep"; + // ActivityActionView av = actionViews[_viewCount]; + // av. } - void startTimer(ActivityModel activity) { - _active = true; - _periodicTimer = Timer.periodic(const Duration(seconds: 1), (Timer timer) { - if (_tickCount == 0) { - if (_currentRep + 1 == - activity.actions[0].activityActionSet.reps.amounts[_currentSet]) { - _currentSet++; - if (_currentSet == activity.actions[0].activityActionSet.total) { - timer.cancel(); - _active = false; - setupTimer(activity); - } - _currentRep = 0; - _totalReps = - activity.actions[0].activityActionSet.reps.amounts[_currentSet]; - _tickCount = activity.actions[0].activityActionSet.rest ~/ 10000; + void pauseTimer() { + _active = false; + notifyListeners(); + } + + void stopTimer() { + _active = false; + _periodicTimer?.cancel(); + notifyListeners(); + } + + void startTimer() { + if (_currentState == 'rep') { + // do nothing for now + } else { + + _active = true; + _tickCount = _activity.actions[0].activityActionSet.rest ~/ 10000; + + _periodicTimer = Timer.periodic(const Duration(seconds: 1), (Timer timer) { + if (_tickCount == 0) { + incrementAction(); + + + // if (_currentRep + 1 == + // _activity.actions[0].activityActionSet.reps.amounts[_currentSet]) { + // _currentSet++; + // if (_currentSet == _activity.actions[0].activityActionSet.total) { + // timer.cancel(); + // _active = false; + // setupTimer(_activity); + // } + // _currentRep = 0; + // _totalReps = + // _activity.actions[0].activityActionSet.reps.amounts[_currentSet]; + // _tickCount = _activity.actions[0].activityActionSet.rest ~/ 10000; + // } else { + // _currentRep++; + // } } else { - _currentRep++; + if (_active) { + _tickCount--; + } } - } else { - _tickCount--; - } - notifyListeners(); - }); + notifyListeners(); + }); + } } + + // void startTimer(ActivityModel activity) { + // _active = true; + // _periodicTimer = Timer.periodic(const Duration(seconds: 1), (Timer timer) { + // if (_tickCount == 0) { + // if (_currentRep + 1 == + // activity.actions[0].activityActionSet.reps.amounts[_currentSet]) { + // _currentSet++; + // if (_currentSet == activity.actions[0].activityActionSet.total) { + // timer.cancel(); + // _active = false; + // setupTimer(activity); + // } + // _currentRep = 0; + // _totalReps = + // activity.actions[0].activityActionSet.reps.amounts[_currentSet]; + // _tickCount = activity.actions[0].activityActionSet.rest ~/ 10000; + // } else { + // _currentRep++; + // } + // } else { + // _tickCount--; + // } + + // notifyListeners(); + // }); + // } } diff --git a/lib/widgets/activity_action_view.dart b/lib/widgets/activity_action_view.dart index 0d77d50..c3673ed 100644 --- a/lib/widgets/activity_action_view.dart +++ b/lib/widgets/activity_action_view.dart @@ -1,21 +1,47 @@ import 'dart:async'; +import 'dart:developer'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:sendtrain/classes/activity_action.dart'; +import 'package:sendtrain/models/activity_timer_model.dart'; import 'package:sendtrain/widgets/action_card.dart'; class ActivityActionView extends StatefulWidget { ActivityActionView({super.key, required this.action}); ActivityAction action; + // ActivityTimerModel activityTimerModel; + // get incrementActivity => ActivityActionViewState().incrementActivity(); @override - State createState() => _ActivityActionViewState(); + State createState() => ActivityActionViewState(); + // void incrementActivity() => _ActivityActionViewState().incrementActivity(); } -class _ActivityActionViewState extends State { +class ActivityActionViewState extends State { + int _index = 0; + int _currentAction = 0; + // int _actionCount = 0; + // int _currentState = "Active" + + // void incrementActivity(int actionNum) { + // setState(() { + // atm.setAction(actionNum); + // }); + // } + + // void setActivity(int currentAction) { + // setState(() { + // widget.atm.setAction(currentAction); + // }); + // } + @override Widget build(BuildContext context) { + int actionCount = 0; + ActivityTimerModel atm = Provider.of(context, listen: true); + return Expanded( child: ListView.builder( padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), @@ -23,34 +49,61 @@ class _ActivityActionViewState extends State { itemBuilder: (BuildContext context, int index) { String title = widget.action.title; Set actionSet = widget.action.activityActionSet; - Reps setReps = widget.action.activityActionSet.reps; + Reps setReps = actionSet.reps; + int setRest = actionSet.rest ~/ 1000; List contents = []; + int currentAction = actionCount; - // contents.add(Card(child: Text('Set ${index + 1}'))); + contents.add(Card( + elevation: 0.5, + // surfaceTintColor: actionCount == _index ? Colors.white : , + clipBehavior: Clip.antiAlias, + child: GestureDetector( + onTap: () { + log('we tappn'); + setState(() { + atm.setAction(currentAction, index, "rep"); + }); + }, + child: Row(children: [ + Consumer(builder: (context, atm, child) { + return Ink( + padding: const EdgeInsets.all(15), + color: currentAction == atm.currentAction + ? const Color.fromARGB(255, 49, 154, 52) + : const Color(0xff3A5FB6), + child: Text('Set: ${index + 1} ')); + }), + Expanded( + child: Text( + textAlign: TextAlign.center, + '$title: ${setReps.amounts[index]} reps')) + ])))); + actionCount++; - for (int repCount = 0; repCount < setReps.amounts[index]; repCount++) { - contents.add(Card( - child: Padding( - padding: const EdgeInsets.fromLTRB(15, 15, 15, 15), - child: Column( - children: [ - Row(children: [Text('Exercise: ${widget.action.title}')]), - Row(children: [Text('Type: ${actionSet.type}')]), - Row(children: [ - Text( - 'Set: ${index + 1} / ${widget.action.activityActionSet.total}') - ]), - Row(children: [ - Text('Rep: ${repCount + 1} / ${setReps.amounts[index]}') - ]), - Row(children: [Text('Tempo: ${setReps.tempo[index]}')]), - Row(children: [ - Text('Weight: ${setReps.weights[index]}') - ]), - Row(children: [Text('Rest: ${setReps.rest}')]) - ], - )))); - } + contents.add(Card( + clipBehavior: Clip.antiAlias, + child: GestureDetector( + onTap: () { + setState(() { + atm.setAction(currentAction + 1, index, "rest"); + }); + }, + child: Row(children: [ + Consumer(builder: (context, atm, child) { + return Ink( + padding: const EdgeInsets.all(15), + color: currentAction + 1 == atm.currentAction + ? const Color.fromARGB(255, 49, 154, 52) + : const Color(0xff3A5FB6), + child: Text('Set: ${index + 1} ')); + }), + Expanded( + child: Text( + textAlign: TextAlign.center, + 'Rest: $setRest seconds')) + ])))); + actionCount++; return Column(children: contents); }, diff --git a/lib/widgets/activity_view.dart b/lib/widgets/activity_view.dart index c9920cc..264ce45 100644 --- a/lib/widgets/activity_view.dart +++ b/lib/widgets/activity_view.dart @@ -9,7 +9,6 @@ import 'package:sendtrain/widgets/media_card.dart'; class ActivityView extends StatefulWidget { ActivityView({super.key, required this.activity}); - ActivityModel activity; @override @@ -20,14 +19,16 @@ class _ActivityViewState extends State { @override Widget build(BuildContext context) { ActivityModel activity = widget.activity; - - ActivityTimerModel atm = Provider.of(context, listen: false); + ActivityTimerModel atm = + Provider.of(context, listen: false); + // ActivityActionView activityActionView = + // ActivityActionView(action: activity.actions[0]); if (atm.active == false) { atm.setupTimer(activity); } - var content = [ + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ AppBar( // surfaceTintColor: ThemeData.dark(useMaterial3: true).colorScheme.primary, // backgroundColor: ThemeData.dark(useMaterial3: true).colorScheme.primaryContainer, @@ -55,55 +56,52 @@ class _ActivityViewState extends State { child: Text( textAlign: TextAlign.left, style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), - 'Actions:')) - ]; - - for (var action in activity.actions) { - content.add(ActivityActionView(action: action)); - } - - // add bottom bar to manage activity - content.add(Container( - height: MediaQuery.sizeOf(context).height * .07, - color: ThemeData.dark(useMaterial3: true).colorScheme.primaryContainer, - child: Row(children: [ - Expanded( - flex: 1, - child: Flex(direction: Axis.horizontal, children: [ - IconButton( - iconSize: 30, - icon: const Icon(Icons.play_arrow_rounded), - onPressed: () => - atm.startTimer(activity)), - IconButton( - iconSize: 36, - icon: const Icon(Icons.skip_next_rounded), - onPressed: () {}) - ])), - Expanded( - flex: 1, - child: Consumer( - builder: (context, atm, child) { - return Text( - style: const TextStyle(fontSize: 25), - textAlign: TextAlign.center, - '${atm.tickCount}'); - }, - )), - Expanded( - flex: 1, - child: Padding( - padding: const EdgeInsets.only(right: 10), - child: Consumer( - builder: (context, atm, child) { + 'Actions:')), + ActivityActionView(action: activity.actions[0]), + Container( + height: MediaQuery.sizeOf(context).height * .07, + color: + ThemeData.dark(useMaterial3: true).colorScheme.primaryContainer, + child: Row(children: [ + Expanded( + flex: 1, + child: Flex(direction: Axis.horizontal, children: [ + IconButton( + iconSize: 30, + icon: const Icon(Icons.play_arrow_rounded), + onPressed: () => { + atm.startTimer() + }), + IconButton( + iconSize: 36, + icon: const Icon(Icons.skip_next_rounded), + onPressed: () { + atm.incrementAction(); + }) + ])), + Expanded( + flex: 1, + child: Consumer( + builder: (context, atm, child) { return Text( - style: const TextStyle(fontSize: 15), - textAlign: TextAlign.right, - 'Set: ${atm.currentSet + 1}/${atm.totalSets}\nRep: ${atm.currentRep + 1}/${atm.totalReps}'); - }))), - ]))); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, children: content); + style: const TextStyle(fontSize: 25), + textAlign: TextAlign.center, + '${atm.tickCount}'); + }, + )), + Expanded( + flex: 1, + child: Padding( + padding: const EdgeInsets.only(right: 10), + child: Consumer( + builder: (context, atm, child) { + return Text( + style: const TextStyle(fontSize: 15), + textAlign: TextAlign.right, + 'Set: ${atm.currentSet + 1}/${atm.totalSets}\nRep: ${atm.currentRep + 1}/${atm.totalReps}'); + }))), + ])) + ]); } } diff --git a/lib/widgets/session_card.dart b/lib/widgets/session_card.dart index 70cdff3..3d24eaf 100644 --- a/lib/widgets/session_card.dart +++ b/lib/widgets/session_card.dart @@ -39,8 +39,8 @@ class SessionCard extends StatelessWidget { actions: [ ActivityAction( id: 1, - title: 'test action', - description: 'test description', + title: '1, 3, 5', + description: 'Move between the first, third, and fifth rungs, alternating hands. Rest and alternate sides, to start', media: [ Media( id: 1, @@ -76,8 +76,8 @@ class SessionCard extends StatelessWidget { actions: [ ActivityAction( id: 1, - title: 'test action', - description: 'test description', + title: 'Attempt Climb', + description: 'Attempt your selected climb, if you fall off early in the climb, attempt again. 1 repitition equals roughly doing all the moves, not necessarily in 1 attempt.', media: [ Media( id: 1, @@ -92,15 +92,15 @@ class SessionCard extends StatelessWidget { description: 'How to project climbs') ], activityActionSet: Set( - type: 'drop_set', - total: 3, + type: 'standard', + total: 10, rest: 300000, reps: Reps( type: 'count', - tempo: [2, 3, 5], - amounts: [5, 3, 2], - weights: [50, 70, 80], - rest: 20000))), + tempo: [], + amounts: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + weights: [], + rest: 0))), ], resources: ['https://www.youtube.com/watch?v=dyAvbUvY_PU']), ActivityModel( @@ -113,8 +113,8 @@ class SessionCard extends StatelessWidget { actions: [ ActivityAction( id: 1, - title: 'test action', - description: 'test description', + title: 'Pull Ups', + description: 'Select your desired weight to add, and pull up. Be sure to fully drop into extension at start, and make sure chin is above bar for a complete rep. Do not kip during the movement.', media: [ Media( id: 1, @@ -130,14 +130,14 @@ class SessionCard extends StatelessWidget { ], activityActionSet: Set( type: 'drop_set', - total: 3, + total: 5, rest: 300000, reps: Reps( type: 'count', - tempo: [2, 3, 5], - amounts: [5, 3, 2], - weights: [50, 70, 80], - rest: 20000))), + tempo: [], + amounts: [3, 5, 5, 3, 6], + weights: [80, 80, 80, 80, 80], + rest: 0))), ], resources: ['https://www.youtube.com/watch?v=dyAvbUvY_PU']), ], -- 2.47.2 From 5bae1aa416cfbbae30cddaa7e0641b8e55e7ccd4 Mon Sep 17 00:00:00 2001 From: Joshua Burman Date: Thu, 5 Dec 2024 00:44:13 -0500 Subject: [PATCH 05/11] alternating timer types --- lib/models/activity_timer_model.dart | 164 ++++++++------------------ lib/widgets/activity_action_view.dart | 45 +++---- lib/widgets/activity_view.dart | 37 +++--- lib/widgets/session_card.dart | 4 +- 4 files changed, 88 insertions(+), 162 deletions(-) diff --git a/lib/models/activity_timer_model.dart b/lib/models/activity_timer_model.dart index 6e860f2..b34575e 100644 --- a/lib/models/activity_timer_model.dart +++ b/lib/models/activity_timer_model.dart @@ -1,148 +1,84 @@ import 'dart:async'; -import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:sendtrain/models/activity_model.dart'; -import 'package:sendtrain/widgets/activity_action_view.dart'; class ActivityTimerModel with ChangeNotifier { - late int _activityId; - late int _tickCount; - late int _currentSet; - late int _totalSets; - late int _currentRep; - late int _totalReps; - late String _currentState; - late Timer? _periodicTimer; - late ActivityModel _activity; - - // int _viewCount = 0; - bool _active = false; + int _tickCount = 0; int _currentAction = 0; + ActivityModel? _activity; + Timer? _periodicTimer; + List _actionMap = []; int get tickCount => _tickCount; - int get currentSet => _currentSet; - int get currentRep => _currentRep; int get currentAction => _currentAction; - int get totalSets => _totalSets; - int get totalReps => _totalReps; - bool get active => _active; + ActivityModel? get activity => _activity; + Timer? get periodicTimer => _periodicTimer; + String get actionType => _actionMap[_currentAction]; - void setAction(int actionNum, int setNum, String state) { - _currentAction = actionNum; - _currentSet = setNum; - _currentState = state; - stopTimer(); + void setup(ActivityModel activity) { + // if there isn't an activity yet + // or we're coming from another activity + // setup new timer + if (_activity == null || activity.id != _activity?.id) { + _periodicTimer?.cancel(); + _activity = activity; + _currentAction = 0; + _actionMap = []; + // for now we just do alternating rest/sets + // in the future, we'll make this more modifiable + for (int i = 0; i < activity.actions[0].activityActionSet.total; i++) { + _actionMap.addAll(['Set', 'Rest']); - if (_currentState == "rest") { - startTimer(); + } + getValue(); } - - notifyListeners(); } - void incrementAction() { - _currentAction++; + void getValue() { + if (_actionMap[_currentAction] == "Rest") { + _tickCount = _activity!.actions[0].activityActionSet.rest ~/ 1000; - if (_currentState == "rest") { - _currentState = "rep"; - _currentSet++; - stopTimer(); } else { - _currentState = "rest"; - startTimer(); + _tickCount = _activity!.actions[0].activityActionSet.reps.rest ~/ 1000; } - - notifyListeners(); } - void setupTimer(ActivityModel activity) { - _activity = activity; - _activityId = activity.id; - _currentSet = 0; - _currentRep = 0; - _totalSets = activity.actions[0].activityActionSet.total; - _totalReps = activity.actions[0].activityActionSet.reps.amounts[0]; - _tickCount = activity.actions[0].activityActionSet.rest ~/ 10000; - _currentState = "rep"; - // ActivityActionView av = actionViews[_viewCount]; - // av. - } - - void pauseTimer() { - _active = false; - notifyListeners(); - } - - void stopTimer() { - _active = false; + void pause() { _periodicTimer?.cancel(); notifyListeners(); } - void startTimer() { - if (_currentState == 'rep') { - // do nothing for now - } else { + void setAction(int actionNum, String type) { + // always pause if we're manually taversing + if (type == 'manual') { pause(); } + _currentAction = actionNum; + getValue(); + notifyListeners(); + } - _active = true; - _tickCount = _activity.actions[0].activityActionSet.rest ~/ 10000; + void nextAction(String type) { + _currentAction + 1 >= _actionMap.length + ? pause() + : setAction(_currentAction + 1, type); + } - _periodicTimer = Timer.periodic(const Duration(seconds: 1), (Timer timer) { - if (_tickCount == 0) { - incrementAction(); + bool isActive() { + return (_periodicTimer != null && _periodicTimer!.isActive) ? true : false; + } - - // if (_currentRep + 1 == - // _activity.actions[0].activityActionSet.reps.amounts[_currentSet]) { - // _currentSet++; - // if (_currentSet == _activity.actions[0].activityActionSet.total) { - // timer.cancel(); - // _active = false; - // setupTimer(_activity); - // } - // _currentRep = 0; - // _totalReps = - // _activity.actions[0].activityActionSet.reps.amounts[_currentSet]; - // _tickCount = _activity.actions[0].activityActionSet.rest ~/ 10000; - // } else { - // _currentRep++; - // } + void start() { + if (_activity != null) { + _periodicTimer?.cancel(); + _periodicTimer = + Timer.periodic(const Duration(seconds: 1), (Timer timer) { + if (_tickCount <= 0) { + nextAction('automatic'); } else { - if (_active) { - _tickCount--; - } + _tickCount--; } - notifyListeners(); }); } } - - // void startTimer(ActivityModel activity) { - // _active = true; - // _periodicTimer = Timer.periodic(const Duration(seconds: 1), (Timer timer) { - // if (_tickCount == 0) { - // if (_currentRep + 1 == - // activity.actions[0].activityActionSet.reps.amounts[_currentSet]) { - // _currentSet++; - // if (_currentSet == activity.actions[0].activityActionSet.total) { - // timer.cancel(); - // _active = false; - // setupTimer(activity); - // } - // _currentRep = 0; - // _totalReps = - // activity.actions[0].activityActionSet.reps.amounts[_currentSet]; - // _tickCount = activity.actions[0].activityActionSet.rest ~/ 10000; - // } else { - // _currentRep++; - // } - // } else { - // _tickCount--; - // } - - // notifyListeners(); - // }); - // } } diff --git a/lib/widgets/activity_action_view.dart b/lib/widgets/activity_action_view.dart index c3673ed..4765e56 100644 --- a/lib/widgets/activity_action_view.dart +++ b/lib/widgets/activity_action_view.dart @@ -9,38 +9,18 @@ import 'package:sendtrain/widgets/action_card.dart'; class ActivityActionView extends StatefulWidget { ActivityActionView({super.key, required this.action}); - ActivityAction action; - // ActivityTimerModel activityTimerModel; - // get incrementActivity => ActivityActionViewState().incrementActivity(); @override State createState() => ActivityActionViewState(); - // void incrementActivity() => _ActivityActionViewState().incrementActivity(); } class ActivityActionViewState extends State { - int _index = 0; - int _currentAction = 0; - // int _actionCount = 0; - // int _currentState = "Active" - - // void incrementActivity(int actionNum) { - // setState(() { - // atm.setAction(actionNum); - // }); - // } - - // void setActivity(int currentAction) { - // setState(() { - // widget.atm.setAction(currentAction); - // }); - // } - @override Widget build(BuildContext context) { int actionCount = 0; - ActivityTimerModel atm = Provider.of(context, listen: true); + ActivityTimerModel atm = + Provider.of(context, listen: true); return Expanded( child: ListView.builder( @@ -56,13 +36,15 @@ class ActivityActionViewState extends State { contents.add(Card( elevation: 0.5, - // surfaceTintColor: actionCount == _index ? Colors.white : , + shadowColor: Theme.of(context).colorScheme.shadow, + color: currentAction == atm.currentAction + ? Theme.of(context).colorScheme.secondaryContainer + : Theme.of(context).colorScheme.onSecondary, clipBehavior: Clip.antiAlias, child: GestureDetector( onTap: () { - log('we tappn'); setState(() { - atm.setAction(currentAction, index, "rep"); + atm.setAction(currentAction, 'manual'); }); }, child: Row(children: [ @@ -70,8 +52,8 @@ class ActivityActionViewState extends State { return Ink( padding: const EdgeInsets.all(15), color: currentAction == atm.currentAction - ? const Color.fromARGB(255, 49, 154, 52) - : const Color(0xff3A5FB6), + ? Theme.of(context).colorScheme.onPrimary + : Theme.of(context).colorScheme.primaryContainer, child: Text('Set: ${index + 1} ')); }), Expanded( @@ -82,11 +64,14 @@ class ActivityActionViewState extends State { actionCount++; contents.add(Card( + color: currentAction + 1 == atm.currentAction + ? Theme.of(context).colorScheme.secondaryContainer + : Theme.of(context).colorScheme.onSecondary, clipBehavior: Clip.antiAlias, child: GestureDetector( onTap: () { setState(() { - atm.setAction(currentAction + 1, index, "rest"); + atm.setAction(currentAction + 1, 'manual'); }); }, child: Row(children: [ @@ -94,8 +79,8 @@ class ActivityActionViewState extends State { return Ink( padding: const EdgeInsets.all(15), color: currentAction + 1 == atm.currentAction - ? const Color.fromARGB(255, 49, 154, 52) - : const Color(0xff3A5FB6), + ? Theme.of(context).colorScheme.onPrimary + : Theme.of(context).colorScheme.primaryContainer, child: Text('Set: ${index + 1} ')); }), Expanded( diff --git a/lib/widgets/activity_view.dart b/lib/widgets/activity_view.dart index 264ce45..e43770f 100644 --- a/lib/widgets/activity_view.dart +++ b/lib/widgets/activity_view.dart @@ -21,12 +21,8 @@ class _ActivityViewState extends State { ActivityModel activity = widget.activity; ActivityTimerModel atm = Provider.of(context, listen: false); - // ActivityActionView activityActionView = - // ActivityActionView(action: activity.actions[0]); - if (atm.active == false) { - atm.setupTimer(activity); - } + atm.setup(activity); return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ AppBar( @@ -60,23 +56,31 @@ class _ActivityViewState extends State { ActivityActionView(action: activity.actions[0]), Container( height: MediaQuery.sizeOf(context).height * .07, - color: - ThemeData.dark(useMaterial3: true).colorScheme.primaryContainer, + color: Theme.of(context).colorScheme.primaryContainer, child: Row(children: [ + // LinearProgressIndicator( + // value: 0.5, + // minHeight: 100, + // color: Theme.of(context).colorScheme.error, + // semanticsLabel: 'Linear progress indicator', + // ), Expanded( flex: 1, child: Flex(direction: Axis.horizontal, children: [ - IconButton( - iconSize: 30, - icon: const Icon(Icons.play_arrow_rounded), - onPressed: () => { - atm.startTimer() - }), + Consumer(builder: (context, atm, child) { + return IconButton( + iconSize: 30, + icon: atm.isActive() + ? const Icon(Icons.pause_rounded) + : const Icon(Icons.play_arrow_rounded), + onPressed: () => + {atm.isActive() ? atm.pause() : atm.start()}); + }), IconButton( iconSize: 36, icon: const Icon(Icons.skip_next_rounded), onPressed: () { - atm.incrementAction(); + atm.nextAction('manual'); }) ])), Expanded( @@ -96,9 +100,10 @@ class _ActivityViewState extends State { child: Consumer( builder: (context, atm, child) { return Text( - style: const TextStyle(fontSize: 15), + style: const TextStyle(fontSize: 20), textAlign: TextAlign.right, - 'Set: ${atm.currentSet + 1}/${atm.totalSets}\nRep: ${atm.currentRep + 1}/${atm.totalReps}'); + "${atm.actionType}"); + // 'Set: ${atm.currentSet + 1}/${atm.totalSets}\nRep: ${atm.currentRep + 1}/${atm.totalReps}'); }))), ])) ]); diff --git a/lib/widgets/session_card.dart b/lib/widgets/session_card.dart index 3d24eaf..add9dd0 100644 --- a/lib/widgets/session_card.dart +++ b/lib/widgets/session_card.dart @@ -67,7 +67,7 @@ class SessionCard extends StatelessWidget { ], resources: ['https://www.youtube.com/watch?v=bLz0xp1PEm4']), ActivityModel( - id: 1, + id: 2, title: 'Projecting', type: 'fundamental', categories: ['technique', 'conditioning'], @@ -104,7 +104,7 @@ class SessionCard extends StatelessWidget { ], resources: ['https://www.youtube.com/watch?v=dyAvbUvY_PU']), ActivityModel( - id: 1, + id: 3, title: 'Weighted Pull Ups', type: 'fundamental', categories: ['Strength', 'Power'], -- 2.47.2 From 56b25a6963152b65b471808a865a89dcc810d2ca Mon Sep 17 00:00:00 2001 From: Joshua Burman Date: Fri, 6 Dec 2024 01:28:43 -0500 Subject: [PATCH 06/11] allow for reps and times --- lib/models/activity_timer_model.dart | 31 ++++- lib/widgets/activity_action_view.dart | 171 ++++++++++++++++++-------- lib/widgets/activity_view.dart | 8 +- lib/widgets/session_card.dart | 40 +++--- 4 files changed, 166 insertions(+), 84 deletions(-) diff --git a/lib/models/activity_timer_model.dart b/lib/models/activity_timer_model.dart index b34575e..5fe34b8 100644 --- a/lib/models/activity_timer_model.dart +++ b/lib/models/activity_timer_model.dart @@ -15,6 +15,10 @@ class ActivityTimerModel with ChangeNotifier { ActivityModel? get activity => _activity; Timer? get periodicTimer => _periodicTimer; String get actionType => _actionMap[_currentAction]; + String get setType => _activity != null + ? _activity!.actions[0].activityActionSet.reps.type + : 'n/a'; + String? get repType => actionState(); void setup(ActivityModel activity) { // if there isn't an activity yet @@ -27,20 +31,33 @@ class ActivityTimerModel with ChangeNotifier { _actionMap = []; // for now we just do alternating rest/sets // in the future, we'll make this more modifiable - for (int i = 0; i < activity.actions[0].activityActionSet.total; i++) { - _actionMap.addAll(['Set', 'Rest']); + int totalActions = activity.actions[0].activityActionSet.total; + // log(activity.actions[0].activityActionSet.type); + // if (activity.actions[0].activityActionSet.type == 'alternating') { + // totalActions = totalActions * 4; + // log('were in $totalActions'); + // } + for (int i = 0; i < totalActions; i++) { + _actionMap.addAll(['Set', 'Rest']); } getValue(); } } + String actionState() { + if (actionType == 'Set') { + return setType == 'seconds' ? 'Seconds' : 'Repititions'; + } + + return 'Seconds'; + } + void getValue() { if (_actionMap[_currentAction] == "Rest") { _tickCount = _activity!.actions[0].activityActionSet.rest ~/ 1000; - } else { - _tickCount = _activity!.actions[0].activityActionSet.reps.rest ~/ 1000; + _tickCount = _activity!.actions[0].activityActionSet.reps.amounts[0]; } } @@ -51,7 +68,9 @@ class ActivityTimerModel with ChangeNotifier { void setAction(int actionNum, String type) { // always pause if we're manually taversing - if (type == 'manual') { pause(); } + if (type == 'manual' && setType == 'seconds') { + pause(); + } _currentAction = actionNum; getValue(); notifyListeners(); @@ -74,7 +93,7 @@ class ActivityTimerModel with ChangeNotifier { Timer.periodic(const Duration(seconds: 1), (Timer timer) { if (_tickCount <= 0) { nextAction('automatic'); - } else { + } else if (actionState() != 'Repititions') { _tickCount--; } notifyListeners(); diff --git a/lib/widgets/activity_action_view.dart b/lib/widgets/activity_action_view.dart index 4765e56..429a557 100644 --- a/lib/widgets/activity_action_view.dart +++ b/lib/widgets/activity_action_view.dart @@ -24,73 +24,136 @@ class ActivityActionViewState extends State { return Expanded( child: ListView.builder( - padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), + padding: const EdgeInsets.fromLTRB(10, 0, 20, 0), itemCount: widget.action.activityActionSet.total, itemBuilder: (BuildContext context, int index) { String title = widget.action.title; Set actionSet = widget.action.activityActionSet; Reps setReps = actionSet.reps; int setRest = actionSet.rest ~/ 1000; - List contents = []; int currentAction = actionCount; + List content = []; - contents.add(Card( - elevation: 0.5, - shadowColor: Theme.of(context).colorScheme.shadow, - color: currentAction == atm.currentAction - ? Theme.of(context).colorScheme.secondaryContainer - : Theme.of(context).colorScheme.onSecondary, - clipBehavior: Clip.antiAlias, - child: GestureDetector( - onTap: () { - setState(() { - atm.setAction(currentAction, 'manual'); - }); - }, - child: Row(children: [ - Consumer(builder: (context, atm, child) { - return Ink( + actionCount = actionCount + 2; + content.addAll([ + GestureDetector( + onTap: () { + setState(() { + atm.setAction(currentAction, 'manual'); + }); + }, + child: Row(children: [ + Consumer(builder: (context, atm, child) { + return Ink( + width: 100, + padding: const EdgeInsets.all(15), + color: currentAction == atm.currentAction + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context).colorScheme.onPrimary, + child: Text('Set ${index + 1} ')); + }), + Expanded( + child: Ink( padding: const EdgeInsets.all(15), color: currentAction == atm.currentAction - ? Theme.of(context).colorScheme.onPrimary - : Theme.of(context).colorScheme.primaryContainer, - child: Text('Set: ${index + 1} ')); - }), - Expanded( - child: Text( - textAlign: TextAlign.center, - '$title: ${setReps.amounts[index]} reps')) - ])))); - actionCount++; - - contents.add(Card( - color: currentAction + 1 == atm.currentAction - ? Theme.of(context).colorScheme.secondaryContainer - : Theme.of(context).colorScheme.onSecondary, - clipBehavior: Clip.antiAlias, - child: GestureDetector( - onTap: () { - setState(() { - atm.setAction(currentAction + 1, 'manual'); - }); - }, - child: Row(children: [ - Consumer(builder: (context, atm, child) { - return Ink( + ? Theme.of(context).colorScheme.surfaceBright + : Theme.of(context).colorScheme.surfaceContainerLow, + child: Text( + textAlign: TextAlign.center, + '$title: ${setReps.amounts[index]} ${atm.setType}'))) + ])), + GestureDetector( + onTap: () { + setState(() { + atm.setAction(currentAction + 1, 'manual'); + }); + }, + child: Row(children: [ + Consumer(builder: (context, atm, child) { + return Ink( + width: 100, + padding: const EdgeInsets.all(15), + color: currentAction + 1 == atm.currentAction + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context).colorScheme.onPrimary, + child: Text('Rest ${index + 1}')); + }), + Expanded( + child: Ink( padding: const EdgeInsets.all(15), color: currentAction + 1 == atm.currentAction - ? Theme.of(context).colorScheme.onPrimary - : Theme.of(context).colorScheme.primaryContainer, - child: Text('Set: ${index + 1} ')); - }), - Expanded( - child: Text( - textAlign: TextAlign.center, - 'Rest: $setRest seconds')) - ])))); - actionCount++; + ? Theme.of(context).colorScheme.surfaceBright + : Theme.of(context).colorScheme.surfaceContainerLow, + child: Text( + textAlign: TextAlign.center, + 'Rest: $setRest seconds'))) + ])), + ]); - return Column(children: contents); + // if (actionSet.type == 'alternating') { + // actionCount = actionCount + 2; + // content.addAll([ + // GestureDetector( + // onTap: () { + // setState(() { + // atm.setAction(currentAction + 2, 'manual'); + // }); + // }, + // child: Row(children: [ + // Consumer(builder: (context, atm, child) { + // return Ink( + // padding: const EdgeInsets.all(15), + // color: currentAction + 2 == atm.currentAction + // ? Theme.of(context).colorScheme.onPrimary + // : Theme.of(context).colorScheme.primaryContainer, + // child: Text('Set: ${index + 1} ')); + // }), + // Expanded( + // child: Ink( + // padding: const EdgeInsets.all(15), + // color: currentAction + 2 == atm.currentAction + // ? Theme.of(context).colorScheme.onSecondary + // : Theme.of(context) + // .colorScheme + // .surfaceContainerLow, + // child: Text( + // textAlign: TextAlign.center, + // '$title: ${setReps.amounts[index]} ${atm.setType}'))) + // ])), + // GestureDetector( + // onTap: () { + // setState(() { + // atm.setAction(currentAction + 3, 'manual'); + // }); + // }, + // child: Row(children: [ + // Consumer(builder: (context, atm, child) { + // return Ink( + // padding: const EdgeInsets.all(15), + // color: currentAction + 3 == atm.currentAction + // ? Theme.of(context).colorScheme.onPrimary + // : Theme.of(context).colorScheme.primaryContainer, + // child: Text('Set: ${index + 1} ')); + // }), + // Expanded( + // child: Ink( + // padding: const EdgeInsets.all(15), + // color: currentAction + 3 == atm.currentAction + // ? Theme.of(context).colorScheme.onSecondary + // : Theme.of(context) + // .colorScheme + // .surfaceContainerLow, + // child: Text( + // textAlign: TextAlign.center, + // 'Rest: $setRest seconds'))) + // ])), + // ]); + // } + + return Card( + clipBehavior: Clip.antiAlias, child: Column(children: content)); + + // return Column(children: contents); }, )); } diff --git a/lib/widgets/activity_view.dart b/lib/widgets/activity_view.dart index e43770f..18ec70d 100644 --- a/lib/widgets/activity_view.dart +++ b/lib/widgets/activity_view.dart @@ -88,21 +88,21 @@ class _ActivityViewState extends State { child: Consumer( builder: (context, atm, child) { return Text( - style: const TextStyle(fontSize: 25), + style: const TextStyle(fontSize: 20), textAlign: TextAlign.center, - '${atm.tickCount}'); + '${atm.tickCount} ${atm.actionState()}'); }, )), Expanded( flex: 1, child: Padding( - padding: const EdgeInsets.only(right: 10), + padding: const EdgeInsets.only(right: 15), child: Consumer( builder: (context, atm, child) { return Text( style: const TextStyle(fontSize: 20), textAlign: TextAlign.right, - "${atm.actionType}"); + '${atm.currentAction + 1}: ${atm.actionType}'); // 'Set: ${atm.currentSet + 1}/${atm.totalSets}\nRep: ${atm.currentRep + 1}/${atm.totalReps}'); }))), ])) diff --git a/lib/widgets/session_card.dart b/lib/widgets/session_card.dart index add9dd0..b11b3de 100644 --- a/lib/widgets/session_card.dart +++ b/lib/widgets/session_card.dart @@ -59,10 +59,10 @@ class SessionCard extends StatelessWidget { total: 3, rest: 300000, reps: Reps( - type: 'count', - tempo: [2, 3, 5], - amounts: [5, 3, 2], - weights: [50, 70, 80], + type: 'repititions', + tempo: [0], + amounts: [1, 1, 1], + weights: [0], rest: 20000))), ], resources: ['https://www.youtube.com/watch?v=bLz0xp1PEm4']), @@ -96,46 +96,46 @@ class SessionCard extends StatelessWidget { total: 10, rest: 300000, reps: Reps( - type: 'count', - tempo: [], + type: 'repititions', + tempo: [0], amounts: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - weights: [], + weights: [0], rest: 0))), ], resources: ['https://www.youtube.com/watch?v=dyAvbUvY_PU']), ActivityModel( id: 3, - title: 'Weighted Pull Ups', - type: 'fundamental', + title: 'Long Block Pulls', + type: 'Hypertrophy', categories: ['Strength', 'Power'], description: - "Weight pullups to increase strength and maximal pulling force.", + "Block pull on a edge of a specific size and time to induce a hypotrophic effect on the formarms.", actions: [ ActivityAction( id: 1, - title: 'Pull Ups', - description: 'Select your desired weight to add, and pull up. Be sure to fully drop into extension at start, and make sure chin is above bar for a complete rep. Do not kip during the movement.', + title: 'Long Pulls', + description: 'Select your desired weight to pull, add it to the block. You should aim for an effort level of 8-9 when reaching the end of the set time, going to failure will result in significantly extended recovery time.', media: [ Media( id: 1, reference: - 'https://trainingforclimbing.com/wp-content/uploads/2016/03/hypergravity_pull-up-compress3-966x1024.jpg', + 'https://trailandcrag.com/sites/default/files/inline-images/05-min_3.jpg', type: 'image', - description: 'Weighted Pullups'), + description: 'Block pull example'), Media( id: 1, - reference: '7TLG1mHQHgw', + reference: 'sZVAEy9UmoY', type: 'youtube', - description: 'How to do weighted pullups') + description: 'Principals of Grip gains, and related protocols') ], activityActionSet: Set( - type: 'drop_set', + type: 'alternating', total: 5, rest: 300000, reps: Reps( - type: 'count', - tempo: [], - amounts: [3, 5, 5, 3, 6], + type: 'seconds', + tempo: [0], + amounts: [60, 60, 60, 60, 60], weights: [80, 80, 80, 80, 80], rest: 0))), ], -- 2.47.2 From 19f835d8f2fbf65b4f511f43aa705913b86e0628 Mon Sep 17 00:00:00 2001 From: Joshua Burman Date: Fri, 6 Dec 2024 17:33:33 -0500 Subject: [PATCH 07/11] rework timer and how we manage actions --- lib/classes/activity_action.dart | 64 ++++++- lib/models/activity_timer_model.dart | 230 +++++++++++++++++--------- lib/widgets/activity_action_view.dart | 155 +++++++++++------ lib/widgets/activity_view.dart | 86 ++++++++-- 4 files changed, 385 insertions(+), 150 deletions(-) diff --git a/lib/classes/activity_action.dart b/lib/classes/activity_action.dart index 8c1ece1..79caba1 100644 --- a/lib/classes/activity_action.dart +++ b/lib/classes/activity_action.dart @@ -14,6 +14,64 @@ class ActivityAction { required this.activityActionSet, this.media, }); + + List>> items() { + List>> sets = []; + Reps reps = activityActionSet.reps; + + for (int i = 0; i < activityActionSet.total; i++) { + List> actions = []; + int? weight = _setWeight(i); + + actions.add({ + 'name': title, + 'type': reps.type, + 'amount': reps.amounts[i], + 'weight': weight, + }); + + if (activityActionSet.type == 'alternating') { + if (reps.rest != null) { + actions.add({ + 'name': 'Rest', + 'type': 'seconds', + 'amount': reps.amounts[i], + }); + } + + actions.add({ + 'name': title, + 'type': reps.type, + 'amount': reps.amounts[i], + 'weights': weight, + }); + } + + actions.add({ + 'name': 'Rest', + 'type': 'seconds', + 'amount': activityActionSet.rest ~/ 1000, + }); + + sets.add(actions); + + // for (int j = 0; i < activityActionSet.reps.amounts; j++) {} + } + + return sets; + } + + int? _setWeight(setNum) { + Reps reps = activityActionSet.reps; + + if (reps.weights.length == activityActionSet.total) { + return reps.weights[setNum]; + } else if (reps.weights.length == 1) { + return reps.weights[0]; + } + + return null; + } } class Set { @@ -34,14 +92,14 @@ class Reps { String type; List tempo; List amounts; - List weights; - int rest; + List weights = []; + int? rest; Reps({ required this.type, required this.tempo, required this.amounts, required this.weights, - required this.rest, + this.rest, }); } diff --git a/lib/models/activity_timer_model.dart b/lib/models/activity_timer_model.dart index 5fe34b8..e2a8753 100644 --- a/lib/models/activity_timer_model.dart +++ b/lib/models/activity_timer_model.dart @@ -1,103 +1,171 @@ import 'dart:async'; +import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:sendtrain/models/activity_model.dart'; class ActivityTimerModel with ChangeNotifier { - int _tickCount = 0; - int _currentAction = 0; + int _actionCount = 0; ActivityModel? _activity; + List _actions = []; + int _currentActionNum = 0; + int _currentSetNum = 0; Timer? _periodicTimer; - List _actionMap = []; - int get tickCount => _tickCount; - int get currentAction => _currentAction; + int get actionCount => _actionCount; + int get currentActionNum => _currentActionNum; + int get currentSetNum => _currentSetNum; ActivityModel? get activity => _activity; + List get actions => _actions; Timer? get periodicTimer => _periodicTimer; - String get actionType => _actionMap[_currentAction]; - String get setType => _activity != null - ? _activity!.actions[0].activityActionSet.reps.type - : 'n/a'; - String? get repType => actionState(); + String get currentActionType => _actions[_currentSetNum][_currentActionNum]['type']; + void get pause => _periodicTimer!.cancel(); void setup(ActivityModel activity) { - // if there isn't an activity yet - // or we're coming from another activity - // setup new timer - if (_activity == null || activity.id != _activity?.id) { - _periodicTimer?.cancel(); - _activity = activity; - _currentAction = 0; - _actionMap = []; - // for now we just do alternating rest/sets - // in the future, we'll make this more modifiable - int totalActions = activity.actions[0].activityActionSet.total; - // log(activity.actions[0].activityActionSet.type); - // if (activity.actions[0].activityActionSet.type == 'alternating') { - // totalActions = totalActions * 4; - // log('were in $totalActions'); - // } - - for (int i = 0; i < totalActions; i++) { - _actionMap.addAll(['Set', 'Rest']); - } - getValue(); - } - } - - String actionState() { - if (actionType == 'Set') { - return setType == 'seconds' ? 'Seconds' : 'Repititions'; - } - - return 'Seconds'; - } - - void getValue() { - if (_actionMap[_currentAction] == "Rest") { - _tickCount = _activity!.actions[0].activityActionSet.rest ~/ 1000; - } else { - _tickCount = _activity!.actions[0].activityActionSet.reps.amounts[0]; - } - } - - void pause() { - _periodicTimer?.cancel(); - notifyListeners(); - } - - void setAction(int actionNum, String type) { - // always pause if we're manually taversing - if (type == 'manual' && setType == 'seconds') { - pause(); - } - _currentAction = actionNum; - getValue(); - notifyListeners(); - } - - void nextAction(String type) { - _currentAction + 1 >= _actionMap.length - ? pause() - : setAction(_currentAction + 1, type); + _activity = activity; + _actions = activity.actions[0].items(); + _currentActionNum = 0; + _currentSetNum = 0; + setActionCount(); } bool isActive() { return (_periodicTimer != null && _periodicTimer!.isActive) ? true : false; } - void start() { - if (_activity != null) { - _periodicTimer?.cancel(); - _periodicTimer = - Timer.periodic(const Duration(seconds: 1), (Timer timer) { - if (_tickCount <= 0) { - nextAction('automatic'); - } else if (actionState() != 'Repititions') { - _tickCount--; - } - notifyListeners(); - }); + bool isCurrentItem(int setNum, int actionNum) { + if (setNum == _currentSetNum && actionNum == _currentActionNum) { + return true; } + + return false; } + + int totalActions() { + int count = 0; + for(int i = 0; i < _actions.length; i++) { + count = count + _actions[i].length as int; + } + + return count; + } + + void setActionCount() { + _actionCount = _actions[_currentSetNum][_currentActionNum]['amount']; + } + + void start() { + _periodicTimer = Timer.periodic(const Duration(seconds: 1), (Timer timer) { + _actionCount--; + notifyListeners(); + }); + } + + void setAction(int setNum, int actionNum, String type) { + _currentActionNum = actionNum; + _currentSetNum = setNum; + notifyListeners(); + } + + // void nextAction(String type) { + // setAction(_currentActionNum + 1, _getSet(), type); + // } + + // int _actionCount = 0; + // int _currentAction = 0; + // ActivityModel? _activity; + // Timer? _periodicTimer; + // List _actionMap = []; + + // int get actionCount => _actionCount; + // int get currentAction => _currentAction; + // ActivityModel? get activity => _activity; + // Timer? get periodicTimer => _periodicTimer; + // String get actionType => _actionMap[_currentAction]; + // String get setType => _activity != null + // ? _activity!.actions[0].activityActionSet.reps.type + // : 'n/a'; + // String? get repType => actionState(); + // List get sets => _activity!.actions[0].items(); + + // void setup(ActivityModel activity) { + // // if there isn't an activity yet + // // or we're coming from another activity + // // setup new timer + // if (_activity == null || activity.id != _activity?.id) { + // _periodicTimer?.cancel(); + // _activity = activity; + // _currentAction = 0; + // _actionMap = []; + // // for now we just do alternating rest/sets + // // in the future, we'll make this more modifiable + // int totalActions = activity.actions[0].activityActionSet.total; + // // log(activity.actions[0].activityActionSet.type); + // // if (activity.actions[0].activityActionSet.type == 'alternating') { + // // totalActions = totalActions * 4; + // // log('were in $totalActions'); + // // } + + // for (int i = 0; i < totalActions; i++) { + // _actionMap.addAll(['Set', 'Rest']); + // } + // getValue(); + // } + // } + + // String actionState() { + // if (actionType == 'Set') { + // return setType == 'seconds' ? 'Seconds' : 'Repititions'; + // } + + // return 'Seconds'; + // } + + // void getValue() { + // if (_actionMap[_currentAction] == "Rest") { + // _actionCount = _activity!.actions[0].activityActionSet.rest ~/ 1000; + // } else { + // _actionCount = _activity!.actions[0].activityActionSet.reps.amounts[0]; + // } + // } + + // void pause() { + // _periodicTimer?.cancel(); + // notifyListeners(); + // } + + // void setAction(int actionNum, String type) { + // // always pause if we're manually taversing + // if (type == 'manual' && setType == 'seconds') { + // pause(); + // } + // _currentAction = actionNum; + // getValue(); + // notifyListeners(); + // } + + // void nextAction(String type) { + // _currentAction + 1 >= _actionMap.length + // ? pause() + // : setAction(_currentAction + 1, type); + // } + + // bool isActive() { + // return (_periodicTimer != null && _periodicTimer!.isActive) ? true : false; + // } + + // void start() { + // if (_activity != null) { + // _periodicTimer?.cancel(); + // _periodicTimer = + // Timer.periodic(const Duration(seconds: 1), (Timer timer) { + // if (_actionCount <= 0) { + // nextAction('automatic'); + // } else if (actionState() != 'Repititions') { + // _actionCount--; + // } + // notifyListeners(); + // }); + // } + // } } diff --git a/lib/widgets/activity_action_view.dart b/lib/widgets/activity_action_view.dart index 429a557..0174283 100644 --- a/lib/widgets/activity_action_view.dart +++ b/lib/widgets/activity_action_view.dart @@ -21,74 +21,107 @@ class ActivityActionViewState extends State { int actionCount = 0; ActivityTimerModel atm = Provider.of(context, listen: true); + List>> sets = atm.activity!.actions[0].items(); return Expanded( child: ListView.builder( - padding: const EdgeInsets.fromLTRB(10, 0, 20, 0), + padding: const EdgeInsets.fromLTRB(10, 0, 10, 20), itemCount: widget.action.activityActionSet.total, - itemBuilder: (BuildContext context, int index) { + itemBuilder: (BuildContext context, int setNum) { String title = widget.action.title; Set actionSet = widget.action.activityActionSet; Reps setReps = actionSet.reps; int setRest = actionSet.rest ~/ 1000; - int currentAction = actionCount; + int currentAction = 0; List content = []; - actionCount = actionCount + 2; - content.addAll([ - GestureDetector( + List> set = sets[setNum]; + // log('${sets.length}'); + // log('${set.length}'); + for (int actionNum = 0; actionNum < set.length; actionNum++) { + Map setItem = set[actionNum]; + + content.add(GestureDetector( onTap: () { - setState(() { - atm.setAction(currentAction, 'manual'); - }); + atm.setAction(setNum, actionNum, 'manual'); + atm.setActionCount(); }, child: Row(children: [ - Consumer(builder: (context, atm, child) { - return Ink( - width: 100, - padding: const EdgeInsets.all(15), - color: currentAction == atm.currentAction - ? Theme.of(context).colorScheme.primaryContainer - : Theme.of(context).colorScheme.onPrimary, - child: Text('Set ${index + 1} ')); - }), + Ink( + // width: 90, + padding: const EdgeInsets.all(15), + color: atm.isCurrentItem(setNum, actionNum) + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context).colorScheme.onPrimary, + child: + Text(textAlign: TextAlign.right, 'Set ${setNum + 1} ')), Expanded( child: Ink( padding: const EdgeInsets.all(15), - color: currentAction == atm.currentAction + color: atm.isCurrentItem(setNum, actionNum) ? Theme.of(context).colorScheme.surfaceBright : Theme.of(context).colorScheme.surfaceContainerLow, child: Text( textAlign: TextAlign.center, - '$title: ${setReps.amounts[index]} ${atm.setType}'))) - ])), - GestureDetector( - onTap: () { - setState(() { - atm.setAction(currentAction + 1, 'manual'); - }); - }, - child: Row(children: [ - Consumer(builder: (context, atm, child) { - return Ink( - width: 100, - padding: const EdgeInsets.all(15), - color: currentAction + 1 == atm.currentAction - ? Theme.of(context).colorScheme.primaryContainer - : Theme.of(context).colorScheme.onPrimary, - child: Text('Rest ${index + 1}')); - }), - Expanded( - child: Ink( - padding: const EdgeInsets.all(15), - color: currentAction + 1 == atm.currentAction - ? Theme.of(context).colorScheme.surfaceBright - : Theme.of(context).colorScheme.surfaceContainerLow, - child: Text( - textAlign: TextAlign.center, - 'Rest: $setRest seconds'))) - ])), - ]); + '${setItem['name']}: ${setItem['amount']} ${setItem['type']}'))) + ]))); + } + + // actionCount = actionCount + 2; + // content.addAll([ + // GestureDetector( + // onTap: () { + // setState(() { + // atm.setAction(currentAction, 'manual'); + // }); + // }, + // child: Row(children: [ + // Consumer(builder: (context, atm, child) { + // return Ink( + // width: 90, + // padding: const EdgeInsets.all(15), + // color: currentAction == atm.currentAction + // ? Theme.of(context).colorScheme.primaryContainer + // : Theme.of(context).colorScheme.onPrimary, + // child: Text(textAlign: TextAlign.right,'Set ${index + 1} ')); + // }), + // Expanded( + // child: Ink( + // padding: const EdgeInsets.all(15), + // color: currentAction == atm.currentAction + // ? Theme.of(context).colorScheme.surfaceBright + // : Theme.of(context).colorScheme.surfaceContainerLow, + // child: Text( + // textAlign: TextAlign.center, + // '$title: ${setReps.amounts[index]} ${atm.setType}'))) + // ])), + // GestureDetector( + // onTap: () { + // setState(() { + // atm.setAction(currentAction + 1, 'manual'); + // }); + // }, + // child: Row(children: [ + // Consumer(builder: (context, atm, child) { + // return Ink( + // width: 90, + // padding: const EdgeInsets.all(15), + // color: currentAction + 1 == atm.currentAction + // ? Theme.of(context).colorScheme.primaryContainer + // : Theme.of(context).colorScheme.onPrimary, + // child: Text(textAlign: TextAlign.right,'Rest ${index + 1}')); + // }), + // Expanded( + // child: Ink( + // padding: const EdgeInsets.all(15), + // color: currentAction + 1 == atm.currentAction + // ? Theme.of(context).colorScheme.surfaceBright + // : Theme.of(context).colorScheme.surfaceContainerLow, + // child: Text( + // textAlign: TextAlign.center, + // 'Rest: $setRest seconds'))) + // ])), + // ]); // if (actionSet.type == 'alternating') { // actionCount = actionCount + 2; @@ -150,9 +183,29 @@ class ActivityActionViewState extends State { // ]); // } - return Card( - clipBehavior: Clip.antiAlias, child: Column(children: content)); - + if (setNum == 0) { + return Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(0), + topRight: Radius.circular(0), + bottomLeft: Radius.circular(10), + bottomRight: Radius.circular(10)), + ), + clipBehavior: Clip.antiAlias, + child: Column(children: content)); + } else { + return Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10), + bottomLeft: Radius.circular(10), + bottomRight: Radius.circular(10)), + ), + clipBehavior: Clip.antiAlias, + child: Column(children: content)); + } // return Column(children: contents); }, )); diff --git a/lib/widgets/activity_view.dart b/lib/widgets/activity_view.dart index 18ec70d..0e048c7 100644 --- a/lib/widgets/activity_view.dart +++ b/lib/widgets/activity_view.dart @@ -53,10 +53,13 @@ class _ActivityViewState extends State { textAlign: TextAlign.left, style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), 'Actions:')), - ActivityActionView(action: activity.actions[0]), - Container( - height: MediaQuery.sizeOf(context).height * .07, - color: Theme.of(context).colorScheme.primaryContainer, + Padding( + padding: const EdgeInsets.only(left: 10, right: 10), + child: Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10)), + ), + color: Theme.of(context).colorScheme.onPrimary, child: Row(children: [ // LinearProgressIndicator( // value: 0.5, @@ -69,19 +72,19 @@ class _ActivityViewState extends State { child: Flex(direction: Axis.horizontal, children: [ Consumer(builder: (context, atm, child) { return IconButton( - iconSize: 30, + // iconSize: 30, icon: atm.isActive() ? const Icon(Icons.pause_rounded) : const Icon(Icons.play_arrow_rounded), onPressed: () => - {atm.isActive() ? atm.pause() : atm.start()}); + {atm.isActive() ? atm.pause : atm.start()}); }), - IconButton( - iconSize: 36, - icon: const Icon(Icons.skip_next_rounded), - onPressed: () { - atm.nextAction('manual'); - }) + // IconButton( + // // iconSize: 36, + // icon: const Icon(Icons.skip_next_rounded), + // onPressed: () { + // atm.nextAction('manual'); + // }) ])), Expanded( flex: 1, @@ -90,7 +93,7 @@ class _ActivityViewState extends State { return Text( style: const TextStyle(fontSize: 20), textAlign: TextAlign.center, - '${atm.tickCount} ${atm.actionState()}'); + '${atm.actionCount} ${atm.currentActionType}'); }, )), Expanded( @@ -102,10 +105,63 @@ class _ActivityViewState extends State { return Text( style: const TextStyle(fontSize: 20), textAlign: TextAlign.right, - '${atm.currentAction + 1}: ${atm.actionType}'); + '${atm.currentActionNum + 1} / ${atm.totalActions()}'); // 'Set: ${atm.currentSet + 1}/${atm.totalSets}\nRep: ${atm.currentRep + 1}/${atm.totalReps}'); }))), - ])) + ]))), + ActivityActionView(action: activity.actions[0]), + // Container( + // height: MediaQuery.sizeOf(context).height * .07, + // color: Theme.of(context).colorScheme.primaryContainer, + // child: Row(children: [ + // // LinearProgressIndicator( + // // value: 0.5, + // // minHeight: 100, + // // color: Theme.of(context).colorScheme.error, + // // semanticsLabel: 'Linear progress indicator', + // // ), + // Expanded( + // flex: 1, + // child: Flex(direction: Axis.horizontal, children: [ + // Consumer(builder: (context, atm, child) { + // return IconButton( + // iconSize: 30, + // icon: atm.isActive() + // ? const Icon(Icons.pause_rounded) + // : const Icon(Icons.play_arrow_rounded), + // onPressed: () => + // {atm.isActive() ? atm.pause() : atm.start()}); + // }), + // IconButton( + // iconSize: 36, + // icon: const Icon(Icons.skip_next_rounded), + // onPressed: () { + // atm.nextAction('manual'); + // }) + // ])), + // Expanded( + // flex: 1, + // child: Consumer( + // builder: (context, atm, child) { + // return Text( + // style: const TextStyle(fontSize: 20), + // textAlign: TextAlign.center, + // '${atm.actionCount} ${atm.actionState()}'); + // }, + // )), + // Expanded( + // flex: 1, + // child: Padding( + // padding: const EdgeInsets.only(right: 15), + // child: Consumer( + // builder: (context, atm, child) { + // return Text( + // style: const TextStyle(fontSize: 20), + // textAlign: TextAlign.right, + // '${atm.currentAction + 1}: ${atm.actionType}'); + // // 'Set: ${atm.currentSet + 1}/${atm.totalSets}\nRep: ${atm.currentRep + 1}/${atm.totalReps}'); + // }))), + // ])) ]); } } -- 2.47.2 From f781001d3b3148395d592d106fbc140ed957b30d Mon Sep 17 00:00:00 2001 From: Joshua Burman Date: Sat, 7 Dec 2024 13:13:45 -0500 Subject: [PATCH 08/11] upgraded dart and packages, analyze fixes, action type timer management --- .gitignore | 1 + lib/classes/activity_action.dart | 5 + lib/models/activity_timer_model.dart | 55 ++++--- lib/widgets/activities_header.dart | 4 +- lib/widgets/activity_action_view.dart | 134 +----------------- lib/widgets/activity_card.dart | 1 - lib/widgets/activity_view.dart | 72 ++++++---- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.yaml | 8 +- .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 11 files changed, 104 insertions(+), 182 deletions(-) diff --git a/.gitignore b/.gitignore index 065dc2c..84fa0f7 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ migrate_working_dir/ .pub/ /build/ pubspec.lock +devtools_options.yaml # Symbolication related app.*.symbols diff --git a/lib/classes/activity_action.dart b/lib/classes/activity_action.dart index 79caba1..6fd7a5a 100644 --- a/lib/classes/activity_action.dart +++ b/lib/classes/activity_action.dart @@ -18,12 +18,14 @@ class ActivityAction { List>> items() { List>> sets = []; Reps reps = activityActionSet.reps; + int totalActions = 1; for (int i = 0; i < activityActionSet.total; i++) { List> actions = []; int? weight = _setWeight(i); actions.add({ + 'actionID': totalActions++, 'name': title, 'type': reps.type, 'amount': reps.amounts[i], @@ -33,6 +35,7 @@ class ActivityAction { if (activityActionSet.type == 'alternating') { if (reps.rest != null) { actions.add({ + 'actionID': totalActions++, 'name': 'Rest', 'type': 'seconds', 'amount': reps.amounts[i], @@ -40,6 +43,7 @@ class ActivityAction { } actions.add({ + 'actionID': totalActions++, 'name': title, 'type': reps.type, 'amount': reps.amounts[i], @@ -48,6 +52,7 @@ class ActivityAction { } actions.add({ + 'actionID': totalActions++, 'name': 'Rest', 'type': 'seconds', 'amount': activityActionSet.rest ~/ 1000, diff --git a/lib/models/activity_timer_model.dart b/lib/models/activity_timer_model.dart index e2a8753..e4e3cb0 100644 --- a/lib/models/activity_timer_model.dart +++ b/lib/models/activity_timer_model.dart @@ -1,35 +1,36 @@ import 'dart:async'; -import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:sendtrain/models/activity_model.dart'; class ActivityTimerModel with ChangeNotifier { - int _actionCount = 0; + int _actionCounter = 0; ActivityModel? _activity; - List _actions = []; + List _sets = []; int _currentActionNum = 0; int _currentSetNum = 0; Timer? _periodicTimer; - int get actionCount => _actionCount; + int get actionCount => _actionCounter; int get currentActionNum => _currentActionNum; + dynamic get currentAction => currentSet[_currentActionNum]; int get currentSetNum => _currentSetNum; + dynamic get currentSet => _sets[_currentSetNum]; ActivityModel? get activity => _activity; - List get actions => _actions; + List get sets => _sets; Timer? get periodicTimer => _periodicTimer; - String get currentActionType => _actions[_currentSetNum][_currentActionNum]['type']; + bool get isActive => _isActive(); void get pause => _periodicTimer!.cancel(); void setup(ActivityModel activity) { _activity = activity; - _actions = activity.actions[0].items(); + _sets = activity.actions[0].items(); _currentActionNum = 0; _currentSetNum = 0; setActionCount(); } - bool isActive() { + bool _isActive() { return (_periodicTimer != null && _periodicTimer!.isActive) ? true : false; } @@ -43,20 +44,30 @@ class ActivityTimerModel with ChangeNotifier { int totalActions() { int count = 0; - for(int i = 0; i < _actions.length; i++) { - count = count + _actions[i].length as int; + for(int i = 0; i < _sets.length; i++) { + count = count + _sets[i].length as int; } return count; } void setActionCount() { - _actionCount = _actions[_currentSetNum][_currentActionNum]['amount']; + _actionCounter = _sets[_currentSetNum][_currentActionNum]['amount']; } void start() { _periodicTimer = Timer.periodic(const Duration(seconds: 1), (Timer timer) { - _actionCount--; + switch(currentAction['type']) { + case 'repititions': + break; + case 'seconds': + if (_actionCounter > 0) { + _actionCounter--; + } else { + nextAction(_currentActionNum + 1); + } + } + notifyListeners(); }); } @@ -67,17 +78,25 @@ class ActivityTimerModel with ChangeNotifier { notifyListeners(); } + void nextAction(int nextActionID) { + + } + + (int, int) _findAction(int actionID) { + return (1, 2); + } + // void nextAction(String type) { // setAction(_currentActionNum + 1, _getSet(), type); // } - // int _actionCount = 0; + // int _actionCounter = 0; // int _currentAction = 0; // ActivityModel? _activity; // Timer? _periodicTimer; // List _actionMap = []; - // int get actionCount => _actionCount; + // int get actionCount => _actionCounter; // int get currentAction => _currentAction; // ActivityModel? get activity => _activity; // Timer? get periodicTimer => _periodicTimer; @@ -123,9 +142,9 @@ class ActivityTimerModel with ChangeNotifier { // void getValue() { // if (_actionMap[_currentAction] == "Rest") { - // _actionCount = _activity!.actions[0].activityActionSet.rest ~/ 1000; + // _actionCounter = _activity!.actions[0].activityActionSet.rest ~/ 1000; // } else { - // _actionCount = _activity!.actions[0].activityActionSet.reps.amounts[0]; + // _actionCounter = _activity!.actions[0].activityActionSet.reps.amounts[0]; // } // } @@ -159,10 +178,10 @@ class ActivityTimerModel with ChangeNotifier { // _periodicTimer?.cancel(); // _periodicTimer = // Timer.periodic(const Duration(seconds: 1), (Timer timer) { - // if (_actionCount <= 0) { + // if (_actionCounter <= 0) { // nextAction('automatic'); // } else if (actionState() != 'Repititions') { - // _actionCount--; + // _actionCounter--; // } // notifyListeners(); // }); diff --git a/lib/widgets/activities_header.dart b/lib/widgets/activities_header.dart index 4411663..a5be690 100644 --- a/lib/widgets/activities_header.dart +++ b/lib/widgets/activities_header.dart @@ -5,10 +5,10 @@ class ActivitiesHeader extends StatefulWidget { const ActivitiesHeader({super.key}); @override - _ActivitiesHeaderState createState() => _ActivitiesHeaderState(); + State createState() => ActivitiesHeaderState(); } -class _ActivitiesHeaderState extends State { +class ActivitiesHeaderState extends State { @override void initState() { super.initState(); diff --git a/lib/widgets/activity_action_view.dart b/lib/widgets/activity_action_view.dart index 0174283..794b81a 100644 --- a/lib/widgets/activity_action_view.dart +++ b/lib/widgets/activity_action_view.dart @@ -1,15 +1,11 @@ -import 'dart:async'; -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:sendtrain/classes/activity_action.dart'; import 'package:sendtrain/models/activity_timer_model.dart'; -import 'package:sendtrain/widgets/action_card.dart'; class ActivityActionView extends StatefulWidget { - ActivityActionView({super.key, required this.action}); - ActivityAction action; + const ActivityActionView({super.key, required this.action}); + final ActivityAction action; @override State createState() => ActivityActionViewState(); @@ -18,7 +14,6 @@ class ActivityActionView extends StatefulWidget { class ActivityActionViewState extends State { @override Widget build(BuildContext context) { - int actionCount = 0; ActivityTimerModel atm = Provider.of(context, listen: true); List>> sets = atm.activity!.actions[0].items(); @@ -28,11 +23,6 @@ class ActivityActionViewState extends State { padding: const EdgeInsets.fromLTRB(10, 0, 10, 20), itemCount: widget.action.activityActionSet.total, itemBuilder: (BuildContext context, int setNum) { - String title = widget.action.title; - Set actionSet = widget.action.activityActionSet; - Reps setReps = actionSet.reps; - int setRest = actionSet.rest ~/ 1000; - int currentAction = 0; List content = []; List> set = sets[setNum]; @@ -48,13 +38,13 @@ class ActivityActionViewState extends State { }, child: Row(children: [ Ink( - // width: 90, + width: 70, padding: const EdgeInsets.all(15), color: atm.isCurrentItem(setNum, actionNum) ? Theme.of(context).colorScheme.primaryContainer : Theme.of(context).colorScheme.onPrimary, child: - Text(textAlign: TextAlign.right, 'Set ${setNum + 1} ')), + Text(textAlign: TextAlign.center, 'Set ${setNum + 1} ')), Expanded( child: Ink( padding: const EdgeInsets.all(15), @@ -67,122 +57,6 @@ class ActivityActionViewState extends State { ]))); } - // actionCount = actionCount + 2; - // content.addAll([ - // GestureDetector( - // onTap: () { - // setState(() { - // atm.setAction(currentAction, 'manual'); - // }); - // }, - // child: Row(children: [ - // Consumer(builder: (context, atm, child) { - // return Ink( - // width: 90, - // padding: const EdgeInsets.all(15), - // color: currentAction == atm.currentAction - // ? Theme.of(context).colorScheme.primaryContainer - // : Theme.of(context).colorScheme.onPrimary, - // child: Text(textAlign: TextAlign.right,'Set ${index + 1} ')); - // }), - // Expanded( - // child: Ink( - // padding: const EdgeInsets.all(15), - // color: currentAction == atm.currentAction - // ? Theme.of(context).colorScheme.surfaceBright - // : Theme.of(context).colorScheme.surfaceContainerLow, - // child: Text( - // textAlign: TextAlign.center, - // '$title: ${setReps.amounts[index]} ${atm.setType}'))) - // ])), - // GestureDetector( - // onTap: () { - // setState(() { - // atm.setAction(currentAction + 1, 'manual'); - // }); - // }, - // child: Row(children: [ - // Consumer(builder: (context, atm, child) { - // return Ink( - // width: 90, - // padding: const EdgeInsets.all(15), - // color: currentAction + 1 == atm.currentAction - // ? Theme.of(context).colorScheme.primaryContainer - // : Theme.of(context).colorScheme.onPrimary, - // child: Text(textAlign: TextAlign.right,'Rest ${index + 1}')); - // }), - // Expanded( - // child: Ink( - // padding: const EdgeInsets.all(15), - // color: currentAction + 1 == atm.currentAction - // ? Theme.of(context).colorScheme.surfaceBright - // : Theme.of(context).colorScheme.surfaceContainerLow, - // child: Text( - // textAlign: TextAlign.center, - // 'Rest: $setRest seconds'))) - // ])), - // ]); - - // if (actionSet.type == 'alternating') { - // actionCount = actionCount + 2; - // content.addAll([ - // GestureDetector( - // onTap: () { - // setState(() { - // atm.setAction(currentAction + 2, 'manual'); - // }); - // }, - // child: Row(children: [ - // Consumer(builder: (context, atm, child) { - // return Ink( - // padding: const EdgeInsets.all(15), - // color: currentAction + 2 == atm.currentAction - // ? Theme.of(context).colorScheme.onPrimary - // : Theme.of(context).colorScheme.primaryContainer, - // child: Text('Set: ${index + 1} ')); - // }), - // Expanded( - // child: Ink( - // padding: const EdgeInsets.all(15), - // color: currentAction + 2 == atm.currentAction - // ? Theme.of(context).colorScheme.onSecondary - // : Theme.of(context) - // .colorScheme - // .surfaceContainerLow, - // child: Text( - // textAlign: TextAlign.center, - // '$title: ${setReps.amounts[index]} ${atm.setType}'))) - // ])), - // GestureDetector( - // onTap: () { - // setState(() { - // atm.setAction(currentAction + 3, 'manual'); - // }); - // }, - // child: Row(children: [ - // Consumer(builder: (context, atm, child) { - // return Ink( - // padding: const EdgeInsets.all(15), - // color: currentAction + 3 == atm.currentAction - // ? Theme.of(context).colorScheme.onPrimary - // : Theme.of(context).colorScheme.primaryContainer, - // child: Text('Set: ${index + 1} ')); - // }), - // Expanded( - // child: Ink( - // padding: const EdgeInsets.all(15), - // color: currentAction + 3 == atm.currentAction - // ? Theme.of(context).colorScheme.onSecondary - // : Theme.of(context) - // .colorScheme - // .surfaceContainerLow, - // child: Text( - // textAlign: TextAlign.center, - // 'Rest: $setRest seconds'))) - // ])), - // ]); - // } - if (setNum == 0) { return Card( shape: const RoundedRectangleBorder( diff --git a/lib/widgets/activity_card.dart b/lib/widgets/activity_card.dart index 91c8e74..52783fc 100644 --- a/lib/widgets/activity_card.dart +++ b/lib/widgets/activity_card.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:sendtrain/classes/media.dart'; import 'package:sendtrain/models/activity_model.dart'; import 'package:sendtrain/widgets/activity_view.dart'; -import 'package:sendtrain/widgets/media_card.dart'; class ActivityCard extends StatelessWidget { const ActivityCard({super.key, required this.activity}); diff --git a/lib/widgets/activity_view.dart b/lib/widgets/activity_view.dart index 0e048c7..2ed711c 100644 --- a/lib/widgets/activity_view.dart +++ b/lib/widgets/activity_view.dart @@ -8,8 +8,8 @@ import 'package:sendtrain/widgets/activity_action_view.dart'; import 'package:sendtrain/widgets/media_card.dart'; class ActivityView extends StatefulWidget { - ActivityView({super.key, required this.activity}); - ActivityModel activity; + const ActivityView({super.key, required this.activity}); + final ActivityModel activity; @override State createState() => _ActivityViewState(); @@ -56,44 +56,62 @@ class _ActivityViewState extends State { Padding( padding: const EdgeInsets.only(left: 10, right: 10), child: Card( + clipBehavior: Clip.antiAlias, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10)), ), color: Theme.of(context).colorScheme.onPrimary, - child: Row(children: [ + child: Padding( + padding: const EdgeInsets.only(left: 0, right: 0), + child: Row(children: [ // LinearProgressIndicator( // value: 0.5, // minHeight: 100, // color: Theme.of(context).colorScheme.error, // semanticsLabel: 'Linear progress indicator', // ), - Expanded( - flex: 1, - child: Flex(direction: Axis.horizontal, children: [ - Consumer(builder: (context, atm, child) { + // Expanded( + // flex: 1, + // child: Flex(direction: Axis.horizontal, children: [ + // Consumer(builder: (context, atm, child) { + // return IconButton( + // iconSize: 30, + // icon: atm.isActive() + // ? const Icon(Icons.pause_rounded) + // : const Icon(Icons.play_arrow_rounded), + // onPressed: () => + // {atm.isActive() ? atm.pause : atm.start()}); + // }), + // // IconButton( + // // // iconSize: 36, + // // icon: const Icon(Icons.skip_next_rounded), + // // onPressed: () { + // // atm.nextAction('manual'); + // // }) + // ])), + Ink( + width: 70, + color: Theme.of(context).colorScheme.primaryContainer, + child: Consumer( + builder: (context, atm, child) { return IconButton( - // iconSize: 30, - icon: atm.isActive() - ? const Icon(Icons.pause_rounded) - : const Icon(Icons.play_arrow_rounded), - onPressed: () => - {atm.isActive() ? atm.pause : atm.start()}); - }), - // IconButton( - // // iconSize: 36, - // icon: const Icon(Icons.skip_next_rounded), - // onPressed: () { - // atm.nextAction('manual'); - // }) - ])), + alignment: AlignmentDirectional.center, + iconSize: 30, + icon: atm.isActive + ? const Icon(Icons.pause_rounded) + : const Icon(Icons.play_arrow_rounded), + onPressed: () => + {atm.isActive ? atm.pause : atm.start()}); + }, + )), Expanded( - flex: 1, + flex: 2, child: Consumer( builder: (context, atm, child) { return Text( style: const TextStyle(fontSize: 20), - textAlign: TextAlign.center, - '${atm.actionCount} ${atm.currentActionType}'); + textAlign: TextAlign.right, + '${atm.actionCount} ${atm.currentAction['type']}'); }, )), Expanded( @@ -103,12 +121,12 @@ class _ActivityViewState extends State { child: Consumer( builder: (context, atm, child) { return Text( - style: const TextStyle(fontSize: 20), + style: const TextStyle(fontSize: 15), textAlign: TextAlign.right, - '${atm.currentActionNum + 1} / ${atm.totalActions()}'); + '${atm.currentAction['actionID']} | ${atm.totalActions()}'); // 'Set: ${atm.currentSet + 1}/${atm.totalSets}\nRep: ${atm.currentRep + 1}/${atm.totalReps}'); }))), - ]))), + ])))), ActivityActionView(action: activity.actions[0]), // Container( // height: MediaQuery.sizeOf(context).height * .07, diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..738fc0a 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import flutter_inappwebview_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) } diff --git a/pubspec.yaml b/pubspec.yaml index 5e5f2bf..fe7c8e9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 0.2.1 environment: - sdk: '>=2.19.2 <3.0.0' + sdk: '>=3.0.0 <4.0.0' # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -36,8 +36,8 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 scaler: ^1.1.2+1 - intl: ^0.18.0 - youtube_player_flutter: ^8.1.2 + intl: ^0.20.1 + youtube_player_flutter: ^9.1.1 json_annotation: ^4.9.0 provider: ^6.1.2 @@ -53,7 +53,7 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^2.0.0 + flutter_lints: ^5.0.0 build_runner: ^2.4.13 json_serializable: ^6.9.0 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..3b4ee90 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..61c79a2 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + flutter_inappwebview_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST -- 2.47.2 From 0c0f596fbb6b7e8e5f87b90e59ca799fc4a9d857 Mon Sep 17 00:00:00 2001 From: Joshua Burman Date: Sat, 7 Dec 2024 15:25:03 -0500 Subject: [PATCH 09/11] reset timer and activity when complete --- lib/classes/activity_action.dart | 11 +- lib/models/activity_timer_model.dart | 134 +++---------------- lib/widgets/activity_action_view.dart | 2 +- lib/widgets/activity_view.dart | 184 +++++++++----------------- lib/widgets/session_card.dart | 6 +- 5 files changed, 93 insertions(+), 244 deletions(-) diff --git a/lib/classes/activity_action.dart b/lib/classes/activity_action.dart index 6fd7a5a..78d6f20 100644 --- a/lib/classes/activity_action.dart +++ b/lib/classes/activity_action.dart @@ -18,7 +18,7 @@ class ActivityAction { List>> items() { List>> sets = []; Reps reps = activityActionSet.reps; - int totalActions = 1; + int totalActions = 0; for (int i = 0; i < activityActionSet.total; i++) { List> actions = []; @@ -38,7 +38,7 @@ class ActivityAction { 'actionID': totalActions++, 'name': 'Rest', 'type': 'seconds', - 'amount': reps.amounts[i], + 'amount': reps.rest! ~/ 1000, }); } @@ -60,6 +60,13 @@ class ActivityAction { sets.add(actions); + // sets.add([{ + // 'actionID': totalActions++, + // 'name': 'Rest', + // 'type': 'seconds', + // 'amount': activityActionSet.rest ~/ 1000, + // }]); + // for (int j = 0; i < activityActionSet.reps.amounts; j++) {} } diff --git a/lib/models/activity_timer_model.dart b/lib/models/activity_timer_model.dart index e4e3cb0..2126a4d 100644 --- a/lib/models/activity_timer_model.dart +++ b/lib/models/activity_timer_model.dart @@ -30,10 +30,6 @@ class ActivityTimerModel with ChangeNotifier { setActionCount(); } - bool _isActive() { - return (_periodicTimer != null && _periodicTimer!.isActive) ? true : false; - } - bool isCurrentItem(int setNum, int actionNum) { if (setNum == _currentSetNum && actionNum == _currentActionNum) { return true; @@ -44,7 +40,7 @@ class ActivityTimerModel with ChangeNotifier { int totalActions() { int count = 0; - for(int i = 0; i < _sets.length; i++) { + for (int i = 0; i < _sets.length; i++) { count = count + _sets[i].length as int; } @@ -52,12 +48,14 @@ class ActivityTimerModel with ChangeNotifier { } void setActionCount() { - _actionCounter = _sets[_currentSetNum][_currentActionNum]['amount']; + _actionCounter = currentAction['amount']; } void start() { _periodicTimer = Timer.periodic(const Duration(seconds: 1), (Timer timer) { - switch(currentAction['type']) { + switch (currentAction['type']) { + // we don't want to count down + // if its repititions case 'repititions': break; case 'seconds': @@ -65,6 +63,7 @@ class ActivityTimerModel with ChangeNotifier { _actionCounter--; } else { nextAction(_currentActionNum + 1); + setActionCount(); } } @@ -78,113 +77,22 @@ class ActivityTimerModel with ChangeNotifier { notifyListeners(); } - void nextAction(int nextActionID) { - + void nextAction(int nextActionIndex) { + if (currentSet.length > nextActionIndex) { + setAction(_currentSetNum, nextActionIndex, 'automatic'); + } else if (_sets.length > _currentSetNum + 1) { + // if the item isn't in the set + // increment the set and reset action index + setAction(_currentSetNum + 1, 0, 'automatic'); + } else { + // if we're done all the sets + // cancel timer and reset activity + _periodicTimer!.cancel(); + setup(_activity!); + } } - (int, int) _findAction(int actionID) { - return (1, 2); + bool _isActive() { + return (_periodicTimer != null && _periodicTimer!.isActive) ? true : false; } - - // void nextAction(String type) { - // setAction(_currentActionNum + 1, _getSet(), type); - // } - - // int _actionCounter = 0; - // int _currentAction = 0; - // ActivityModel? _activity; - // Timer? _periodicTimer; - // List _actionMap = []; - - // int get actionCount => _actionCounter; - // int get currentAction => _currentAction; - // ActivityModel? get activity => _activity; - // Timer? get periodicTimer => _periodicTimer; - // String get actionType => _actionMap[_currentAction]; - // String get setType => _activity != null - // ? _activity!.actions[0].activityActionSet.reps.type - // : 'n/a'; - // String? get repType => actionState(); - // List get sets => _activity!.actions[0].items(); - - // void setup(ActivityModel activity) { - // // if there isn't an activity yet - // // or we're coming from another activity - // // setup new timer - // if (_activity == null || activity.id != _activity?.id) { - // _periodicTimer?.cancel(); - // _activity = activity; - // _currentAction = 0; - // _actionMap = []; - // // for now we just do alternating rest/sets - // // in the future, we'll make this more modifiable - // int totalActions = activity.actions[0].activityActionSet.total; - // // log(activity.actions[0].activityActionSet.type); - // // if (activity.actions[0].activityActionSet.type == 'alternating') { - // // totalActions = totalActions * 4; - // // log('were in $totalActions'); - // // } - - // for (int i = 0; i < totalActions; i++) { - // _actionMap.addAll(['Set', 'Rest']); - // } - // getValue(); - // } - // } - - // String actionState() { - // if (actionType == 'Set') { - // return setType == 'seconds' ? 'Seconds' : 'Repititions'; - // } - - // return 'Seconds'; - // } - - // void getValue() { - // if (_actionMap[_currentAction] == "Rest") { - // _actionCounter = _activity!.actions[0].activityActionSet.rest ~/ 1000; - // } else { - // _actionCounter = _activity!.actions[0].activityActionSet.reps.amounts[0]; - // } - // } - - // void pause() { - // _periodicTimer?.cancel(); - // notifyListeners(); - // } - - // void setAction(int actionNum, String type) { - // // always pause if we're manually taversing - // if (type == 'manual' && setType == 'seconds') { - // pause(); - // } - // _currentAction = actionNum; - // getValue(); - // notifyListeners(); - // } - - // void nextAction(String type) { - // _currentAction + 1 >= _actionMap.length - // ? pause() - // : setAction(_currentAction + 1, type); - // } - - // bool isActive() { - // return (_periodicTimer != null && _periodicTimer!.isActive) ? true : false; - // } - - // void start() { - // if (_activity != null) { - // _periodicTimer?.cancel(); - // _periodicTimer = - // Timer.periodic(const Duration(seconds: 1), (Timer timer) { - // if (_actionCounter <= 0) { - // nextAction('automatic'); - // } else if (actionState() != 'Repititions') { - // _actionCounter--; - // } - // notifyListeners(); - // }); - // } - // } } diff --git a/lib/widgets/activity_action_view.dart b/lib/widgets/activity_action_view.dart index 794b81a..3fef92b 100644 --- a/lib/widgets/activity_action_view.dart +++ b/lib/widgets/activity_action_view.dart @@ -44,7 +44,7 @@ class ActivityActionViewState extends State { ? Theme.of(context).colorScheme.primaryContainer : Theme.of(context).colorScheme.onPrimary, child: - Text(textAlign: TextAlign.center, 'Set ${setNum + 1} ')), + Text(textAlign: TextAlign.center, '${setNum + 1}.${actionNum + 1} ')), Expanded( child: Ink( padding: const EdgeInsets.all(15), diff --git a/lib/widgets/activity_view.dart b/lib/widgets/activity_view.dart index 2ed711c..8ad6664 100644 --- a/lib/widgets/activity_view.dart +++ b/lib/widgets/activity_view.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:provider/provider.dart'; import 'package:sendtrain/classes/activity_action.dart'; import 'package:sendtrain/classes/media.dart'; @@ -54,132 +55,65 @@ class _ActivityViewState extends State { style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), 'Actions:')), Padding( - padding: const EdgeInsets.only(left: 10, right: 10), - child: Card( - clipBehavior: Clip.antiAlias, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10)), - ), - color: Theme.of(context).colorScheme.onPrimary, - child: Padding( - padding: const EdgeInsets.only(left: 0, right: 0), - child: Row(children: [ - // LinearProgressIndicator( - // value: 0.5, - // minHeight: 100, - // color: Theme.of(context).colorScheme.error, - // semanticsLabel: 'Linear progress indicator', - // ), - // Expanded( - // flex: 1, - // child: Flex(direction: Axis.horizontal, children: [ - // Consumer(builder: (context, atm, child) { - // return IconButton( - // iconSize: 30, - // icon: atm.isActive() - // ? const Icon(Icons.pause_rounded) - // : const Icon(Icons.play_arrow_rounded), - // onPressed: () => - // {atm.isActive() ? atm.pause : atm.start()}); - // }), - // // IconButton( - // // // iconSize: 36, - // // icon: const Icon(Icons.skip_next_rounded), - // // onPressed: () { - // // atm.nextAction('manual'); - // // }) - // ])), - Ink( - width: 70, - color: Theme.of(context).colorScheme.primaryContainer, - child: Consumer( - builder: (context, atm, child) { - return IconButton( - alignment: AlignmentDirectional.center, - iconSize: 30, - icon: atm.isActive - ? const Icon(Icons.pause_rounded) - : const Icon(Icons.play_arrow_rounded), - onPressed: () => - {atm.isActive ? atm.pause : atm.start()}); - }, - )), - Expanded( - flex: 2, - child: Consumer( - builder: (context, atm, child) { - return Text( - style: const TextStyle(fontSize: 20), - textAlign: TextAlign.right, - '${atm.actionCount} ${atm.currentAction['type']}'); - }, - )), - Expanded( - flex: 1, - child: Padding( - padding: const EdgeInsets.only(right: 15), - child: Consumer( - builder: (context, atm, child) { - return Text( - style: const TextStyle(fontSize: 15), - textAlign: TextAlign.right, - '${atm.currentAction['actionID']} | ${atm.totalActions()}'); - // 'Set: ${atm.currentSet + 1}/${atm.totalSets}\nRep: ${atm.currentRep + 1}/${atm.totalReps}'); - }))), - ])))), + padding: const EdgeInsets.only(left: 10, right: 10), + child: Card( + clipBehavior: Clip.antiAlias, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10)), + ), + color: Theme.of(context).colorScheme.onPrimary, + child: Row(children: [ + // LinearProgressIndicator( + // value: 0.5, + // minHeight: 100, + // color: Theme.of(context).colorScheme.error, + // semanticsLabel: 'Linear progress indicator', + // ), + Ink( + width: 70, + color: Theme.of(context).colorScheme.primaryContainer, + child: Consumer( + builder: (context, atm, child) { + return IconButton( + alignment: AlignmentDirectional.center, + iconSize: 30, + icon: atm.isActive + ? const Icon(Icons.pause_rounded) + : const Icon(Icons.play_arrow_rounded), + onPressed: () => + {atm.isActive ? atm.pause : atm.start()}); + }, + )), + Expanded( + flex: 1, + child: Stack(alignment: Alignment.center, children: [ + Container( + alignment: Alignment.center, + child: Consumer( + builder: (context, atm, child) { + return Text( + style: const TextStyle(fontSize: 20), + textAlign: TextAlign.center, + '${atm.actionCount} ${atm.currentAction['type']}'); + }, + ), + ), + Container( + alignment: Alignment.centerRight, + padding: EdgeInsets.only(right: 10), + child: Consumer( + builder: (context, atm, child) { + return Text( + style: const TextStyle(fontSize: 15), + textAlign: TextAlign.right, + '${atm.currentAction['actionID'] + 1} | ${atm.totalActions()}'); + // 'Set: ${atm.currentSet + 1}/${atm.totalSets}\nRep: ${atm.currentRep + 1}/${atm.totalReps}'); + })), + ])), + ]))), ActivityActionView(action: activity.actions[0]), - // Container( - // height: MediaQuery.sizeOf(context).height * .07, - // color: Theme.of(context).colorScheme.primaryContainer, - // child: Row(children: [ - // // LinearProgressIndicator( - // // value: 0.5, - // // minHeight: 100, - // // color: Theme.of(context).colorScheme.error, - // // semanticsLabel: 'Linear progress indicator', - // // ), - // Expanded( - // flex: 1, - // child: Flex(direction: Axis.horizontal, children: [ - // Consumer(builder: (context, atm, child) { - // return IconButton( - // iconSize: 30, - // icon: atm.isActive() - // ? const Icon(Icons.pause_rounded) - // : const Icon(Icons.play_arrow_rounded), - // onPressed: () => - // {atm.isActive() ? atm.pause() : atm.start()}); - // }), - // IconButton( - // iconSize: 36, - // icon: const Icon(Icons.skip_next_rounded), - // onPressed: () { - // atm.nextAction('manual'); - // }) - // ])), - // Expanded( - // flex: 1, - // child: Consumer( - // builder: (context, atm, child) { - // return Text( - // style: const TextStyle(fontSize: 20), - // textAlign: TextAlign.center, - // '${atm.actionCount} ${atm.actionState()}'); - // }, - // )), - // Expanded( - // flex: 1, - // child: Padding( - // padding: const EdgeInsets.only(right: 15), - // child: Consumer( - // builder: (context, atm, child) { - // return Text( - // style: const TextStyle(fontSize: 20), - // textAlign: TextAlign.right, - // '${atm.currentAction + 1}: ${atm.actionType}'); - // // 'Set: ${atm.currentSet + 1}/${atm.totalSets}\nRep: ${atm.currentRep + 1}/${atm.totalReps}'); - // }))), - // ])) ]); } } diff --git a/lib/widgets/session_card.dart b/lib/widgets/session_card.dart index b11b3de..162b20d 100644 --- a/lib/widgets/session_card.dart +++ b/lib/widgets/session_card.dart @@ -131,13 +131,13 @@ class SessionCard extends StatelessWidget { activityActionSet: Set( type: 'alternating', total: 5, - rest: 300000, + rest: 5000, reps: Reps( type: 'seconds', tempo: [0], - amounts: [60, 60, 60, 60, 60], + amounts: [5, 5, 5, 5, 5], weights: [80, 80, 80, 80, 80], - rest: 0))), + rest: 5000))), ], resources: ['https://www.youtube.com/watch?v=dyAvbUvY_PU']), ], -- 2.47.2 From 4e5eeec93737e15e7d31d3c6f53b1787ace2356f Mon Sep 17 00:00:00 2001 From: Joshua Burman Date: Sat, 7 Dec 2024 16:25:04 -0500 Subject: [PATCH 10/11] progress indicator --- lib/models/activity_timer_model.dart | 14 ++++- lib/widgets/activity_view.dart | 92 ++++++++++++++-------------- 2 files changed, 59 insertions(+), 47 deletions(-) diff --git a/lib/models/activity_timer_model.dart b/lib/models/activity_timer_model.dart index 2126a4d..7fa82fd 100644 --- a/lib/models/activity_timer_model.dart +++ b/lib/models/activity_timer_model.dart @@ -10,6 +10,7 @@ class ActivityTimerModel with ChangeNotifier { int _currentActionNum = 0; int _currentSetNum = 0; Timer? _periodicTimer; + double _progress = 0; int get actionCount => _actionCounter; int get currentActionNum => _currentActionNum; @@ -20,7 +21,7 @@ class ActivityTimerModel with ChangeNotifier { List get sets => _sets; Timer? get periodicTimer => _periodicTimer; bool get isActive => _isActive(); - void get pause => _periodicTimer!.cancel(); + double get progress => _progress; void setup(ActivityModel activity) { _activity = activity; @@ -51,6 +52,11 @@ class ActivityTimerModel with ChangeNotifier { _actionCounter = currentAction['amount']; } + void pause() { + _periodicTimer!.cancel(); + notifyListeners(); + } + void start() { _periodicTimer = Timer.periodic(const Duration(seconds: 1), (Timer timer) { switch (currentAction['type']) { @@ -65,12 +71,18 @@ class ActivityTimerModel with ChangeNotifier { nextAction(_currentActionNum + 1); setActionCount(); } + updateProgress(); } notifyListeners(); }); } + void updateProgress() { + _progress = (currentAction['actionID'] + (1.0 - _actionCounter / currentAction['amount'])) / totalActions(); + notifyListeners(); + } + void setAction(int setNum, int actionNum, String type) { _currentActionNum = actionNum; _currentSetNum = setNum; diff --git a/lib/widgets/activity_view.dart b/lib/widgets/activity_view.dart index 8ad6664..40680ca 100644 --- a/lib/widgets/activity_view.dart +++ b/lib/widgets/activity_view.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:provider/provider.dart'; import 'package:sendtrain/classes/activity_action.dart'; import 'package:sendtrain/classes/media.dart'; @@ -53,7 +52,7 @@ class _ActivityViewState extends State { child: Text( textAlign: TextAlign.left, style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), - 'Actions:')), + 'Actions')), Padding( padding: const EdgeInsets.only(left: 10, right: 10), child: Card( @@ -65,54 +64,55 @@ class _ActivityViewState extends State { ), color: Theme.of(context).colorScheme.onPrimary, child: Row(children: [ - // LinearProgressIndicator( - // value: 0.5, - // minHeight: 100, - // color: Theme.of(context).colorScheme.error, - // semanticsLabel: 'Linear progress indicator', - // ), - Ink( - width: 70, - color: Theme.of(context).colorScheme.primaryContainer, + Ink( + width: 70, + color: Theme.of(context).colorScheme.primaryContainer, + child: Consumer( + builder: (context, atm, child) { + return IconButton( + alignment: AlignmentDirectional.center, + iconSize: 30, + icon: atm.isActive + ? const Icon(Icons.pause_rounded) + : const Icon(Icons.play_arrow_rounded), + onPressed: () => + {atm.isActive ? atm.pause() : atm.start()}); + }, + )), + Expanded( + flex: 1, + child: Stack(alignment: Alignment.center, children: [ + Container( + alignment: Alignment.center, child: Consumer( builder: (context, atm, child) { - return IconButton( - alignment: AlignmentDirectional.center, - iconSize: 30, - icon: atm.isActive - ? const Icon(Icons.pause_rounded) - : const Icon(Icons.play_arrow_rounded), - onPressed: () => - {atm.isActive ? atm.pause : atm.start()}); + return Text( + style: const TextStyle(fontSize: 20), + textAlign: TextAlign.center, + '${atm.actionCount} ${atm.currentAction['type']}'); }, - )), - Expanded( - flex: 1, - child: Stack(alignment: Alignment.center, children: [ - Container( - alignment: Alignment.center, - child: Consumer( + ), + ), + Container( + alignment: Alignment.centerRight, + padding: EdgeInsets.only(right: 10), + child: Consumer( builder: (context, atm, child) { - return Text( - style: const TextStyle(fontSize: 20), - textAlign: TextAlign.center, - '${atm.actionCount} ${atm.currentAction['type']}'); - }, - ), - ), - Container( - alignment: Alignment.centerRight, - padding: EdgeInsets.only(right: 10), - child: Consumer( - builder: (context, atm, child) { - return Text( - style: const TextStyle(fontSize: 15), - textAlign: TextAlign.right, - '${atm.currentAction['actionID'] + 1} | ${atm.totalActions()}'); - // 'Set: ${atm.currentSet + 1}/${atm.totalSets}\nRep: ${atm.currentRep + 1}/${atm.totalReps}'); - })), - ])), - ]))), + return Text( + style: const TextStyle(fontSize: 15), + textAlign: TextAlign.right, + '${atm.currentAction['actionID'] + 1} of ${atm.totalActions()}'); + })), + ])), + ]))), + Padding( + padding: EdgeInsets.only(left: 14, right: 14), + child: Consumer(builder: (context, atm, child) { + return LinearProgressIndicator( + value: atm.progress, + semanticsLabel: 'Activity Progress', + ); + })), ActivityActionView(action: activity.actions[0]), ]); } -- 2.47.2 From 586d2355c9348480f7efa69ab77c49ea640d8cb9 Mon Sep 17 00:00:00 2001 From: Joshua Burman Date: Sat, 7 Dec 2024 23:32:56 -0500 Subject: [PATCH 11/11] moving list view --- lib/models/activity_timer_model.dart | 43 +++++++++++++++++++++++---- lib/widgets/activity_action_view.dart | 33 ++++++++++++++++---- lib/widgets/activity_card.dart | 4 +-- lib/widgets/activity_view.dart | 5 +--- lib/widgets/session_card.dart | 4 +-- pubspec.yaml | 1 + 6 files changed, 71 insertions(+), 19 deletions(-) diff --git a/lib/models/activity_timer_model.dart b/lib/models/activity_timer_model.dart index 7fa82fd..46fbdee 100644 --- a/lib/models/activity_timer_model.dart +++ b/lib/models/activity_timer_model.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:sendtrain/models/activity_model.dart'; class ActivityTimerModel with ChangeNotifier { @@ -11,6 +12,7 @@ class ActivityTimerModel with ChangeNotifier { int _currentSetNum = 0; Timer? _periodicTimer; double _progress = 0; + ItemScrollController? _isc; int get actionCount => _actionCounter; int get currentActionNum => _currentActionNum; @@ -24,11 +26,31 @@ class ActivityTimerModel with ChangeNotifier { double get progress => _progress; void setup(ActivityModel activity) { - _activity = activity; - _sets = activity.actions[0].items(); + if (_activity == null || activity.id != _activity?.id) { + _periodicTimer?.cancel(); + _progress = 0; + _isc = null; + _activity = activity; + _sets = activity.actions[0].items(); + _currentActionNum = 0; + _currentSetNum = 0; + setActionCount(); + } + + moveToIndex(_currentSetNum); + } + + void reset() { + _progress = 0; _currentActionNum = 0; _currentSetNum = 0; + _periodicTimer!.cancel(); setActionCount(); + moveToIndex(0); + } + + void setScrollController(ItemScrollController isc) { + _isc = isc; } bool isCurrentItem(int setNum, int actionNum) { @@ -79,13 +101,16 @@ class ActivityTimerModel with ChangeNotifier { } void updateProgress() { - _progress = (currentAction['actionID'] + (1.0 - _actionCounter / currentAction['amount'])) / totalActions(); + _progress = (currentAction['actionID'] + + (1.0 - _actionCounter / currentAction['amount'])) / + totalActions(); notifyListeners(); } void setAction(int setNum, int actionNum, String type) { _currentActionNum = actionNum; _currentSetNum = setNum; + moveToIndex(_currentSetNum); notifyListeners(); } @@ -99,8 +124,16 @@ class ActivityTimerModel with ChangeNotifier { } else { // if we're done all the sets // cancel timer and reset activity - _periodicTimer!.cancel(); - setup(_activity!); + reset(); + } + } + + void moveToIndex(int index) { + if (_isc != null && _isc!.isAttached) { + _isc?.scrollTo( + index: index, + duration: Duration(milliseconds: 500), + curve: Curves.easeInOutCubic); } } diff --git a/lib/widgets/activity_action_view.dart b/lib/widgets/activity_action_view.dart index 3fef92b..2d88665 100644 --- a/lib/widgets/activity_action_view.dart +++ b/lib/widgets/activity_action_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:sendtrain/classes/activity_action.dart'; import 'package:sendtrain/models/activity_timer_model.dart'; @@ -12,22 +13,36 @@ class ActivityActionView extends StatefulWidget { } class ActivityActionViewState extends State { + final ItemScrollController itemScrollController = ItemScrollController(); + final ScrollOffsetController scrollOffsetController = + ScrollOffsetController(); + final ItemPositionsListener itemPositionsListener = + ItemPositionsListener.create(); + final ScrollOffsetListener scrollOffsetListener = + ScrollOffsetListener.create(); + @override Widget build(BuildContext context) { ActivityTimerModel atm = Provider.of(context, listen: true); List>> sets = atm.activity!.actions[0].items(); + // we need to set the scroll controller + // so we can update the selected item position + atm.setScrollController(itemScrollController); + return Expanded( - child: ListView.builder( + child: ScrollablePositionedList.builder( padding: const EdgeInsets.fromLTRB(10, 0, 10, 20), itemCount: widget.action.activityActionSet.total, + itemScrollController: itemScrollController, + scrollOffsetController: scrollOffsetController, + itemPositionsListener: itemPositionsListener, + scrollOffsetListener: scrollOffsetListener, itemBuilder: (BuildContext context, int setNum) { List content = []; - List> set = sets[setNum]; - // log('${sets.length}'); - // log('${set.length}'); + for (int actionNum = 0; actionNum < set.length; actionNum++) { Map setItem = set[actionNum]; @@ -35,6 +50,11 @@ class ActivityActionViewState extends State { onTap: () { atm.setAction(setNum, actionNum, 'manual'); atm.setActionCount(); + + itemScrollController.scrollTo( + index: setNum, + duration: Duration(milliseconds: 500), + curve: Curves.easeInOutCubic); }, child: Row(children: [ Ink( @@ -43,8 +63,9 @@ class ActivityActionViewState extends State { color: atm.isCurrentItem(setNum, actionNum) ? Theme.of(context).colorScheme.primaryContainer : Theme.of(context).colorScheme.onPrimary, - child: - Text(textAlign: TextAlign.center, '${setNum + 1}.${actionNum + 1} ')), + child: Text( + textAlign: TextAlign.center, + '${setNum + 1}.${actionNum + 1} ')), Expanded( child: Ink( padding: const EdgeInsets.all(15), diff --git a/lib/widgets/activity_card.dart b/lib/widgets/activity_card.dart index 52783fc..af38eba 100644 --- a/lib/widgets/activity_card.dart +++ b/lib/widgets/activity_card.dart @@ -64,9 +64,9 @@ class ActivityCard extends StatelessWidget { } ImageProvider findMediaByType(List? media, String type) { - var found = media!.where((m) => m.type == 'image'); + var found = media?.where((m) => m.type == 'image'); - if (found.isNotEmpty) { + if (found != null) { return NetworkImage(found.first.reference); } else { // Element is not found diff --git a/lib/widgets/activity_view.dart b/lib/widgets/activity_view.dart index 40680ca..72820b1 100644 --- a/lib/widgets/activity_view.dart +++ b/lib/widgets/activity_view.dart @@ -26,8 +26,6 @@ class _ActivityViewState extends State { return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ AppBar( - // surfaceTintColor: ThemeData.dark(useMaterial3: true).colorScheme.primary, - // backgroundColor: ThemeData.dark(useMaterial3: true).colorScheme.primaryContainer, centerTitle: true, title: const Text('Activity', style: TextStyle(fontSize: 15)), ), @@ -71,7 +69,6 @@ class _ActivityViewState extends State { builder: (context, atm, child) { return IconButton( alignment: AlignmentDirectional.center, - iconSize: 30, icon: atm.isActive ? const Icon(Icons.pause_rounded) : const Icon(Icons.play_arrow_rounded), @@ -99,7 +96,7 @@ class _ActivityViewState extends State { child: Consumer( builder: (context, atm, child) { return Text( - style: const TextStyle(fontSize: 15), + style: const TextStyle(fontSize: 12), textAlign: TextAlign.right, '${atm.currentAction['actionID'] + 1} of ${atm.totalActions()}'); })), diff --git a/lib/widgets/session_card.dart b/lib/widgets/session_card.dart index 162b20d..fd030dd 100644 --- a/lib/widgets/session_card.dart +++ b/lib/widgets/session_card.dart @@ -72,7 +72,7 @@ class SessionCard extends StatelessWidget { type: 'fundamental', categories: ['technique', 'conditioning'], description: - "Session focussed on attempting a climb at or beyond your perceived limit.", + "Session focused on attempting a climb at or beyond your perceived limit.", actions: [ ActivityAction( id: 1, @@ -109,7 +109,7 @@ class SessionCard extends StatelessWidget { type: 'Hypertrophy', categories: ['Strength', 'Power'], description: - "Block pull on a edge of a specific size and time to induce a hypotrophic effect on the formarms.", + "Block pull on a edge of a specific size and time to induce a hypertrophic effect on the formarms.", actions: [ ActivityAction( id: 1, diff --git a/pubspec.yaml b/pubspec.yaml index fe7c8e9..f1206c7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: youtube_player_flutter: ^9.1.1 json_annotation: ^4.9.0 provider: ^6.1.2 + scrollable_positioned_list: ^0.3.8 flutter_launcher_name: name: "SendTrain" -- 2.47.2