34 Commits

Author SHA1 Message Date
cfb7f39304 awesome-notifications poc 2024-12-10 21:39:42 -05:00
54d47245ae Merge pull request 'drift-impl' (#3) from drift-impl into main
Reviewed-on: #3
2024-12-10 00:19:53 -08:00
7f2cf0b49f more seed data and passing data along 2024-12-10 02:26:34 -05:00
8fe60e7ae3 officially getting data from the dbgit add . -p! 2024-12-10 02:10:02 -05:00
b2e2eb67b0 seeded db, updated some db fields 2024-12-09 00:06:44 -05:00
0dc7c3ced0 upgraded kotlin, gradle, added drift 2024-12-08 22:25:23 -05:00
13fe7e2ef4 more prep work for local data storage 2024-12-08 14:40:12 -05:00
d6e62024d7 added dash for local db, ui prep work 2024-12-08 14:14:10 -05:00
4094f7edba Merge pull request 'activity counter' (#2) from activity-actions into main
Reviewed-on: #2
2024-12-07 20:45:35 -08:00
586d2355c9 moving list view 2024-12-07 23:32:56 -05:00
4e5eeec937 progress indicator 2024-12-07 16:25:04 -05:00
0c0f596fbb reset timer and activity when complete 2024-12-07 15:25:03 -05:00
f781001d3b upgraded dart and packages, analyze fixes, action type timer management 2024-12-07 13:13:45 -05:00
19f835d8f2 rework timer and how we manage actions 2024-12-06 17:33:33 -05:00
56b25a6963 allow for reps and times 2024-12-06 01:28:43 -05:00
5bae1aa416 alternating timer types 2024-12-05 00:44:13 -05:00
29479e8aba setting up timer and action management 2024-12-01 21:23:12 -05:00
9ffa0d178c added provider class for timer 2024-12-01 01:42:03 -05:00
932e9cd6a4 add provider package 2024-11-30 12:46:19 -05:00
d42696df61 show rep and set counts 2024-11-30 12:37:38 -05:00
1564d6cd83 start adding timer to activity actions 2024-11-30 04:13:47 -05:00
4fd36246ae ignore pubspec 2024-11-29 12:31:48 -05:00
e8e1737875 update gitignore 2024-11-29 12:28:35 -05:00
baa0f603cf Merge pull request 'SNDTRN-1: update build and some ui changes' (#1) from feature/SNDTRN-1-update-build into main
Reviewed-on: #1
2024-11-29 09:25:00 -08:00
d7bd755c57 Activity media 2024-11-29 12:24:11 -05:00
8890346b59 not working but trying... 2024-11-29 01:06:22 -05:00
a2812b40a0 add activity action view 2024-11-28 17:19:31 -05:00
1c8f03c97b activity categories 2024-11-27 08:21:12 -05:00
b273979ac0 refactor and new activity view 2024-11-27 00:32:01 -05:00
780e270c15 0.2.2 2024-11-25 18:00:07 -05:00
e3a09458d8 0.2.1 2024-11-25 17:58:59 -05:00
6b319a8d96 add version 2024-11-25 17:57:22 -05:00
0fe3d65bb8 formatting 2024-11-25 17:47:27 -05:00
ec6381f04d prep for activity screen 2024-11-25 17:44:50 -05:00
40 changed files with 6042 additions and 1221 deletions

145
.gitignore vendored
View File

@ -9,6 +9,7 @@
.history
.svn/
migrate_working_dir/
bkp
# IntelliJ related
*.iml
@ -31,6 +32,8 @@ migrate_working_dir/
.pub-cache/
.pub/
/build/
pubspec.lock
devtools_options.yaml
# Symbolication related
app.*.symbols
@ -42,3 +45,145 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
# Do not remove or rename entries in this file, only add new ones
# See https://github.com/flutter/flutter/issues/128635 for more context.
# Miscellaneous
*.class
*.lock
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# Visual Studio Code related
.classpath
.project
.settings/
.vscode/*
# Flutter repo-specific
/bin/cache/
/bin/internal/bootstrap.bat
/bin/internal/bootstrap.sh
/bin/mingit/
/dev/benchmarks/mega_gallery/
/dev/bots/.recipe_deps
/dev/bots/android_tools/
/dev/devicelab/ABresults*.json
/dev/docs/doc/
/dev/docs/api_docs.zip
/dev/docs/flutter.docs.zip
/dev/docs/lib/
/dev/docs/pubspec.yaml
/dev/integration_tests/**/xcuserdata
/dev/integration_tests/**/Pods
/packages/flutter/coverage/
version
analysis_benchmark.json
# packages file containing multi-root paths
.packages.generated
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
**/generated_plugin_registrant.dart
.packages
.pub-preload-cache/
.pub-cache/
.pub/
build/
flutter_*.png
linked_*.ds
unlinked.ds
unlinked_spec.ds
# Android related
**/android/**/gradle-wrapper.jar
.gradle/
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
**/android/key.properties
*.jks
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/.last_build_id
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/ephemeral
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# macOS
**/Flutter/ephemeral/
**/Pods/
**/macos/Flutter/GeneratedPluginRegistrant.swift
**/macos/Flutter/ephemeral
**/xcuserdata/
# Windows
**/windows/flutter/ephemeral/
**/windows/flutter/generated_plugin_registrant.cc
**/windows/flutter/generated_plugin_registrant.h
**/windows/flutter/generated_plugins.cmake
# Linux
**/linux/flutter/ephemeral/
**/linux/flutter/generated_plugin_registrant.cc
**/linux/flutter/generated_plugin_registrant.h
**/linux/flutter/generated_plugins.cmake
# Coverage
coverage/
# Symbols
app.*.symbols
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
!/dev/ci/**/Gemfile.lock
!.vscode/settings.json

View File

@ -1,11 +1,11 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
# This file should be version controlled and should not be manually edited.
version:
revision: 9944297138845a94256f1cf37beb88ff9a8e811a
channel: stable
revision: "dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668"
channel: "stable"
project_type: app
@ -13,26 +13,26 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: android
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: ios
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: linux
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: macos
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: web
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: windows
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
# User provided section

View File

@ -1,74 +0,0 @@
{
// Place your snippets for dart here. Each snippet is defined under a snippet name and has a prefix, body and
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
// same ids are connected.
// Example:
// "Print to console": {
// "prefix": "log",
// "body": [
// "console.log('$1');",
// "$2"
// ],
// "description": "Log output to console"
// }
"Stateless Widget": {
"prefix": "stateless",
"body": [
"import 'package:flutter/material.dart';",
"\n",
"class ${1:MyClass} extends StatelessWidget {",
"@override",
"Widget build(BuildContext context) {",
"return Container($0);}}"
]
},
"Stateful Widget": {
"prefix": "stateful",
"body": [
"import 'package:flutter/material.dart';",
"\n",
"class ${1:MyClass} extends StatefulWidget {",
"const ${1:MyClass}({Key key, $2}) : super(key: key);",
"\n",
"@override",
"_${1:MyClass}State createState() => _${1:MyClass}State();",
"}",
"\n",
"class _${1:MyClass}State extends State<${1:MyClass}> {",
"\n",
"@override",
"void initState() {",
"super.initState();",
"}",
"\n",
"@override",
"void dispose() {",
"super.dispose();",
"}",
"\n",
"@override",
"Widget build(BuildContext context) {",
"return Container($0);}}"
]
},
"Widget Test": {
"prefix": "widgettest",
"body": [
"import 'package:flutter_test/flutter_test.dart';",
"import 'package:flutter/material.dart';",
"//import 'package:piota/${1:widgetfile}.dart';",
"\n",
"import 'ui_test_util.dart';",
"\n",
"void main() {",
"group('${2:groupname}', () {",
"final testableWidget = testWidget(${3:Container()},Size(375, 667));",
"testWidgets('${3:Container()} test', (WidgetTester tester) async {",
"final finder = find.byKey(Key('${5:keyname}'));",
"await tester.pumpWidget(testableWidget);",
"expect(finder, findsOneWidget);",
"});});}"
]
}
}

View File

@ -1,4 +1,4 @@
# SendTrain
# SendTrain v0.2.2
Mobile app for community driven climbing training and support.

2
android/.gitignore vendored
View File

@ -7,7 +7,7 @@ gradle-wrapper.jar
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks

View File

@ -1,71 +1,44 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
plugins {
id "com.android.application"
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
namespace = "com.example.sendtrain"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
jvmTarget = JavaVersion.VERSION_1_8
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.sendtrain.sendtrain"
applicationId = "com.example.sendtrain"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = 23
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
signingConfig = signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
source = "../.."
}

View File

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sendtrain.sendtrain">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.

View File

@ -1,9 +1,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sendtrain.sendtrain">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:label="SendTrain"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<!-- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> -->
<activity
android:name=".MainActivity"
android:exported="true"
@ -11,7 +13,8 @@
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
android:windowSoftInputMode="adjustResize"
android:enableOnBackInvokedCallback="true">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
@ -21,8 +24,12 @@
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.

View File

@ -0,0 +1,5 @@
package com.example.sendtrain
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity()

View File

@ -1,6 +0,0 @@
package com.sendtrain.sendtrain
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sendtrain.sendtrain">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.

View File

@ -1,16 +1,3 @@
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
@ -18,12 +5,12 @@ allprojects {
}
}
rootProject.buildDir = '../build'
rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {

View File

@ -1,3 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true

View File

@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip

View File

@ -1,11 +1,25 @@
include ':app'
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.1.0" apply false
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
}
include ":app"

View File

@ -1,33 +0,0 @@
PODS:
- Flutter (1.0.0)
- flutter_inappwebview (0.0.1):
- Flutter
- flutter_inappwebview/Core (= 0.0.1)
- OrderedSet (~> 5.0)
- flutter_inappwebview/Core (0.0.1):
- Flutter
- OrderedSet (~> 5.0)
- OrderedSet (5.0.0)
DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
SPEC REPOS:
trunk:
- OrderedSet
EXTERNAL SOURCES:
Flutter:
:path: Flutter
flutter_inappwebview:
:path: ".symlinks/plugins/flutter_inappwebview/ios"
SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
COCOAPODS: 1.12.0

View File

@ -0,0 +1,12 @@
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}

View File

@ -1,15 +1,89 @@
import 'package:sendtrain/classes/media.dart';
class ActivityAction {
int id;
String title;
String description;
Set activityActionSet;
List<Media>? media;
ActivityAction({
required this.id,
required this.title,
required this.description,
required this.activityActionSet,
this.media,
});
List<List<Map<String, dynamic>>> items() {
List<List<Map<String, dynamic>>> sets = [];
Reps reps = activityActionSet.reps;
int totalActions = 0;
for (int i = 0; i < activityActionSet.total; i++) {
List<Map<String, dynamic>> actions = [];
int? weight = _setWeight(i);
actions.add({
'actionID': totalActions++,
'name': title,
'type': reps.type,
'amount': reps.amounts[i],
'weight': weight,
});
if (activityActionSet.type == 'alternating') {
if (reps.rest != null) {
actions.add({
'actionID': totalActions++,
'name': 'Rest',
'type': 'seconds',
'amount': reps.rest! ~/ 1000,
});
}
actions.add({
'actionID': totalActions++,
'name': title,
'type': reps.type,
'amount': reps.amounts[i],
'weights': weight,
});
}
actions.add({
'actionID': totalActions++,
'name': 'Rest',
'type': 'seconds',
'amount': activityActionSet.rest ~/ 1000,
});
sets.add(actions);
// sets.add([{
// 'actionID': totalActions++,
// 'name': 'Rest',
// 'type': 'seconds',
// 'amount': activityActionSet.rest ~/ 1000,
// }]);
// for (int j = 0; i < activityActionSet.reps.amounts; j++) {}
}
return sets;
}
int? _setWeight(setNum) {
Reps reps = activityActionSet.reps;
if (reps.weights.length == activityActionSet.total) {
return reps.weights[setNum];
} else if (reps.weights.length == 1) {
return reps.weights[0];
}
return null;
}
}
class Set {
@ -30,14 +104,14 @@ class Reps {
String type;
List<int> tempo;
List<int> amounts;
List<int> weights;
int rest;
List<int> weights = [];
int? rest;
Reps({
required this.type,
required this.tempo,
required this.amounts,
required this.weights,
required this.rest,
this.rest,
});
}

121
lib/database.dart Normal file
View File

@ -0,0 +1,121 @@
import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart';
part 'database.g.dart';
enum SessionStatus {
pending,
started,
completed,
missed
}
class Sessions extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 3, max: 32)();
TextColumn get content => text().named('body')();
TextColumn get status => textEnum<SessionStatus>()();
DateTimeColumn get date => dateTime().nullable()();
DateTimeColumn get createdAt => dateTime().withDefault(Variable(DateTime.now()))();
}
class SessionActivities extends Table {
IntColumn get id => integer().autoIncrement()();
IntColumn get sessionId => integer().references(Sessions, #id)();
IntColumn get activityId => integer().references(Activities, #id)();
TextColumn get results => text().nullable()();
TextColumn get achievements => text().nullable()();
DateTimeColumn get createdAt => dateTime().withDefault(Variable(DateTime.now()))();
}
enum ActivityCategories {
fundamentals,
conditioning,
advanced,
custom,
pro
}
enum ActivityType {
strength,
power,
conditioning,
hypertrophy,
endurance,
stability,
mobility,
flexibility,
rehabilitation,
technical
}
class Activities extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 3, max: 32)();
TextColumn get type => textEnum<ActivityType>()();
TextColumn get description => text().named('body')();
TextColumn get category => textEnum<ActivityCategories>()();
DateTimeColumn get createdAt => dateTime().withDefault(Variable(DateTime.now()))();
}
class ActivityActions extends Table {
IntColumn get id => integer().autoIncrement()();
IntColumn get activityId => integer().references(Activities, #id)();
IntColumn get actionId => integer().references(Actions, #id)();
DateTimeColumn get createdAt => dateTime().withDefault(Variable(DateTime.now()))();
}
class Actions extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 3, max: 32)();
TextColumn get description => text().named('body')();
TextColumn get set => text()();
DateTimeColumn get createdAt => dateTime().withDefault(Variable(DateTime.now()))();
}
class ObjectMediaItems extends Table {
IntColumn get id => integer().autoIncrement()();
IntColumn get objectId => integer().references(Actions, #id)();
IntColumn get mediaId => integer().references(MediaItems, #id)();
DateTimeColumn get createdAt => dateTime().withDefault(Variable(DateTime.now()))();
}
enum MediaType {
youtube,
image
}
class MediaItems extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 3, max: 32)();
TextColumn get description => text().named('body')();
TextColumn get reference => text().withLength(min: 3, max: 32)();
TextColumn get type => textEnum<MediaType>()();
DateTimeColumn get createdAt => dateTime().withDefault(Variable(DateTime.now()))();
}
@DriftDatabase(tables: [
Sessions,
SessionActivities,
Activities,
ActivityActions,
Actions,
ObjectMediaItems,
MediaItems
])
class AppDatabase extends _$AppDatabase {
// After generating code, this class needs to define a `schemaVersion` getter
// and a constructor telling drift where the database should be stored.
// These are described in the getting started guide: https://drift.simonbinder.eu/setup/
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 1;
static QueryExecutor _openConnection() {
// `driftDatabase` from `package:drift_flutter` stores the database in
// `getApplicationDocumentsDirectory()`.
return driftDatabase(name: 'sendtrain');
}
}

