added dash for local db, ui prep work

This commit is contained in:
Joshua Burman 2024-12-08 14:14:10 -05:00
parent 4094f7edba
commit d6e62024d7
6 changed files with 235 additions and 137 deletions

View File

@ -13,6 +13,7 @@ class ActivityTimerModel with ChangeNotifier {
Timer? _periodicTimer; Timer? _periodicTimer;
double _progress = 0; double _progress = 0;
ItemScrollController? _isc; ItemScrollController? _isc;
int _totalTime = 0;
int get actionCount => _actionCounter; int get actionCount => _actionCounter;
int get currentActionNum => _currentActionNum; int get currentActionNum => _currentActionNum;
@ -24,6 +25,7 @@ class ActivityTimerModel with ChangeNotifier {
Timer? get periodicTimer => _periodicTimer; Timer? get periodicTimer => _periodicTimer;
bool get isActive => _isActive(); bool get isActive => _isActive();
double get progress => _progress; double get progress => _progress;
int get totalTime => _totalTime;
void setup(ActivityModel activity) { void setup(ActivityModel activity) {
if (_activity == null || activity.id != _activity?.id) { if (_activity == null || activity.id != _activity?.id) {
@ -35,11 +37,26 @@ class ActivityTimerModel with ChangeNotifier {
_currentActionNum = 0; _currentActionNum = 0;
_currentSetNum = 0; _currentSetNum = 0;
setActionCount(); setActionCount();
getTotalTime();
} }
moveToIndex(_currentSetNum); moveToIndex(_currentSetNum);
} }
void getTotalTime() {
int time = 0;
for(int setIndex = 0; _sets.length > setIndex; setIndex++) {
for (int actionIndex = 0; _sets[setIndex].length > actionIndex; actionIndex++) {
var action = _sets[setIndex][actionIndex];
if (action['type'] == 'seconds') {
time = time + action['amount'] as int;
}
}
}
_totalTime = time;
}
void reset() { void reset() {
_progress = 0; _progress = 0;
_currentActionNum = 0; _currentActionNum = 0;
@ -89,6 +106,7 @@ class ActivityTimerModel with ChangeNotifier {
case 'seconds': case 'seconds':
if (_actionCounter > 0) { if (_actionCounter > 0) {
_actionCounter--; _actionCounter--;
_totalTime--;
} else { } else {
nextAction(_currentActionNum + 1); nextAction(_currentActionNum + 1);
setActionCount(); setActionCount();
@ -110,8 +128,8 @@ class ActivityTimerModel with ChangeNotifier {
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();
moveToIndex(_currentSetNum);
} }
void nextAction(int nextActionIndex) { void nextAction(int nextActionIndex) {

View File

@ -1,20 +1,39 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sendtrain/classes/media.dart'; import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/models/activity_model.dart'; import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/models/activity_timer_model.dart';
import 'package:sendtrain/widgets/activity_view.dart'; import 'package:sendtrain/widgets/activity_view.dart';
class ActivityCard extends StatelessWidget { class ActivityCard extends StatefulWidget {
final ActivityModel activity;
const ActivityCard({super.key, required this.activity}); const ActivityCard({super.key, required this.activity});
final ActivityModel activity; @override
State<ActivityCard> createState() => ActivityCardState();
}
class ActivityCardState extends State<ActivityCard> {
String formattedTime(int timeInSecond) {
int sec = timeInSecond % 60;
int min = (timeInSecond / 60).floor();
String minute = min.toString().length <= 1 ? "0$min" : "$min";
String second = sec.toString().length <= 1 ? "0$sec" : "$sec";
return "$minute:$second";
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ActivityTimerModel atm =
Provider.of<ActivityTimerModel>(context, listen: false);
return Card( return Card(
color: const Color(0xff3A5FB6), color: atm.activity?.id == widget.activity.id
? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).colorScheme.surfaceContainerLow,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
child: InkWell( child: InkWell(
splashColor: Colors.deepPurple,
onTap: () => showGeneralDialog( onTap: () => showGeneralDialog(
barrierColor: Colors.black.withOpacity(0.5), barrierColor: Colors.black.withOpacity(0.5),
transitionDuration: const Duration(milliseconds: 220), transitionDuration: const Duration(milliseconds: 220),
@ -29,7 +48,7 @@ class ActivityCard extends StatelessWidget {
return SlideTransition( return SlideTransition(
position: custom, position: custom,
child: Dialog.fullscreen( child: Dialog.fullscreen(
child: ActivityView(activity: activity))); child: ActivityView(activity: widget.activity)));
}, },
barrierDismissible: true, barrierDismissible: true,
barrierLabel: '', barrierLabel: '',
@ -49,14 +68,24 @@ class ActivityCard extends StatelessWidget {
image: DecorationImage( image: DecorationImage(
fit: BoxFit.cover, fit: BoxFit.cover,
image: findMediaByType( image: findMediaByType(
activity.actions[0].media, 'image')), widget.activity.actions[0].media, 'image')),
// color: Colors.blue, // color: Colors.blue,
borderRadius: borderRadius:
const BorderRadius.all(Radius.elliptical(10, 10)), const BorderRadius.all(Radius.elliptical(10, 10)),
), ),
)), )),
title: Text(maxLines: 1, activity.title), title: Consumer<ActivityTimerModel>(
subtitle: Text(maxLines: 2, activity.description), builder: (context, atm, child) {
if (atm.activity?.id == widget.activity.id) {
return Text(
maxLines: 1,
"${widget.activity.title} (${formattedTime(atm.totalTime)})");
} else {
return Text(maxLines: 1, widget.activity.title);
}
},
),
subtitle: Text(maxLines: 2, widget.activity.description),
), ),
], ],
)), )),

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:sendtrain/classes/activity_action.dart'; import 'package:sendtrain/classes/activity_action.dart';
import 'package:sendtrain/classes/media.dart'; import 'package:sendtrain/classes/media.dart';
@ -24,94 +25,122 @@ class _ActivityViewState extends State<ActivityView> {
atm.setup(activity); atm.setup(activity);
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ return Scaffold(
AppBar( floatingActionButtonLocation: ExpandableFab.location,
centerTitle: true, floatingActionButton: ExpandableFab(
title: const Text('Activity', style: TextStyle(fontSize: 15)), distance: 70,
), type: ExpandableFabType.up,
Padding( overlayStyle: ExpandableFabOverlayStyle(
padding: color: Colors.black.withOpacity(0.5),
const EdgeInsets.only(left: 15, right: 20, top: 15, bottom: 10), blur: 10,
child: Text( ),
maxLines: 1, children: [
style: const TextStyle(fontSize: 25, fontWeight: FontWeight.bold), FloatingActionButton.extended(
activity.title)), icon: const Icon(Icons.history_outlined),
ActivityViewCategories(categories: activity.categories), label: Text('Restart'),
Padding( onPressed: () {},
padding:
const EdgeInsets.only(top: 0, bottom: 20, left: 15, right: 15),
child: Text(
textAlign: TextAlign.left,
style: const TextStyle(fontSize: 15),
activity.description)),
ActivityViewMedia(activity: activity),
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
textAlign: TextAlign.left,
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, FloatingActionButton.extended(
child: Row(children: [ icon: const Icon(Icons.done_all_outlined),
Ink( label: Text('Done'),
width: 70, onPressed: () {},
color: Theme.of(context).colorScheme.primaryContainer, ),
child: Consumer<ActivityTimerModel>( FloatingActionButton.extended(
builder: (context, atm, child) { icon: const Icon(Icons.edit_outlined),
return IconButton( label: Text('Edit'),
alignment: AlignmentDirectional.center, onPressed: () {},
icon: atm.isActive ),
? const Icon(Icons.pause_rounded) ]),
: const Icon(Icons.play_arrow_rounded), body: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
onPressed: () => AppBar(
{atm.isActive ? atm.pause() : atm.start()}); centerTitle: true,
}, title: const Text('Activity', style: TextStyle(fontSize: 15)),
)), ),
Expanded( Padding(
flex: 1, padding: const EdgeInsets.only(
child: Stack(alignment: Alignment.center, children: [ left: 15, right: 20, top: 15, bottom: 10),
Container( child: Text(
alignment: Alignment.center, maxLines: 1,
style: const TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
activity.title)),
ActivityViewCategories(categories: activity.categories),
Padding(
padding: const EdgeInsets.only(
top: 0, bottom: 20, left: 15, right: 15),
child: Text(
textAlign: TextAlign.left,
style: const TextStyle(fontSize: 15),
activity.description)),
ActivityViewMedia(activity: activity),
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
textAlign: TextAlign.left,
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: Row(children: [
Ink(
width: 70,
color: Theme.of(context).colorScheme.primaryContainer,
child: Consumer<ActivityTimerModel>( child: Consumer<ActivityTimerModel>(
builder: (context, atm, child) { builder: (context, atm, child) {
return Text( return IconButton(
style: const TextStyle(fontSize: 20), alignment: AlignmentDirectional.center,
textAlign: TextAlign.center, icon: atm.isActive
'${atm.actionCount} ${atm.currentAction['type']}'); ? const Icon(Icons.pause_rounded)
: const Icon(Icons.play_arrow_rounded),
onPressed: () =>
{atm.isActive ? atm.pause() : atm.start()});
}, },
), )),
), Expanded(
Container( flex: 1,
alignment: Alignment.centerRight, child: Stack(alignment: Alignment.center, children: [
padding: EdgeInsets.only(right: 10), Container(
child: Consumer<ActivityTimerModel>( alignment: Alignment.center,
child: Consumer<ActivityTimerModel>(
builder: (context, atm, child) { builder: (context, atm, child) {
return Text( return Text(
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 20),
textAlign: TextAlign.right, textAlign: TextAlign.center,
'${atm.currentAction['actionID'] + 1} of ${atm.totalActions()}'); '${atm.actionCount} ${atm.currentAction['type']}');
})), },
])), ),
]))), ),
Padding( Container(
padding: EdgeInsets.only(left: 14, right: 14), alignment: Alignment.centerRight,
child: Consumer<ActivityTimerModel>(builder: (context, atm, child) { padding: EdgeInsets.only(right: 10),
return LinearProgressIndicator( child: Consumer<ActivityTimerModel>(
value: atm.progress, builder: (context, atm, child) {
semanticsLabel: 'Activity Progress', return Text(
); style: const TextStyle(fontSize: 12),
})), textAlign: TextAlign.right,
ActivityActionView(action: activity.actions[0]), '${atm.currentAction['actionID'] + 1} of ${atm.totalActions()}');
]); })),
])),
]))),
Padding(
padding: EdgeInsets.only(left: 14, right: 14),
child:
Consumer<ActivityTimerModel>(builder: (context, atm, child) {
return LinearProgressIndicator(
value: atm.progress,
semanticsLabel: 'Activity Progress',
);
})),
ActivityActionView(action: activity.actions[0]),
]));
} }
} }

