added sound during countdown, and upgraded minsdkversion
This commit is contained in:
@ -1,39 +1,305 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:drift/drift.dart' hide Column;
|
||||
import 'package:flutter/material.dart' hide Action;
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:sendtrain/daos/actions_dao.dart';
|
||||
import 'package:sendtrain/daos/activity_actions_dao.dart';
|
||||
import 'package:sendtrain/database/database.dart';
|
||||
import 'package:sendtrain/helpers/widget_helpers.dart';
|
||||
import 'package:sendtrain/widgets/generic/elements/form_drop_down.dart';
|
||||
import 'package:sendtrain/widgets/generic/elements/form_text_input.dart';
|
||||
|
||||
class ActivityActionEditor extends StatelessWidget {
|
||||
ActivityActionEditor({super.key, required this.action, this.callback});
|
||||
class ActivityActionEditor extends StatefulWidget {
|
||||
const ActivityActionEditor(
|
||||
{super.key,
|
||||
required this.session,
|
||||
required this.activity,
|
||||
this.action,
|
||||
this.callback});
|
||||
|
||||
final Action action;
|
||||
final Session session;
|
||||
final Activity activity;
|
||||
final Action? action;
|
||||
final Function? callback;
|
||||
|
||||
@override
|
||||
State<ActivityActionEditor> createState() => _ActivityActionEditorState();
|
||||
}
|
||||
|
||||
class _ActivityActionEditorState extends State<ActivityActionEditor> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
|
||||
final Map<String, TextEditingController> actionEditController = {
|
||||
'sets': TextEditingController(),
|
||||
'reps': TextEditingController(),
|
||||
'weight': TextEditingController(),
|
||||
'repLength': TextEditingController(),
|
||||
'preparation': TextEditingController(),
|
||||
'setRest': TextEditingController(),
|
||||
'repRest': TextEditingController(),
|
||||
'cooldown': TextEditingController(),
|
||||
'type': TextEditingController(),
|
||||
'alternating': TextEditingController(),
|
||||
};
|
||||
|
||||
late final AppDatabase db;
|
||||
|
||||
bool isAlternating = false;
|
||||
bool isTimed = false;
|
||||
String editorType = 'Create';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
db = Provider.of<AppDatabase>(context, listen: false);
|
||||
|
||||
// if we're editing a session, we'll want to populate it with the appropriate values
|
||||
if (widget.action != null) {
|
||||
final Action action = widget.action!;
|
||||
editorType = 'Edit';
|
||||
isAlternating = action.isAlternating;
|
||||
isTimed = action.repType == RepType.time ? true : false;
|
||||
|
||||
actionEditController['sets']?.text = action.totalSets.toString();
|
||||
actionEditController['reps']?.text =
|
||||
json.decode(action.totalReps)[0].toString();
|
||||
actionEditController['weight']?.text =
|
||||
json.decode(action.repWeights ?? "")[0].toString();
|
||||
actionEditController['repLength']?.text =
|
||||
((action.repLength ?? 0) ~/ 1000).toString();
|
||||
actionEditController['preparation']?.text =
|
||||
((action.restBeforeSets ?? 0) ~/ 1000).toString();
|
||||
actionEditController['setRest']?.text =
|
||||
((action.restBetweenSets ?? 0) ~/ 1000).toString();
|
||||
actionEditController['repRest']?.text =
|
||||
((action.restBetweenReps ?? 0) ~/ 1000).toString();
|
||||
actionEditController['cooldown']?.text =
|
||||
((action.restAfterSets ?? 0) ~/ 1000).toString();
|
||||
actionEditController['isTimed']?.text = isTimed.toString();
|
||||
actionEditController['alternating']?.text = isAlternating.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String editorType = 'Create';
|
||||
if (widget.action != null) {
|
||||
editorType = 'Edit';
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(15, 0, 15, 15),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 10, bottom: 10),
|
||||
child: Text('$editorType Action',
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleLarge)),
|
||||
FormTextInput(
|
||||
controller: actionEditController['sets']!,
|
||||
title: 'Total Sets'),])));
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 10, bottom: 10),
|
||||
child: Text('$editorType Action',
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleLarge)),
|
||||
Row(children: [
|
||||
formItemWrapper(
|
||||
CheckboxListTile(
|
||||
title: Text("Reps alternate? (eg. Left/Right Hand)"),
|
||||
value: isAlternating,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(10.0)),
|
||||
),
|
||||
onChanged: (bool? value) {
|
||||
setState(() {
|
||||
isAlternating = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
EdgeInsets.fromLTRB(10, 10, 10, 10)),
|
||||
]),
|
||||
Row(children: [
|
||||
formItemWrapper(
|
||||
CheckboxListTile(
|
||||
title: Text("Are reps timed?"),
|
||||
value: isTimed,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(10.0)),
|
||||
),
|
||||
onChanged: (bool? value) {
|
||||
setState(() {
|
||||
isTimed = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
EdgeInsets.fromLTRB(10, 10, 10, 15))
|
||||
]),
|
||||
Row(children: [
|
||||
FormDropDown(
|
||||
title: 'Sets',
|
||||
entries: numericDropDownItems('Set', 50),
|
||||
controller: actionEditController['sets']!),
|
||||
FormDropDown(
|
||||
title: 'Reps',
|
||||
entries: numericDropDownItems('Rep', 100),
|
||||
controller: actionEditController['reps']!,
|
||||
)
|
||||
]),
|
||||
Row(children: [
|
||||
formItemWrapper(
|
||||
FormTextInput(
|
||||
type: InputTypes.number,
|
||||
controller: actionEditController['preparation']!,
|
||||
title: 'Preparation (sec)',
|
||||
hint: 'time before start',
|
||||
requiresValidation: false),
|
||||
EdgeInsets.fromLTRB(10, 5, 10, 0)),
|
||||
formItemWrapper(
|
||||
FormTextInput(
|
||||
type: InputTypes.number,
|
||||
controller: actionEditController['cooldown']!,
|
||||
title: 'Cooldown (sec)',
|
||||
hint: 'rest after completion',
|
||||
requiresValidation: false),
|
||||
EdgeInsets.fromLTRB(10, 5, 10, 0)),
|
||||
]),
|
||||
Row(children: [
|
||||
formItemWrapper(
|
||||
FormTextInput(
|
||||
type: InputTypes.number,
|
||||
controller: actionEditController['setRest']!,
|
||||
title: 'Set Rest (sec)',
|
||||
hint: 'Rest between sets',
|
||||
requiresValidation: false),
|
||||
EdgeInsets.only(left: 10, right: 10)),
|
||||
formItemWrapper(
|
||||
FormTextInput(
|
||||
type: InputTypes.number,
|
||||
controller: actionEditController['repRest']!,
|
||||
title: 'Rep Rest (sec)',
|
||||
hint: 'Rest between reps',
|
||||
requiresValidation: false),
|
||||
EdgeInsets.only(left: 10, right: 10)),
|
||||
]),
|
||||
Row(children: [
|
||||
formItemWrapper(
|
||||
FormTextInput(
|
||||
type: InputTypes.number,
|
||||
controller: actionEditController['repLength']!,
|
||||
title: 'Rep Length (sec)',
|
||||
hint: 'Total rep time (not required)',
|
||||
requiresValidation: false),
|
||||
EdgeInsets.only(left: 10, right: 10)),
|
||||
formItemWrapper(
|
||||
FormTextInput(
|
||||
type: InputTypes.number,
|
||||
controller: actionEditController['weight']!,
|
||||
title: 'Weight',
|
||||
hint: 'Weight for reps',
|
||||
requiresValidation: false),
|
||||
EdgeInsets.only(left: 10, right: 10)),
|
||||
]),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.end, children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 10, right: 10),
|
||||
child: FilledButton(
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
if (widget.action != null) {
|
||||
Action newAction = widget.action!.copyWith(
|
||||
totalSets: int.parse(
|
||||
actionEditController['sets']!.text),
|
||||
totalReps: json.encode([
|
||||
int.parse(
|
||||
actionEditController['reps']!.text)
|
||||
]),
|
||||
repLength: Value<int>(int.parse(
|
||||
actionEditController['repLength']!
|
||||
.text) *
|
||||
1000),
|
||||
restBeforeSets: Value<int>(int.parse(
|
||||
actionEditController['preparation']!
|
||||
.text) *
|
||||
1000),
|
||||
restBetweenSets: Value<int>(int.parse(
|
||||
actionEditController['setRest']!
|
||||
.text) *
|
||||
1000),
|
||||
restBetweenReps: Value<int>(int.parse(
|
||||
actionEditController['repRest']!
|
||||
.text) *
|
||||
1000),
|
||||
restAfterSets: Value<int>(int.parse(
|
||||
actionEditController['cooldown']!
|
||||
.text) *
|
||||
1000),
|
||||
repType: int.parse(actionEditController[
|
||||
'repLength']!
|
||||
.text) >
|
||||
0
|
||||
? RepType.time
|
||||
: RepType.count,
|
||||
repWeights: Value<String>(json.encode([
|
||||
int.parse(
|
||||
actionEditController['weight']!.text)
|
||||
])),
|
||||
// setWeights: Value<String>(json.encode([actionEditController['setWeights']!.text])),
|
||||
isAlternating: isAlternating,
|
||||
);
|
||||
|
||||
// var result = await ActionsDao(db).createOrUpdate(
|
||||
// newAction.toCompanion(true));
|
||||
await ActionsDao(db).replace(newAction);
|
||||
} else {
|
||||
// create action
|
||||
await ActionsDao(db)
|
||||
.createOrUpdate(ActionsCompanion(
|
||||
title: Value('title'),
|
||||
description: Value('description'),
|
||||
totalSets: Value(int.parse(
|
||||
actionEditController['sets']!
|
||||
.text)),
|
||||
totalReps: Value(json.encode(
|
||||
[int.parse(actionEditController['reps']!.text)])),
|
||||
repLength: Value<int>(
|
||||
int.parse(actionEditController['repLength']!.text) *
|
||||
1000),
|
||||
restBeforeSets: Value<int>(
|
||||
int.parse(actionEditController['preparation']!.text) *
|
||||
1000),
|
||||
restBetweenSets: Value<int>(
|
||||
int.parse(actionEditController['setRest']!.text) *
|
||||
1000),
|
||||
restBetweenReps:
|
||||
Value<int>(int.parse(actionEditController['repRest']!.text) * 1000),
|
||||
restAfterSets: Value<int>(int.parse(actionEditController['cooldown']!.text) * 1000),
|
||||
repType: Value(int.parse(actionEditController['repLength']!.text) > 0 ? RepType.time : RepType.count),
|
||||
repWeights: Value<String>(json.encode([int.parse(actionEditController['weight']!.text)])),
|
||||
// setWeights: Value<String>(json.encode([actionEditController['setWeights']!.text])),
|
||||
isAlternating: Value<bool>(isAlternating),
|
||||
// repType: RepType.values.firstWhere((e) => e.toString() == "RepType.${actionEditController['repType']!.text}"),
|
||||
set: Value("")))
|
||||
.then((actionId) {
|
||||
ActivityActionsDao(db).createOrUpdate(
|
||||
ActivityActionsCompanion(
|
||||
activityId:
|
||||
Value(widget.activity.id),
|
||||
sessionId: Value(widget.session.id),
|
||||
actionId: Value(actionId),
|
||||
position: Value(0)));
|
||||
});
|
||||
}
|
||||
|
||||
Navigator.pop(
|
||||
_formKey.currentContext!, 'Submit');
|
||||
|
||||
if (widget.callback != null) {
|
||||
await widget.callback!();
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text('Submit')))
|
||||
])
|
||||
])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,20 +3,32 @@ import 'package:provider/provider.dart';
|
||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
import 'package:sendtrain/database/database.dart';
|
||||
import 'package:sendtrain/extensions/string_extensions.dart';
|
||||
import 'package:sendtrain/helpers/widget_helpers.dart';
|
||||
import 'package:sendtrain/models/action_model.dart';
|
||||
import 'package:sendtrain/providers/action_timer.dart';
|
||||
import 'package:sendtrain/widgets/activities/activity_action_editor.dart';
|
||||
import 'package:sendtrain/widgets/generic/elements/add_card_generic.dart';
|
||||
|
||||
class ActivityActionView extends StatefulWidget {
|
||||
const ActivityActionView({super.key, required this.actions, this.resetOnLoad = true});
|
||||
// class ActivityActionView extends StatefulWidget {
|
||||
class ActivityActionView extends StatelessWidget {
|
||||
ActivityActionView(
|
||||
{super.key,
|
||||
required this.session,
|
||||
required this.activity,
|
||||
required this.actions,
|
||||
this.callback,
|
||||
this.resetOnLoad = true});
|
||||
final Session session;
|
||||
final Activity activity;
|
||||
final List actions;
|
||||
final Function? callback;
|
||||
final bool resetOnLoad;
|
||||
|
||||
@override
|
||||
State<ActivityActionView> createState() => ActivityActionViewState();
|
||||
}
|
||||
// @override
|
||||
// State<ActivityActionView> createState() => ActivityActionViewState();
|
||||
// }
|
||||
|
||||
class ActivityActionViewState extends State<ActivityActionView> {
|
||||
// class ActivityActionViewState extends State<ActivityActionView> {
|
||||
// class ActivityActionView extends StatelessWidget {
|
||||
// ActivityActionView({super.key, required this.actions});
|
||||
|
||||
@ -30,7 +42,7 @@ class ActivityActionViewState extends State<ActivityActionView> {
|
||||
ScrollOffsetListener.create();
|
||||
|
||||
late final ActionTimer at;
|
||||
int actionCount = 0;
|
||||
// int actionCount = 0;
|
||||
|
||||
GestureDetector gtBuild(
|
||||
ActionTimer at, Item item, int actionNum, int selectedIndex,
|
||||
@ -68,17 +80,23 @@ class ActivityActionViewState extends State<ActivityActionView> {
|
||||
}));
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
at = Provider.of<ActionTimer>(context, listen: false);
|
||||
}
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// at = Provider.of<ActionTimer>(context, listen: false);
|
||||
// }
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.actions.isNotEmpty) {
|
||||
at.setup(ActionModel(
|
||||
action: widget.actions.first, db: Provider.of<AppDatabase>(context)), itemScrollController, widget.resetOnLoad);
|
||||
at = Provider.of<ActionTimer>(context, listen: false);
|
||||
int actionCount = 0;
|
||||
if (actions.isNotEmpty) {
|
||||
at.setup(
|
||||
ActionModel(
|
||||
action: actions.first,
|
||||
db: Provider.of<AppDatabase>(context)),
|
||||
itemScrollController,
|
||||
resetOnLoad);
|
||||
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// if (itemScrollController.isAttached) {
|
||||
@ -117,7 +135,7 @@ class ActivityActionViewState extends State<ActivityActionView> {
|
||||
onPressed: () => {
|
||||
if (at.started)
|
||||
{at.pause()}
|
||||
else if (at.available)
|
||||
else if (at.available || at.complete)
|
||||
{at.start()}
|
||||
});
|
||||
},
|
||||
@ -220,7 +238,12 @@ class ActivityActionViewState extends State<ActivityActionView> {
|
||||
description:
|
||||
'Click here to create an exercise template (sets and reps, etc) for your activity!',
|
||||
action: () {
|
||||
print('teset');
|
||||
showEditorSheet(
|
||||
context,
|
||||
ActivityActionEditor(
|
||||
session: session,
|
||||
activity: activity,
|
||||
callback: callback));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ class ActivityCardState extends State<ActivityCard> {
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: InkWell(
|
||||
onTap: () => showGenericDialog(
|
||||
ActivityView(activity: widget.activity), context),
|
||||
ActivityView(session: widget.session, activity: widget.activity), context),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
|
@ -7,6 +7,7 @@ import 'package:sendtrain/daos/actions_dao.dart';
|
||||
import 'package:sendtrain/database/database.dart';
|
||||
import 'package:sendtrain/extensions/string_extensions.dart';
|
||||
import 'package:sendtrain/helpers/widget_helpers.dart';
|
||||
import 'package:sendtrain/providers/action_timer.dart';
|
||||
import 'package:sendtrain/widgets/activities/activity_action_editor.dart';
|
||||
import 'package:sendtrain/widgets/activities/activity_action_view.dart';
|
||||
import 'package:sendtrain/widgets/activities/activity_view_categories.dart';
|
||||
@ -14,7 +15,9 @@ import 'package:sendtrain/widgets/activities/activity_view_media.dart';
|
||||
import 'package:sendtrain/widgets/builders/dialogs.dart';
|
||||
|
||||
class ActivityView extends StatefulWidget {
|
||||
const ActivityView({super.key, required this.activity});
|
||||
const ActivityView(
|
||||
{super.key, required this.session, required this.activity});
|
||||
final Session session;
|
||||
final Activity activity;
|
||||
|
||||
@override
|
||||
@ -22,6 +25,17 @@ class ActivityView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ActivityViewState extends State<ActivityView> {
|
||||
final _fabKey = GlobalKey<ExpandableFabState>();
|
||||
|
||||
void resetState() async {
|
||||
final state = _fabKey.currentState;
|
||||
if (state != null && state.isOpen) {
|
||||
state.toggle();
|
||||
}
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
List<ActivityMuscle> activityMuscle(Activity activity) {
|
||||
List<ActivityMuscle> muscles = [];
|
||||
|
||||
@ -39,10 +53,11 @@ class _ActivityViewState extends State<ActivityView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Activity activity = widget.activity;
|
||||
final Session session = widget.session;
|
||||
|
||||
return FutureBuilder<List>(
|
||||
future: ActionsDao(Provider.of<AppDatabase>(context))
|
||||
.fromActivity(activity),
|
||||
.fromActivity(activity, session),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
List<Action> actions = snapshot.data! as List<Action>;
|
||||
@ -61,12 +76,19 @@ class _ActivityViewState extends State<ActivityView> {
|
||||
child: Scaffold(
|
||||
floatingActionButtonLocation: ExpandableFab.location,
|
||||
floatingActionButton: ExpandableFab(
|
||||
key: _fabKey,
|
||||
distance: 70,
|
||||
type: ExpandableFabType.up,
|
||||
overlayStyle: ExpandableFabOverlayStyle(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
blur: 10,
|
||||
),
|
||||
onOpen: () {
|
||||
// pause the activity on open
|
||||
ActionTimer at =
|
||||
Provider.of<ActionTimer>(context, listen: false);
|
||||
if (at.started) at.pause();
|
||||
},
|
||||
children: [
|
||||
// FloatingActionButton.extended(
|
||||
// icon: const Icon(Icons.upload_outlined),
|
||||
@ -80,7 +102,10 @@ class _ActivityViewState extends State<ActivityView> {
|
||||
showEditorSheet(
|
||||
context,
|
||||
ActivityActionEditor(
|
||||
action: actions.first, callback: () {}));
|
||||
session: session,
|
||||
activity: activity,
|
||||
action: actions.first,
|
||||
callback: resetState));
|
||||
},
|
||||
),
|
||||
FloatingActionButton.extended(
|
||||
@ -228,7 +253,10 @@ class _ActivityViewState extends State<ActivityView> {
|
||||
context,
|
||||
Column(children: [
|
||||
ActivityActionView(
|
||||
session: session,
|
||||
activity: activity,
|
||||
actions: actions,
|
||||
callback: resetState,
|
||||
resetOnLoad: false)
|
||||
]),
|
||||
Theme.of(context).colorScheme.surface);
|
||||
@ -237,7 +265,11 @@ class _ActivityViewState extends State<ActivityView> {
|
||||
alignment: Alignment.bottomCenter,
|
||||
)
|
||||
])),
|
||||
ActivityActionView(actions: actions)
|
||||
ActivityActionView(
|
||||
session: session,
|
||||
activity: activity,
|
||||
actions: actions,
|
||||
callback: resetState)
|
||||
])));
|
||||
// ] +
|
||||
// action(actions, context)));
|
||||
|
Reference in New Issue
Block a user