4487
lib/database.g.dart Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,9 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sendtrain/models/activity_timer_model.dart';
import 'package:sendtrain/screens/activities_screen.dart';
import 'package:sendtrain/screens/sessions_screen.dart';
import 'package:awesome_notifications/awesome_notifications.dart';
class SendTrain extends StatelessWidget {
const SendTrain({super.key});
@ -17,6 +20,10 @@ class SendTrain extends StatelessWidget {
class App extends StatefulWidget {
const App({super.key});
static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
static const String name = 'Awesome Notifications - Example App';
static const Color mainColor = Colors.deepPurple;
@override
State<App> createState() => _AppState();
}
@ -42,7 +49,7 @@ class _AppState extends State<App> {
body: Padding(
padding: const EdgeInsets.fromLTRB(0, 50, 0, 0),
child: <Widget>[
const SessionsScreen(),
SessionsScreen(),
const ActivitiesScreen(),
Container(
alignment: Alignment.center,
@ -88,5 +95,108 @@ class _AppState extends State<App> {
}
void main() {
runApp(const SendTrain());
// final database = AppDatabase();
// database.into(database.sessions).insert(SessionsCompanion.insert(
// title: 'Projecting @ Climbers Rock',
// content: 'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
// status: SessionStatus.started,
// date: Value(DateTime.now())));
// database.into(database.sessions).insert(SessionsCompanion.insert(
// title: 'Moonboard @ Boardroom',
// content: 'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
// status: SessionStatus.pending,
// date: Value(DateTime.now())));
// database.into(database.sessions).insert(SessionsCompanion.insert(
// title: 'Moonboard @ Boardroom',
// content: 'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
// status: SessionStatus.completed,
// date: Value(DateTime.now())));
// database.into(database.sessions).insert(SessionsCompanion.insert(
// title: 'Projecting @ Climbers Rock',
// content: 'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
// status: SessionStatus.completed,
// date: Value(DateTime.now())));
// database.into(database.sessions).insert(SessionsCompanion.insert(
// title: 'Off-Wall Training',
// content: 'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
// status: SessionStatus.missed,
// date: Value(DateTime.now())));
// database.into(database.sessions).insert(SessionsCompanion.insert(
// title: 'Off-Wall Training',
// content: 'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
// status: SessionStatus.completed,
// date: Value(DateTime.now())));
// database.into(database.activities).insert(ActivitiesCompanion.insert(
// title: "test activity",
// type: ActivityType.technical,
// description: "test training activity",
// category: ActivityCategories.fundamentals));
// database
// .into(database.sessionActivities)
// .insert(SessionActivitiesCompanion.insert(
// sessionId: 1,
// activityId: 1,
// results: Value("results json, will need to test"),
// achievements: Value("comma, seperated, items"),
// ));
// database.into(database.actions).insert(ActionsCompanion.insert(
// title: "test action title",
// description: "teste action description",
// set: "not sure how the json will work yet",
// ));
// database
// .into(database.activityActions)
// .insert(ActivityActionsCompanion.insert(
// activityId: 1,
// actionId: 1,
// ));
// database.into(database.mediaItems).insert(MediaItemsCompanion.insert(
// title: "test youtube media item",
// description: "this is a test youtube item",
// reference: "sZVAEy9UmoY",
// type: MediaType.youtube));
// database
// .into(database.objectMediaItems)
// .insert(ObjectMediaItemsCompanion.insert(objectId: 1, mediaId: 1));
WidgetsFlutterBinding.ensureInitialized();
AwesomeNotifications().initialize(
// set the icon to null if you want to use the default app icon
null,
[
NotificationChannel(
channelGroupKey: 'activity_progress_group',
channelKey: 'activity_progress',
channelName: 'Activity Progress notifications',
channelDescription: 'Notification channel for Activity progression',
defaultColor: Color(0xFF9D50DD),
ledColor: Colors.white,
importance: NotificationImportance.Max,
)
],
// Channel groups are only visual and are not required
channelGroups: [
NotificationChannelGroup(
channelGroupKey: 'activity_progress_group',
channelGroupName: 'Activity Progress group')
],
debug: true);
runApp(
ChangeNotifierProvider(
create: (context) => ActivityTimerModel(),
child: const SendTrain(),
),
);
}

View File

@ -0,0 +1,201 @@
import 'dart:async';
import 'package:awesome_notifications/awesome_notifications.dart';
import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:sendtrain/models/activity_model.dart';
class ActivityTimerModel with ChangeNotifier {
int _actionCounter = 0;
ActivityModel? _activity;
List _sets = [];
int _currentActionNum = 0;
int _currentSetNum = 0;
Timer? _periodicTimer;
double _progress = 0;
ItemScrollController? _isc;
int _totalTime = 0;
int get actionCount => _actionCounter;
int get currentActionNum => _currentActionNum;
dynamic get currentAction => currentSet[_currentActionNum];
int get currentSetNum => _currentSetNum;
dynamic get currentSet => _sets[_currentSetNum];
ActivityModel? get activity => _activity;
List get sets => _sets;
Timer? get periodicTimer => _periodicTimer;
bool get isActive => _isActive();
double get progress => _progress;
int get totalTime => _totalTime;
void createNotification() {
AwesomeNotifications().isNotificationAllowed().then((isAllowed) {
if (!isAllowed) {
// This is just a basic example. For real apps, you must show some
// friendly dialog box before call the request method.
// This is very important to not harm the user experience
AwesomeNotifications().requestPermissionToSendNotifications();
}
});
AwesomeNotifications().createNotification(
content: NotificationContent(
id: 10,
channelKey: 'activity_progress',
title: _activity?.title,
body: _activity?.description,
category: NotificationCategory.Workout,
// payload: {
// 'file': 'filename.txt',
// 'path': '-rmdir c://ruwindows/system32/huehuehue'
// },
notificationLayout: NotificationLayout.ProgressBar,
progress: _progress,
locked: true));
// AwesomeNotifications().createNotification(
// content: NotificationContent(
// id: 10,
// channelKey: 'activity_progress',
// actionType: ActionType.Default,
// title: _activity?.title,
// body: _activity?.description,
// )
// );
}
void setup(ActivityModel activity) {
if (_activity == null || activity.id != _activity?.id) {
_periodicTimer?.cancel();
_progress = 0;
_isc = null;
_activity = activity;
_sets = activity.actions[0].items();
_currentActionNum = 0;
_currentSetNum = 0;
setActionCount();
getTotalTime();
createNotification();
}
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() {
_progress = 0;
_currentActionNum = 0;
_currentSetNum = 0;
_periodicTimer!.cancel();
setActionCount();
moveToIndex(0);
}
void setScrollController(ItemScrollController isc) {
_isc = isc;
}
bool isCurrentItem(int setNum, int actionNum) {
if (setNum == _currentSetNum && actionNum == _currentActionNum) {
return true;
}
return false;
}
int totalActions() {
int count = 0;
for (int i = 0; i < _sets.length; i++) {
count = count + _sets[i].length as int;
}
return count;
}
void setActionCount() {
_actionCounter = currentAction['amount'];
}
void pause() {
_periodicTimer!.cancel();
notifyListeners();
}
void start() {
_periodicTimer = Timer.periodic(const Duration(seconds: 1), (Timer timer) {
switch (currentAction['type']) {
// we don't want to count down
// if its repititions
case 'repititions':
break;
case 'seconds':
if (_actionCounter > 0) {
_actionCounter--;
_totalTime--;
} else {
nextAction(_currentActionNum + 1);
setActionCount();
}
updateProgress();
createNotification();
}
notifyListeners();
});
}
void updateProgress() {
_progress = (currentAction['actionID'] +
(1.0 - _actionCounter / currentAction['amount'])) /
totalActions();
notifyListeners();
}
void setAction(int setNum, int actionNum, String type) {
_currentActionNum = actionNum;
_currentSetNum = setNum;
notifyListeners();
moveToIndex(_currentSetNum);
}
void nextAction(int nextActionIndex) {
if (currentSet.length > nextActionIndex) {
setAction(_currentSetNum, nextActionIndex, 'automatic');
} else if (_sets.length > _currentSetNum + 1) {
// if the item isn't in the set
// increment the set and reset action index
setAction(_currentSetNum + 1, 0, 'automatic');
} else {
// if we're done all the sets
// cancel timer and reset activity
reset();
}
}
void moveToIndex(int index) {
if (_isc != null && _isc!.isAttached) {
_isc?.scrollTo(
index: index,
duration: Duration(milliseconds: 500),
curve: Curves.easeInOutCubic);
}
}
bool _isActive() {
return (_periodicTimer != null && _periodicTimer!.isActive) ? true : false;
}
}

View File

@ -1,4 +1,6 @@
import 'package:flutter/material.dart';
import 'package:sendtrain/classes/activity_action.dart';
import 'package:sendtrain/models/activity_model.dart';
import '../widgets/activities_header.dart';
import '../widgets/activity_card.dart';
@ -11,9 +13,34 @@ class ActivitiesScreen extends StatefulWidget {
}
class _ActivitiesScreenState extends State<ActivitiesScreen> {
final data = ActivityModel(
id: 1,
categories: ['power'],
description: 'description',
title: 'activity',
type: 'fundamentals',
actions: List.generate(
10,
(i) => ActivityAction(
id: 1,
title: 'test action',
description: 'test description',
activityActionSet: Set(
type: 'drop_set',
total: 3,
rest: 300000,
reps: Reps(
type: 'count',
tempo: [2, 3, 5],
amounts: [5, 3, 2],
weights: [50, 70, 80],
rest: 20000))),
));
@override
Widget build(BuildContext context) {
List<Widget> activities = List.generate(10, (i) => ActivityCard());
List<Widget> activities = List.generate(10, (i) => ActivityCard(activity: data));
return Padding(
padding: const EdgeInsets.fromLTRB(10, 15, 10, 0),

View File

@ -1,15 +1,34 @@
import 'package:drift/drift.dart' hide Column;
import 'package:flutter/material.dart';
import 'package:sendtrain/database.dart';
import '../widgets/session_card.dart';
class SessionsScreen extends StatelessWidget {
const SessionsScreen({super.key});
final AppDatabase database = AppDatabase();
SessionsScreen({super.key});
Future<List<Session>> getSessions() async {
// database.managers.sessions.filter((session) => session.status(SessionStatus.pending));
return await database.managers.sessions.get();
}
@override
Widget build(BuildContext context) {
List<Widget> previousSessions =
List.generate(10, (i) => const SessionCard(state: 1, type: 1));
Widget upcomingSession = const SessionCard(state: 2);
Widget currentSession = const SessionCard();
return FutureBuilder<List<Session>>(
future: getSessions(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final sessions = snapshot.data!;
final pending = sessions.where((session) => session.status == SessionStatus.completed || session.status == SessionStatus.missed);
final upcoming = sessions.firstWhere((session) => session.status == SessionStatus.pending);
final current = sessions.firstWhere((session) => session.status == SessionStatus.started);
List<Widget> previousSessions = List.generate(pending.length, (i) => SessionCard(type: 1, session: pending.elementAt(i)));
Widget upcomingSession =
SessionCard(session: upcoming);
Widget currentSession = SessionCard(session: current);
database.close();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -41,12 +60,54 @@ class SessionsScreen extends StatelessWidget {
mainAxisSpacing: 5,
crossAxisCount: 1,
children: previousSessions))
// Flexible(
// child: ListView(
// scrollDirection: Axis.vertical,
// children: previousSessions,
// )),
],
);
} else {
return const CircularProgressIndicator();
}
});
// List<Widget> previousSessions = List.generate(
// 10, (i) => SessionCard(state: 1, type: 1, session: _sessions.first));
// Widget upcomingSession = SessionCard(state: 2, session: _sessions.first);
// Widget currentSession = SessionCard(session: _sessions.first);
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: <Widget>[
// const Padding(
// padding: EdgeInsets.fromLTRB(15, 5, 0, 0),
// child: Text(
// style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
// 'Current:')),
// currentSession,
// const Padding(
// padding: EdgeInsets.fromLTRB(15, 30, 0, 0),
// child: Text(
// style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
// 'Upcoming:')),
// upcomingSession,
// const Padding(
// padding: EdgeInsets.fromLTRB(15, 30, 0, 0),
// child: Text(
// style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
// 'Previous:')),
// SizedBox(
// width: double.infinity,
// height: 160,
// child: GridView.count(
// padding: const EdgeInsets.fromLTRB(15, 10, 0, 0),
// scrollDirection: Axis.horizontal,
// crossAxisSpacing: 5,
// mainAxisSpacing: 5,
// crossAxisCount: 1,
// children: previousSessions))
// // Flexible(
// // child: ListView(
// // scrollDirection: Axis.vertical,
// // children: previousSessions,
// // )),
// ],
// );
}
}

View File

@ -5,10 +5,10 @@ class ActivitiesHeader extends StatefulWidget {
const ActivitiesHeader({super.key});
@override
_ActivitiesHeaderState createState() => _ActivitiesHeaderState();
State<ActivitiesHeader> createState() => ActivitiesHeaderState();
}
class _ActivitiesHeaderState extends State<ActivitiesHeader> {
class ActivitiesHeaderState extends State<ActivitiesHeader> {
@override
void initState() {
super.initState();

View File

@ -0,0 +1,108 @@
import 'package:flutter/material.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/models/activity_timer_model.dart';
class ActivityActionView extends StatefulWidget {
const ActivityActionView({super.key, required this.action});
final ActivityAction action;
@override
State<ActivityActionView> createState() => ActivityActionViewState();
}
class ActivityActionViewState extends State<ActivityActionView> {
final ItemScrollController itemScrollController = ItemScrollController();
final ScrollOffsetController scrollOffsetController =
ScrollOffsetController();
final ItemPositionsListener itemPositionsListener =
ItemPositionsListener.create();
final ScrollOffsetListener scrollOffsetListener =
ScrollOffsetListener.create();
@override
Widget build(BuildContext context) {
ActivityTimerModel atm =
Provider.of<ActivityTimerModel>(context, listen: true);
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(
child: ScrollablePositionedList.builder(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 20),
itemCount: widget.action.activityActionSet.total,
itemScrollController: itemScrollController,
scrollOffsetController: scrollOffsetController,
itemPositionsListener: itemPositionsListener,
scrollOffsetListener: scrollOffsetListener,
itemBuilder: (BuildContext context, int setNum) {
List<GestureDetector> content = [];
List<Map<String, dynamic>> set = sets[setNum];
for (int actionNum = 0; actionNum < set.length; actionNum++) {
Map<String, dynamic> setItem = set[actionNum];
content.add(GestureDetector(
onTap: () {
atm.setAction(setNum, actionNum, 'manual');
atm.setActionCount();
itemScrollController.scrollTo(
index: setNum,
duration: Duration(milliseconds: 500),
curve: Curves.easeInOutCubic);
},
child: Row(children: [
Ink(
width: 70,
padding: const EdgeInsets.all(15),
color: atm.isCurrentItem(setNum, actionNum)
? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).colorScheme.onPrimary,
child: Text(
textAlign: TextAlign.center,
'${setNum + 1}.${actionNum + 1} ')),
Expanded(
child: Ink(
padding: const EdgeInsets.all(15),
color: atm.isCurrentItem(setNum, actionNum)
? Theme.of(context).colorScheme.surfaceBright
: Theme.of(context).colorScheme.surfaceContainerLow,
child: Text(
textAlign: TextAlign.center,
'${setItem['name']}: ${setItem['amount']} ${setItem['type']}')))
])));
}
if (setNum == 0) {
return Card(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(0),
topRight: Radius.circular(0),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10)),
),
clipBehavior: Clip.antiAlias,
child: Column(children: content));
} else {
return Card(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10)),
),
clipBehavior: Clip.antiAlias,
child: Column(children: content));
}
// return Column(children: contents);
},
));
}
}

View File

@ -1,46 +1,127 @@
import 'package:flutter/material.dart';
import 'package:sendtrain/classes/activity_action.dart';
import 'package:provider/provider.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/models/activity_timer_model.dart';
import 'package:sendtrain/widgets/activity_view.dart';
class ActivityCard extends StatelessWidget {
ActivityCard({super.key});
class ActivityCard extends StatefulWidget {
final ActivityModel activity;
final data = ActivityModel(
id: 1,
categories: ['power'],
description: 'description',
title: 'activity',
type: 'fundamentals',
actions: List.generate(
10,
(i) => ActivityAction(
id: 1,
title: 'test action',
description:'test description',
activityActionSet: Set(
type: 'drop_set',
total: 3,
rest: 3000,
reps: Reps(
type: 'count',
tempo: [2,3,5],
amounts: [5,3,2],
weights: [50,70,80],
rest: 200
)
)
),));
const ActivityCard({super.key, required this.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
Widget build(BuildContext context) {
final ActivityTimerModel atm =
Provider.of<ActivityTimerModel>(context, listen: false);
return Card(
color: atm.activity?.id == widget.activity.id
? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).colorScheme.surfaceContainerLow,
clipBehavior: Clip.hardEdge,
child: Align(
alignment: Alignment.center,
child: Text(
data.title,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
child: InkWell(
onTap: () => showGeneralDialog(
barrierColor: Colors.black.withOpacity(0.5),
transitionDuration: const Duration(milliseconds: 220),
transitionBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
Animation<Offset> custom = Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: const Offset(0.0, 0.0))
.animate(animation);
return SlideTransition(
position: custom,
child: Dialog.fullscreen(
child: ActivityView(activity: widget.activity)));
},
barrierDismissible: true,
barrierLabel: '',
context: context,
pageBuilder: (context, animation1, animation2) {
return Container();
}),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: Container(
width: 60,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: findMediaByType(
widget.activity.actions[0].media, 'image')),
// color: Colors.blue,
borderRadius:
const BorderRadius.all(Radius.elliptical(10, 10)),
),
));
)),
title: Consumer<ActivityTimerModel>(
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),
trailing: IconButton(
visualDensity: VisualDensity.compact,
icon: Icon(Icons.close_rounded),
onPressed: () {
showAdaptiveDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('Activity Removal'),
content: const Text('Would you like to permanently remove this activity from the current session?'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'Cancel'),
child: const Text('Cancel'),
),
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
),
);
},
)),
],
)),
);
}
ImageProvider findMediaByType(List<Media>? media, String type) {
var found = media?.where((m) => m.type == 'image');
if (found != null) {
return NetworkImage(found.first.reference);
} else {
// Element is not found
return const AssetImage('assets/images/placeholder.jpg');
}
}
}

