import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:sendtrain/database/database.dart'; import 'package:sendtrain/models/action_model.dart'; class ActionTimer with ChangeNotifier { ActionModel? actionModel; double _progress = 0; int _currentTime = 0; final List<ItemScrollController> _scrollControllers = []; ActionTimer(); Map get state => actionModel?.state ?? _stateConstructor(); ActionStatus get status => actionModel?.status ?? ActionStatus.pending; bool get started => status == ActionStatus.started; bool get paused => status == ActionStatus.paused; bool get pending => status == ActionStatus.pending; bool get complete => status == ActionStatus.complete; bool get available => paused | pending; List<Set> get sets => actionModel!.sets; List<Item> get items => actionModel!.items; Set get currentSet => sets[state['currentSet']]; Reps get currentRep => currentSet.reps[state['currentRep']]; Item get currentAction => allActions[state['currentAction']]; int get currentTime => _currentTime; dynamic get currentValue => currentAction.valueType == RepType.time ? currentTime : currentAction.value; List<Item> get allActions => actionModel?.allItems ?? []; String get repType => actionModel!.action.repType == RepType.time ? 'Seconds' : 'Reps'; int? get repLength => currentRep.value; int? get repCount => currentRep.count; dynamic get repValue => actionModel!.action.repType == RepType.time ? repLength : repCount; double get progress => _progress; int get totalTime => actionModel!.totalTime; Timer? _periodicTimer; Map _stateConstructor() { return { 'currentSet': 0, 'currentRep': 0, 'currentTime': 0, 'currentAction': 0 }; } void setup(ActionModel actionModel, ItemScrollController scrollController, [bool resetOnLoad = true]) { if (resetOnLoad) { if (this.actionModel == actionModel) { reset(); } this.actionModel = actionModel; setAction(currentAction.id); } _scrollControllers.add(scrollController); } Future pause() async => await actionModel?.updateStatus(ActionStatus.paused).whenComplete(() { _periodicTimer?.cancel(); notifyListeners(); }); Future start() async { await actionModel!.updateStatus(ActionStatus.started); // start timer if (_periodicTimer == null || _periodicTimer!.isActive == false) { _periodicTimer = Timer.periodic(const Duration(seconds: 1), (Timer timer) async { switch (currentAction.valueType) { case RepType.count: break; case RepType.time: _currentTime--; if (_currentTime == 0) { // move to next action await setAction(state['currentAction'] + 1); } await updateProgress(); notifyListeners(); } }); } notifyListeners(); } Future close() async => await actionModel!.updateStatus(ActionStatus.complete).whenComplete(() { _periodicTimer!.cancel(); notifyListeners(); }); Future reset() async { await actionModel!.updateStatus(ActionStatus.pending); await actionModel!.updateState(json.encode(_stateConstructor())); _periodicTimer?.cancel(); _progress = 0; _scrollControllers.clear(); notifyListeners(); } Future clear() async { await reset(); } double timeUsed() { Iterable<Item> usedItems = allActions.getRange(0, state['currentAction']); return usedItems.fold(0.0, (p, c) => p + c.value!); } double totalComplete() { Iterable<Item> usedItems = allActions.getRange(0, state['currentAction']); return usedItems.length / allActions.length; } updateProgress() { double repUsed = (currentAction.value - currentTime) / currentAction.value; _progress = totalComplete() + ((repUsed < 0 ? 0 : repUsed) / allActions.length); notifyListeners(); } setAction(int actionNum, [bool isManual = false]) async { Item item = allActions[actionNum]; Map newState = state; newState['currentAction'] = actionNum; newState['currentSet'] = item.parentId; newState['currentRep'] = item.id; newState['currentTime'] = _currentTime = item.value!; await actionModel! .updateState(json.encode(newState)) .whenComplete(() async { if (isManual) { await pause(); await updateProgress(); } int index = currentAction.parentId != null ? currentAction.parentId! : currentAction.id; for (int i = 0; i < _scrollControllers.length; i++) { ItemScrollController sc = _scrollControllers[i]; sc.scrollTo( index: index, duration: Duration(milliseconds: 500), curve: Curves.easeInOutCubic); } // _scrollController?.scrollTo( // index: index, // duration: Duration(milliseconds: 500), // curve: Curves.easeInOutCubic); }); notifyListeners(); } }