View File

@ -18,8 +18,8 @@ class SessionCard extends StatelessWidget {
final DateFormat dateFormat = DateFormat('yyyy-MM-dd'); final DateFormat dateFormat = DateFormat('yyyy-MM-dd');
Color color = (state == 0) Color color = (state == 0)
? const Color(0xff3A5FB6) ? Theme.of(context).colorScheme.primaryContainer
: ThemeData.dark(useMaterial3: true).colorScheme.surfaceBright; : Theme.of(context).colorScheme.surfaceContainerLow;
// place holder until we can retrieve real data // place holder until we can retrieve real data
final data = SessionModel( final data = SessionModel(
@ -228,7 +228,7 @@ class SessionCard extends StatelessWidget {
); );
} else { } else {
return Card( return Card(
color: const Color.fromARGB(125, 0, 0, 0), color: color,
child: InkWell( child: InkWell(
// overlayColor: MaterialStateColor(Colors.deepPurple as int), // overlayColor: MaterialStateColor(Colors.deepPurple as int),
splashColor: Colors.deepPurple, splashColor: Colors.deepPurple,

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart'; import 'package:intl/date_symbol_data_local.dart';
@ -18,48 +19,67 @@ class SessionView extends StatelessWidget {
initializeDateFormatting('en'); initializeDateFormatting('en');
final DateFormat dateFormat = DateFormat('yyyy-MM-dd'); final DateFormat dateFormat = DateFormat('yyyy-MM-dd');
return Column( return Scaffold(
crossAxisAlignment: CrossAxisAlignment.start, floatingActionButtonLocation: ExpandableFab.location,
children: <Widget>[ floatingActionButton: ExpandableFab(
AppBar( distance: 70,
centerTitle: true, type: ExpandableFabType.up,
title: Text('Session @ ${dateFormat.format(data.date)}', overlayStyle: ExpandableFabOverlayStyle(
style: const TextStyle(fontSize: 15)), color: Colors.black.withOpacity(0.5),
), blur: 10,
Padding( ),
padding: children: [
const EdgeInsets.only(left: 15, right: 20, top: 15, bottom: 10), FloatingActionButton.extended(
child: Text( icon: const Icon(Icons.history_outlined),
maxLines: 1, label: Text('Restart'),
style: onPressed: () {},
const TextStyle(fontSize: 25, fontWeight: FontWeight.bold), ),
data.title)), FloatingActionButton.extended(
SessionViewAchievements(achievements: data.achievements), icon: const Icon(Icons.done_all_outlined),
Padding( label: Text('Done'),
padding: const EdgeInsets.only(left: 15, right: 15), onPressed: () {},
child: Text( ),
style: const TextStyle(fontSize: 15), FloatingActionButton.extended(
data.content)), icon: const Icon(Icons.edit_outlined),
const Padding( label: Text('Edit'),
padding: EdgeInsets.fromLTRB(15, 30, 0, 10), onPressed: () {},
child: Text( ),
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ]),
'Media:')), body: Column(
SessionViewMedia(media: data.media), crossAxisAlignment: CrossAxisAlignment.start,
const Padding( children: <Widget>[
padding: EdgeInsets.fromLTRB(15, 30, 0, 10), AppBar(
child: Text( centerTitle: true,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), title: Text('Session @ ${dateFormat.format(data.date)}',
'Activites:')), style: const TextStyle(fontSize: 15)),
SessionViewActivities(activities: data.activities), ),
// TextButton( Padding(
// onPressed: () { padding: const EdgeInsets.only(
// Navigator.pop(context); left: 15, right: 20, top: 15, bottom: 10),
// }, child: Text(
// child: const Text('Close'), maxLines: 1,
// ), style: const TextStyle(
], fontSize: 25, fontWeight: FontWeight.bold),
); data.title)),
SessionViewAchievements(achievements: data.achievements),
Padding(
padding: const EdgeInsets.only(left: 15, right: 15),
child:
Text(style: const TextStyle(fontSize: 15), data.content)),
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
'Media:')),
SessionViewMedia(media: data.media),
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
'Activites:')),
SessionViewActivities(activities: data.activities),
],
));
} }
} }

View File

@ -41,6 +41,8 @@ dependencies:
json_annotation: ^4.9.0 json_annotation: ^4.9.0
provider: ^6.1.2 provider: ^6.1.2
scrollable_positioned_list: ^0.3.8 scrollable_positioned_list: ^0.3.8
drift: ^2.22.1
flutter_expandable_fab: ^2.3.0
flutter_launcher_name: flutter_launcher_name:
name: "SendTrain" name: "SendTrain"