View File

@ -0,0 +1,214 @@
import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import 'package:provider/provider.dart';
import 'package:sendtrain/classes/activity_action.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/models/activity_timer_model.dart';
import 'package:sendtrain/widgets/activity_action_view.dart';
import 'package:sendtrain/widgets/media_card.dart';
class ActivityView extends StatefulWidget {
const ActivityView({super.key, required this.activity});
final ActivityModel activity;
@override
State<ActivityView> createState() => _ActivityViewState();
}
class _ActivityViewState extends State<ActivityView> {
@override
Widget build(BuildContext context) {
ActivityModel activity = widget.activity;
ActivityTimerModel atm =
Provider.of<ActivityTimerModel>(context, listen: false);
atm.setup(activity);
return Scaffold(
floatingActionButtonLocation: ExpandableFab.location,
floatingActionButton: ExpandableFab(
distance: 70,
type: ExpandableFabType.up,
overlayStyle: ExpandableFabOverlayStyle(
color: Colors.black.withOpacity(0.5),
blur: 10,
),
children: [
FloatingActionButton.extended(
icon: const Icon(Icons.history_outlined),
label: Text('Restart'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.done_all_outlined),
label: Text('Done'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.edit_outlined),
label: Text('Edit'),
onPressed: () {},
),
]),
body: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
AppBar(
centerTitle: true,
title: const Text('Activity', style: TextStyle(fontSize: 15)),
),
Padding(
padding: const EdgeInsets.only(
left: 15, right: 20, top: 15, bottom: 10),
child: Text(
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>(
builder: (context, atm, child) {
return IconButton(
alignment: AlignmentDirectional.center,
icon: atm.isActive
? const Icon(Icons.pause_rounded)
: const Icon(Icons.play_arrow_rounded),
onPressed: () =>
{atm.isActive ? atm.pause() : atm.start()});
},
)),
Expanded(
flex: 1,
child: Stack(alignment: Alignment.center, children: [
Container(
alignment: Alignment.center,
child: Consumer<ActivityTimerModel>(
builder: (context, atm, child) {
return Text(
style: const TextStyle(fontSize: 20),
textAlign: TextAlign.center,
'${atm.actionCount} ${atm.currentAction['type']}');
},
),
),
Container(
alignment: Alignment.centerRight,
padding: EdgeInsets.only(right: 15),
child: Consumer<ActivityTimerModel>(
builder: (context, atm, child) {
return Text(
style: const TextStyle(fontSize: 12),
textAlign: TextAlign.right,
'${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]),
]));
}
}
class ActivityViewCategories extends StatelessWidget {
const ActivityViewCategories({super.key, this.categories});
final List<String>? categories;
@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: SizedBox(
height: 40,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
itemCount: categories?.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.only(right: 5),
child: ActionChip(
visualDensity: VisualDensity.compact,
avatar: const Icon(Icons.check_circle_outline),
label: Text(maxLines: 1, '${categories?[index]}'),
onPressed: () {},
));
},
))),
],
);
}
}
class ActivityViewMedia extends StatelessWidget {
const ActivityViewMedia({super.key, this.activity});
final ActivityModel? activity;
@override
Widget build(BuildContext context) {
List<Media> media = [];
for (ActivityAction action in activity!.actions) {
if (action.media!.isNotEmpty) {
media.addAll(action.media as Iterable<Media>);
}
}
List<Widget> mediaCards =
List.generate(media.length, (i) => MediaCard(media: media[i]));
return Column(
children: [
SizedBox(
width: double.infinity,
height: 100,
child: GridView.count(
padding: const EdgeInsets.fromLTRB(15, 0, 0, 0),
scrollDirection: Axis.horizontal,
crossAxisSpacing: 5,
mainAxisSpacing: 5,
crossAxisCount: 1,
children: mediaCards))
],
);
}
}

