activity counter #2
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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),
|
||||||
|
@ -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
|
||||||
|
@ -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()}');
|
||||||
})),
|
})),
|
||||||
|
@ -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,
|
||||||
|
@ -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"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user