activity counter #2

Merged
jshbrmn merged 11 commits from activity-actions into main 2024-12-07 20:45:35 -08:00
6 changed files with 71 additions and 19 deletions
Showing only changes of commit 586d2355c9 - Show all commits

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:sendtrain/models/activity_model.dart'; import 'package:sendtrain/models/activity_model.dart';
class ActivityTimerModel with ChangeNotifier { class ActivityTimerModel with ChangeNotifier {
@ -11,6 +12,7 @@ class ActivityTimerModel with ChangeNotifier {
int _currentSetNum = 0; int _currentSetNum = 0;
Timer? _periodicTimer; Timer? _periodicTimer;
double _progress = 0; double _progress = 0;
ItemScrollController? _isc;
int get actionCount => _actionCounter; int get actionCount => _actionCounter;
int get currentActionNum => _currentActionNum; int get currentActionNum => _currentActionNum;
@ -24,11 +26,31 @@ class ActivityTimerModel with ChangeNotifier {
double get progress => _progress; double get progress => _progress;
void setup(ActivityModel activity) { void setup(ActivityModel activity) {
_activity = activity; if (_activity == null || activity.id != _activity?.id) {
_sets = activity.actions[0].items(); _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; _currentActionNum = 0;
_currentSetNum = 0; _currentSetNum = 0;
_periodicTimer!.cancel();
setActionCount(); setActionCount();
moveToIndex(0);
}
void setScrollController(ItemScrollController isc) {
_isc = isc;
} }
bool isCurrentItem(int setNum, int actionNum) { bool isCurrentItem(int setNum, int actionNum) {
@ -79,13 +101,16 @@ class ActivityTimerModel with ChangeNotifier {
} }
void updateProgress() { void updateProgress() {
_progress = (currentAction['actionID'] + (1.0 - _actionCounter / currentAction['amount'])) / totalActions(); _progress = (currentAction['actionID'] +
(1.0 - _actionCounter / currentAction['amount'])) /
totalActions();
notifyListeners(); notifyListeners();
} }
void setAction(int setNum, int actionNum, String type) { void setAction(int setNum, int actionNum, String type) {
_currentActionNum = actionNum; _currentActionNum = actionNum;
_currentSetNum = setNum; _currentSetNum = setNum;
moveToIndex(_currentSetNum);
notifyListeners(); notifyListeners();
} }
@ -99,8 +124,16 @@ class ActivityTimerModel with ChangeNotifier {
} else { } else {
// if we're done all the sets // if we're done all the sets
// cancel timer and reset activity // cancel timer and reset activity
_periodicTimer!.cancel(); reset();
setup(_activity!); }
}
void moveToIndex(int index) {
if (_isc != null && _isc!.isAttached) {
_isc?.scrollTo(
index: index,
duration: Duration(milliseconds: 500),
curve: Curves.easeInOutCubic);
} }
} }

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.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/classes/activity_action.dart';
import 'package:sendtrain/models/activity_timer_model.dart'; import 'package:sendtrain/models/activity_timer_model.dart';
@ -12,22 +13,36 @@ class ActivityActionView extends StatefulWidget {
} }
class ActivityActionViewState extends State<ActivityActionView> { class ActivityActionViewState extends State<ActivityActionView> {
final ItemScrollController itemScrollController = ItemScrollController();
final ScrollOffsetController scrollOffsetController =
ScrollOffsetController();
final ItemPositionsListener itemPositionsListener =
ItemPositionsListener.create();
final ScrollOffsetListener scrollOffsetListener =
ScrollOffsetListener.create();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
ActivityTimerModel atm = ActivityTimerModel atm =
Provider.of<ActivityTimerModel>(context, listen: true); Provider.of<ActivityTimerModel>(context, listen: true);
List<List<Map<String, dynamic>>> sets = atm.activity!.actions[0].items(); List<List<Map<String, dynamic>>> 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( return Expanded(
child: ListView.builder( child: ScrollablePositionedList.builder(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 20), padding: const EdgeInsets.fromLTRB(10, 0, 10, 20),
itemCount: widget.action.activityActionSet.total, itemCount: widget.action.activityActionSet.total,
itemScrollController: itemScrollController,
scrollOffsetController: scrollOffsetController,
itemPositionsListener: itemPositionsListener,
scrollOffsetListener: scrollOffsetListener,
itemBuilder: (BuildContext context, int setNum) { itemBuilder: (BuildContext context, int setNum) {
List<GestureDetector> content = []; List<GestureDetector> content = [];
List<Map<String, dynamic>> set = sets[setNum]; List<Map<String, dynamic>> set = sets[setNum];
// log('${sets.length}');
// log('${set.length}');
for (int actionNum = 0; actionNum < set.length; actionNum++) { for (int actionNum = 0; actionNum < set.length; actionNum++) {
Map<String, dynamic> setItem = set[actionNum]; Map<String, dynamic> setItem = set[actionNum];
@ -35,6 +50,11 @@ class ActivityActionViewState extends State<ActivityActionView> {
onTap: () { onTap: () {
atm.setAction(setNum, actionNum, 'manual'); atm.setAction(setNum, actionNum, 'manual');
atm.setActionCount(); atm.setActionCount();
itemScrollController.scrollTo(
index: setNum,
duration: Duration(milliseconds: 500),
curve: Curves.easeInOutCubic);
}, },
child: Row(children: [ child: Row(children: [
Ink( Ink(
@ -43,8 +63,9 @@ class ActivityActionViewState extends State<ActivityActionView> {
color: atm.isCurrentItem(setNum, actionNum) color: atm.isCurrentItem(setNum, actionNum)
? Theme.of(context).colorScheme.primaryContainer ? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).colorScheme.onPrimary, : Theme.of(context).colorScheme.onPrimary,
child: child: Text(
Text(textAlign: TextAlign.center, '${setNum + 1}.${actionNum + 1} ')), textAlign: TextAlign.center,
'${setNum + 1}.${actionNum + 1} ')),
Expanded( Expanded(
child: Ink( child: Ink(
padding: const EdgeInsets.all(15), padding: const EdgeInsets.all(15),

View File

@ -64,9 +64,9 @@ class ActivityCard extends StatelessWidget {
} }
ImageProvider findMediaByType(List<Media>? media, String type) { ImageProvider findMediaByType(List<Media>? 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); return NetworkImage(found.first.reference);
} else { } else {
// Element is not found // Element is not found

View File

@ -26,8 +26,6 @@ class _ActivityViewState extends State<ActivityView> {
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
AppBar( AppBar(
// surfaceTintColor: ThemeData.dark(useMaterial3: true).colorScheme.primary,
// backgroundColor: ThemeData.dark(useMaterial3: true).colorScheme.primaryContainer,
centerTitle: true, centerTitle: true,
title: const Text('Activity', style: TextStyle(fontSize: 15)), title: const Text('Activity', style: TextStyle(fontSize: 15)),
), ),
@ -71,7 +69,6 @@ class _ActivityViewState extends State<ActivityView> {
builder: (context, atm, child) { builder: (context, atm, child) {
return IconButton( return IconButton(
alignment: AlignmentDirectional.center, alignment: AlignmentDirectional.center,
iconSize: 30,
icon: atm.isActive icon: atm.isActive
? const Icon(Icons.pause_rounded) ? const Icon(Icons.pause_rounded)
: const Icon(Icons.play_arrow_rounded), : const Icon(Icons.play_arrow_rounded),
@ -99,7 +96,7 @@ class _ActivityViewState extends State<ActivityView> {
child: Consumer<ActivityTimerModel>( child: Consumer<ActivityTimerModel>(
builder: (context, atm, child) { builder: (context, atm, child) {
return Text( return Text(
style: const TextStyle(fontSize: 15), style: const TextStyle(fontSize: 12),
textAlign: TextAlign.right, textAlign: TextAlign.right,
'${atm.currentAction['actionID'] + 1} of ${atm.totalActions()}'); '${atm.currentAction['actionID'] + 1} of ${atm.totalActions()}');
})), })),

View File

@ -72,7 +72,7 @@ class SessionCard extends StatelessWidget {
type: 'fundamental', type: 'fundamental',
categories: ['technique', 'conditioning'], categories: ['technique', 'conditioning'],
description: 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: [ actions: [
ActivityAction( ActivityAction(
id: 1, id: 1,
@ -109,7 +109,7 @@ class SessionCard extends StatelessWidget {
type: 'Hypertrophy', type: 'Hypertrophy',
categories: ['Strength', 'Power'], categories: ['Strength', 'Power'],
description: 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: [ actions: [
ActivityAction( ActivityAction(
id: 1, id: 1,

View File

@ -40,6 +40,7 @@ dependencies:
youtube_player_flutter: ^9.1.1 youtube_player_flutter: ^9.1.1
json_annotation: ^4.9.0 json_annotation: ^4.9.0
provider: ^6.1.2 provider: ^6.1.2
scrollable_positioned_list: ^0.3.8
flutter_launcher_name: flutter_launcher_name:
name: "SendTrain" name: "SendTrain"