View File

@ -3,23 +3,24 @@ import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:sendtrain/classes/activity_action.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/database.dart' hide ActivityAction;
import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/models/session_model.dart';
import 'package:sendtrain/widgets/session_view.dart';
class SessionCard extends StatelessWidget {
final int state;
final int type;
const SessionCard({super.key, this.state = 0, this.type = 0});
final Session session;
const SessionCard({super.key, this.type = 0, required this.session});
@override
Widget build(BuildContext context) {
initializeDateFormatting('en');
final DateFormat dateFormat = DateFormat('yyyy-MM-dd');
Color color = (state == 0)
? const Color(0xff3A5FB6)
: ThemeData.dark(useMaterial3: true).colorScheme.surface;
Color color = (session.status == SessionStatus.started)
? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).colorScheme.surfaceContainerLow;
// place holder until we can retrieve real data
final data = SessionModel(
@ -35,79 +36,113 @@ class SessionCard extends StatelessWidget {
type: 'fundamental',
categories: ['strength', 'power'],
description:
"Campus board session, focussing on explosiveness and contact strength.",
"Campus board session, focusing on explosiveness and contact strength.",
actions: [
ActivityAction(
id: 1,
title: 'test action',
description:'test description',
title: '1, 3, 5',
description:
'Move between the first, third, and fifth rungs, alternating hands. Rest and alternate sides, to start',
media: [
Media(
id: 1,
reference:
'https://www.climbing.com/wp-content/uploads/2022/06/campus-board-e1655470701154.jpeg',
type: 'image',
description: 'Campus board movement'),
Media(
id: 1,
reference: '7ACyeOP-Hxo',
type: 'youtube',
description: 'How to campus board')
],
activityActionSet: Set(
type: 'drop_set',
total: 3,
rest: 3000,
rest: 300000,
reps: Reps(
type: 'count',
tempo: [2,3,5],
amounts: [5,3,2],
weights: [50,70,80],
rest: 200
)
)
),
type: 'repititions',
tempo: [0],
amounts: [1, 1, 1],
weights: [0],
rest: 20000))),
],
resources: ['https://www.youtube.com/watch?v=bLz0xp1PEm4']),
ActivityModel(
id: 1,
id: 2,
title: 'Projecting',
type: 'fundamental',
categories: ['technique', 'conditioning'],
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: [
ActivityAction(
id: 1,
title: 'test action',
description:'test description',
title: 'Attempt Climb',
description:
'Attempt your selected climb, if you fall off early in the climb, attempt again. 1 repitition equals roughly doing all the moves, not necessarily in 1 attempt.',
media: [
Media(
id: 1,
reference:
'https://www.climbing.com/wp-content/uploads/2022/07/Fixed-44.jpg',
type: 'image',
description: 'Projecting a climb'),
Media(
id: 1,
reference: 'BgheYcxhrsw',
type: 'youtube',
description: 'How to project climbs')
],
activityActionSet: Set(
type: 'drop_set',
total: 3,
rest: 3000,
type: 'standard',
total: 10,
rest: 300000,
reps: Reps(
type: 'count',
tempo: [2,3,5],
amounts: [5,3,2],
weights: [50,70,80],
rest: 200
)
)
),
type: 'repititions',
tempo: [0],
amounts: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
weights: [0],
rest: 0))),
],
resources: ['https://www.youtube.com/watch?v=dyAvbUvY_PU']),
ActivityModel(
id: 1,
title: 'Weighted Pull Ups',
type: 'fundamental',
id: 3,
title: 'Long Block Pulls',
type: 'Hypertrophy',
categories: ['Strength', 'Power'],
description:
"Weight pullups to increase strength and maximal pulling force.",
"Block pull on a edge of a specific size and time to induce a hypertrophic effect on the formarms.",
actions: [
ActivityAction(
id: 1,
title: 'test action',
description:'test description',
title: 'Long Pulls',
description:
'Select your desired weight to pull, add it to the block. You should aim for an effort level of 8-9 when reaching the end of the set time, going to failure will result in significantly extended recovery time.',
media: [
Media(
id: 1,
reference:
'https://trailandcrag.com/sites/default/files/inline-images/05-min_3.jpg',
type: 'image',
description: 'Block pull example'),
Media(
id: 1,
reference: 'sZVAEy9UmoY',
type: 'youtube',
description:
'Principals of Grip gains, and related protocols')
],
activityActionSet: Set(
type: 'drop_set',
total: 3,
rest: 3000,
type: 'alternating',
total: 5,
rest: 5000,
reps: Reps(
type: 'count',
tempo: [2,3,5],
amounts: [5,3,2],
weights: [50,70,80],
rest: 200
)
)
),
type: 'seconds',
tempo: [0],
amounts: [5, 5, 5, 5, 5],
weights: [80, 80, 80, 80, 80],
rest: 5000))),
],
resources: ['https://www.youtube.com/watch?v=dyAvbUvY_PU']),
],
@ -142,7 +177,7 @@ class SessionCard extends StatelessWidget {
margin: const EdgeInsets.fromLTRB(15, 15, 15, 0),
clipBehavior: Clip.hardEdge,
child: InkWell(
// splashColor: Colors.deepPurple,
splashColor: Colors.deepPurple,
onTap: () => showGeneralDialog(
barrierColor: Colors.black.withOpacity(0.5),
transitionDuration: const Duration(milliseconds: 220),
@ -156,7 +191,7 @@ class SessionCard extends StatelessWidget {
.animate(animation);
return SlideTransition(
position: custom,
child: Dialog.fullscreen(child: SessionView(data: data)));
child: Dialog.fullscreen(child: SessionView(data: data, session: session)));
},
barrierDismissible: true,
barrierLabel: '',
@ -182,8 +217,32 @@ class SessionCard extends StatelessWidget {
BorderRadius.all(Radius.elliptical(10, 10)),
),
)),
title: Text(maxLines: 1, data.title),
subtitle: Text(maxLines: 1, dateFormat.format(data.date)),
title: Text(maxLines: 1, session.title),
subtitle: Text(maxLines: 1, dateFormat.format(session.date as DateTime)),
trailing: IconButton(
visualDensity: VisualDensity.compact,
icon: Icon(Icons.close_rounded),
onPressed: () {
showAdaptiveDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('Session Removal'),
content: const Text(
'Would you like to permanently remove this session?'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'Cancel'),
child: const Text('Cancel'),
),
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
),
);
},
),
),
ListTile(
contentPadding: const EdgeInsets.fromLTRB(15, 0, 15, 15),
@ -191,17 +250,17 @@ class SessionCard extends StatelessWidget {
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w300),
data.content),
session.content),
),
],
)),
);
} else {
return Card(
color: const Color.fromARGB(125, 0, 0, 0),
color: color,
child: InkWell(
// overlayColor: MaterialStateColor(Colors.deepPurple as int),
// splashColor: Colors.deepPurple,
splashColor: Colors.deepPurple,
borderRadius: const BorderRadius.all(Radius.elliptical(10, 10)),
onTap: () => showGeneralDialog(
// barrierColor: Colors.black.withOpacity(0.5),
@ -217,7 +276,7 @@ class SessionCard extends StatelessWidget {
return SlideTransition(
position: custom,
child:
Dialog.fullscreen(child: SessionView(data: data)));
Dialog.fullscreen(child: SessionView(data: data, session: session)));
},
barrierDismissible: true,
barrierLabel: '',
@ -244,11 +303,11 @@ class SessionCard extends StatelessWidget {
ListTile(
title: Text(
maxLines: 3,
data.title,
session.title,
textAlign: TextAlign.center),
subtitle: Text(
maxLines: 1,
dateFormat.format(data.date),
dateFormat.format(session.date as DateTime),
textAlign: TextAlign.center),
),
])))));

View File

@ -1,57 +1,87 @@
import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/database.dart';
import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/models/session_model.dart';
import 'package:sendtrain/widgets/activity_card.dart';
import 'package:sendtrain/widgets/media_card.dart';
class SessionView extends StatelessWidget {
const SessionView({super.key, required this.data});
const SessionView({super.key, required this.data, required this.session});
final SessionModel data;
final Session session;
@override
Widget build(BuildContext context) {
initializeDateFormatting('en');
final DateFormat dateFormat = DateFormat('yyyy-MM-dd');
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
return Scaffold(
floatingActionButtonLocation: ExpandableFab.location,
floatingActionButton: ExpandableFab(
distance: 70,
type: ExpandableFabType.up,
overlayStyle: ExpandableFabOverlayStyle(
color: Colors.black.withOpacity(0.5),
blur: 10,
),
children: [
FloatingActionButton.extended(
icon: const Icon(Icons.history_outlined),
label: Text('Restart'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.done_all_outlined),
label: Text('Done'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.edit_outlined),
label: Text('Edit'),
onPressed: () {},
),
]),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AppBar(
centerTitle: true,
title: Text('Session @ ${dateFormat.format(data.date)}',
title: Text('Session @ ${dateFormat.format(session.date as DateTime)}',
style: const TextStyle(fontSize: 15)),
),
Padding(
padding: const EdgeInsets.only(left: 20, right: 20, bottom: 10),
padding: const EdgeInsets.only(
left: 15, right: 20, top: 15, bottom: 10),
child: Text(
maxLines: 1,
style:
const TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
data.title)),
style: const TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
session.title)),
SessionViewAchievements(achievements: data.achievements),
Padding(
padding: const EdgeInsets.only(left: 15, right: 15),
child: Text(textAlign: TextAlign.center, data.content)),
SessionViewMedia(media: data.media),
child:
Text(style: const TextStyle(fontSize: 15), session.content)),
const Padding(
padding: EdgeInsets.only(top: 25, bottom: 10),
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
'Activities:')),
'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),
// TextButton(
// onPressed: () {
// Navigator.pop(context);
// },
// child: const Text('Close'),
// ),
],
);
));
}
}
@ -64,57 +94,11 @@ class SessionViewActivities extends StatelessWidget {
Widget build(BuildContext context) {
return Expanded(
child: ListView.builder(
shrinkWrap: true,
// shrinkWrap: true,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
itemCount: activities?.length,
itemBuilder: (BuildContext context, int index) {
return Card(
color: const Color(0xff3A5FB6),
child: ListTile(
// dense: true,
focusColor: const Color(0xff3A5FB6),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.elliptical(10, 10))),
onTap: () => showGeneralDialog(
barrierColor: Colors.black.withOpacity(0.5),
transitionDuration: const Duration(milliseconds: 220),
transitionBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
Animation<Offset> custom = Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: const Offset(0.0, 0.0))
.animate(animation);
return SlideTransition(
position: custom,
child: Dialog.fullscreen(
child: Flex(
direction: Axis.vertical,
mainAxisSize: MainAxisSize.min,
children: [
Text('${activities?[index].title}'),
Text('${activities?[index].categories}'),
Text('${activities?[index].description}'),
Text('${activities?[index].resources}'),
Text('${activities?[index].actions[0].title}'),
Text(
'${activities?[index].actions[0].description}'),
])));
},
barrierDismissible: true,
barrierLabel: '',
context: context,
pageBuilder: (context, animation1, animation2) {
return Container();
}),
enableFeedback: true,
title: Text(maxLines: 1, '${activities?[index].title}'),
subtitle: Padding(
padding: const EdgeInsets.only(bottom: 3),
child:
Text(maxLines: 2, '${activities?[index].description}')),
));
return ActivityCard(activity: activities![index]);
},
));
}
@ -144,22 +128,8 @@ class SessionViewAchievements extends StatelessWidget {
visualDensity: VisualDensity.compact,
avatar: const Icon(Icons.check_circle_outline),
label: Text(maxLines: 1, '${achievements?[index]}'),
onPressed: () {
// setState(() {
// favorite = !favorite;
// });
},
onPressed: () {},
));
// return Card(
// child: ListTile(
// // dense: true,
// focusColor: Colors.deepPurple,
// shape: const RoundedRectangleBorder(
// borderRadius: BorderRadius.all(Radius.circular(10))),
// onTap: () {},
// enableFeedback: true,
// title: Text(maxLines: 1, '${achievements?[index]}'),
// ));
},
))),
],
@ -179,16 +149,11 @@ class SessionViewMedia extends StatelessWidget {
return Column(
children: [
const Padding(
padding: EdgeInsets.only(top: 25),
child: Text(
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
'Media:')),
SizedBox(
width: double.infinity,
height: 100,
child: GridView.count(
padding: const EdgeInsets.fromLTRB(15, 10, 0, 0),
padding: const EdgeInsets.fromLTRB(15, 0, 0, 0),
scrollDirection: Axis.horizontal,
crossAxisSpacing: 5,
mainAxisSpacing: 5,
@ -198,24 +163,3 @@ class SessionViewMedia extends StatelessWidget {
);
}
}
// SizedBox(
// height: 100,
// width: double.infinity,
// child: ListView.builder(
// // scrollDirection: Axis.horizontal,
// padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
// itemCount: media?.length,
// itemBuilder: (BuildContext context, int index) {
// return Card(
// child: ListTile(
// dense: true,
// focusColor: Colors.deepPurple,
// shape: const RoundedRectangleBorder(
// borderRadius: BorderRadius.all(Radius.circular(10))),
// onTap: () {},
// enableFeedback: true,
// title: const Text(maxLines: 1, 'test'),
// ));
// }))

View File

@ -1,11 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
void fl_register_plugins(FlPluginRegistry* registry) {
}

View File

@ -1,15 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View File

@ -1,23 +0,0 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View File

@ -1,10 +0,0 @@
//
// Generated file. Do not edit.
//
import FlutterMacOS
import Foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
}

View File

@ -0,0 +1,12 @@
import Cocoa
import FlutterMacOS
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}

View File

@ -1,610 +0,0 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834
url: "https://pub.dev"
source: hosted
version: "72.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.2"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139
url: "https://pub.dev"
source: hosted
version: "6.7.0"
args:
dependency: transitive
description:
name: args
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
url: "https://pub.dev"
source: hosted
version: "2.6.0"
async:
dependency: transitive
description:
name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
source: hosted
version: "2.11.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
build:
dependency: transitive
description:
name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
build_config:
dependency: transitive
description:
name: build_config
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
url: "https://pub.dev"
source: hosted
version: "1.1.1"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
url: "https://pub.dev"
source: hosted
version: "4.0.1"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
url: "https://pub.dev"
source: hosted
version: "2.4.13"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799"
url: "https://pub.dev"
source: hosted
version: "7.3.0"
built_collection:
dependency: transitive
description:
name: built_collection
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
url: "https://pub.dev"
source: hosted
version: "8.9.2"
characters:
dependency: transitive
description:
name: characters
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
url: "https://pub.dev"
source: hosted
version: "2.0.3"
clock:
dependency: transitive
description:
name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
source: hosted
version: "1.1.1"
code_builder:
dependency: transitive
description:
name: code_builder
sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
url: "https://pub.dev"
source: hosted
version: "4.10.0"
collection:
dependency: transitive
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted
version: "1.18.0"
convert:
dependency: transitive
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
crypto:
dependency: transitive
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.3"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
url: "https://pub.dev"
source: hosted
version: "1.0.5"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
url: "https://pub.dev"
source: hosted
version: "2.3.6"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.dev"
source: hosted
version: "1.1.1"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_inappwebview:
dependency: transitive
description:
name: flutter_inappwebview
sha256: f73505c792cf083d5566e1a94002311be497d984b5607f25be36d685cf6361cf
url: "https://pub.dev"
source: hosted
version: "5.7.2+3"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
url: "https://pub.dev"
source: hosted
version: "2.0.1"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev"
source: hosted
version: "4.0.0"
glob:
dependency: transitive
description:
name: glob
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
graphs:
dependency: transitive
description:
name: graphs
sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
url: "https://pub.dev"
source: hosted
version: "2.3.1"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
intl:
dependency: "direct main"
description:
name: intl
sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6
url: "https://pub.dev"
source: hosted
version: "0.18.0"
io:
dependency: transitive
description:
name: io
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
js:
dependency: transitive
description:
name: js
sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
url: "https://pub.dev"
source: hosted
version: "0.6.5"
json_annotation:
dependency: "direct main"
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
json_serializable:
dependency: "direct dev"
description:
name: json_serializable
sha256: c2fcb3920cf2b6ae6845954186420fca40bc0a8abcc84903b7801f17d7050d7c
url: "https://pub.dev"
source: hosted
version: "6.9.0"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
url: "https://pub.dev"
source: hosted
version: "10.0.5"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
url: "https://pub.dev"
source: hosted
version: "3.0.5"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
lints:
dependency: transitive
description:
name: lints
sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
logging:
dependency: transitive
description:
name: logging
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
macros:
dependency: transitive
description:
name: macros
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
url: "https://pub.dev"
source: hosted
version: "0.1.2-main.4"
matcher:
dependency: transitive
description:
name: matcher
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev"
source: hosted
version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
url: "https://pub.dev"
source: hosted
version: "1.15.0"
mime:
dependency: transitive
description:
name: mime
sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
package_config:
dependency: transitive
description:
name: package_config
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
path:
dependency: transitive
description:
name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.9.0"
pool:
dependency: transitive
description:
name: pool
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8
url: "https://pub.dev"
source: hosted
version: "1.3.0"
scaler:
dependency: "direct main"
description:
name: scaler
sha256: d761b02e08445fa6617338c40903fb6cd2c77660000bafafe928b0287bd5f04a
url: "https://pub.dev"
source: hosted
version: "1.1.2+1"
shelf:
dependency: transitive
description:
name: shelf
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
url: "https://pub.dev"
source: hosted
version: "1.4.1"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd"
url: "https://pub.dev"
source: hosted
version: "1.3.4"
source_span:
dependency: transitive
description:
name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev"
source: hosted
version: "1.10.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev"
source: hosted
version: "1.11.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
version: "2.1.2"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
url: "https://pub.dev"
source: hosted
version: "0.7.2"
timing:
dependency: transitive
description:
name: timing
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.2"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
url: "https://pub.dev"
source: hosted
version: "14.2.5"
watcher:
dependency: transitive
description:
name: watcher
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web:
dependency: transitive
description:
name: web
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
url: "https://pub.dev"
source: hosted
version: "0.5.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42"
url: "https://pub.dev"
source: hosted
version: "2.4.5"
yaml:
dependency: transitive
description:
name: yaml
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
youtube_player_flutter:
dependency: "direct main"
description:
name: youtube_player_flutter
sha256: "72d487e1a1b9155a2dc9d448c137380791101a0ff623723195275ac275ac6942"
url: "https://pub.dev"
source: hosted
version: "8.1.2"
sdks:
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"

View File

@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 0.2.1
environment:
sdk: '>=2.19.2 <3.0.0'
sdk: '>=3.0.0 <4.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
@ -28,6 +28,10 @@ environment:
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
# Awesome plugins for local notifications
awesome_notifications_core: ^0.10.0 # use the latest core version available
awesome_notifications: ^0.10.0 # This version is managed by core plugin
flutter:
sdk: flutter
@ -36,9 +40,16 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
scaler: ^1.1.2+1
intl: ^0.18.0
youtube_player_flutter: ^8.1.2
intl: ^0.20.1
youtube_player_flutter: ^9.1.1
json_annotation: ^4.9.0
provider: ^6.1.2
scrollable_positioned_list: ^0.3.8
drift: ^2.22.1
flutter_expandable_fab: ^2.3.0
drift_flutter: ^0.2.2
dependency_overrides:
intl: ^0.20.1
flutter_launcher_name:
name: "SendTrain"
@ -52,9 +63,10 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.0.0
flutter_lints: ^5.0.0
build_runner: ^2.4.13
json_serializable: ^6.9.0
drift_dev: ^2.22.1
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View File

@ -1,11 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
void RegisterPlugins(flutter::PluginRegistry* registry) {
}

View File

@ -1,15 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter/plugin_registry.h>
// Registers Flutter plugins.
void RegisterPlugins(flutter::PluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View File

@ -1,23 +0,0 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)