37 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
88021cdade full screen activity view 2024-11-24 18:02:17 -05:00
55a435718d updated pub 2024-11-24 17:22:27 -05:00
df5f24ef16 update to run modern version 2024-11-24 15:21:14 -05:00
66 changed files with 6446 additions and 843 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 17
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,14 +5,14 @@ allprojects {
}
}
rootProject.buildDir = '../build'
rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
project.evaluationDependsOn(":app")
}
task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

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,17 +1,117 @@
import 'package:sendtrain/classes/media.dart';
class ActivityAction {
ActivityAction(
{required this.id,
int id;
String title;
String description;
Set activityActionSet;
List<Media>? media;
ActivityAction({
required this.id,
required this.title,
required this.description,
this.repetitions,
this.time,
this.weight});
required this.activityActionSet,
this.media,
});
final int id;
final String title;
final String description;
final int? repetitions;
final int? weight;
// in milliseconds
final int? time;
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 {
String type;
int total;
int rest;
Reps reps;
Set({
required this.type,
required this.total,
required this.rest,
required this.reps,
});
}
class Reps {
String type;
List<int> tempo;
List<int> amounts;
List<int> weights = [];
int? rest;
Reps({
required this.type,
required this.tempo,
required this.amounts,
required this.weights,
this.rest,
});
}

View File

@ -1,4 +1,3 @@
import 'package:flutter/material.dart';
class Media {
Media(

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();
@ -21,12 +21,12 @@ class _ActivitiesHeaderState extends State<ActivitiesHeader> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 10),
return const Padding(
padding: EdgeInsets.only(bottom: 10),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const <Widget>[
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
child: Text(

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,35 +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: 'exercise',
description: 'description',
repetitions: 5,
time: 5200)));
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

@ -9,7 +9,7 @@ class MediaCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
YoutubePlayerController _controller = YoutubePlayerController(
YoutubePlayerController controller = YoutubePlayerController(
initialVideoId: media.reference,
flags: const YoutubePlayerFlags(
autoPlay: false, mute: true, showLiveFullscreenButton: false));
@ -31,7 +31,7 @@ class MediaCard extends StatelessWidget {
return Image(image: NetworkImage(media.reference));
} else if (media.type == "youtube") {
return YoutubePlayer(
controller: _controller,
controller: controller,
aspectRatio: 16 / 9,
);
}

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.background;
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,86 +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: '1, 3, 5 - left hand start',
title: '1, 3, 5',
description:
'move from rungs 1, 3, and 5, alternating arms on each movement',
repetitions: 1),
ActivityAction(
id: 2,
title: 'Rest',
description: 'rest for alotted time',
time: 15000),
ActivityAction(
id: 3,
title: '1, 3, 5 - right hand start',
description:
'move from rungs 1, 3, and 5, alternating arms on each movement',
repetitions: 1),
ActivityAction(
id: 4,
title: 'Rest',
description: 'rest for alotted time',
time: 300000),
ActivityAction(
id: 5,
title: 'Repeat',
description: 'repeat cycle',
repetitions: 5)
'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: 300000,
reps: Reps(
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: 'attempt boulder',
description: 'attempt boulder project',
repetitions: 1),
ActivityAction(
id: 2,
title: 'Rest',
description: 'rest for alotted time',
time: 300000),
ActivityAction(
id: 5,
title: 'Repeat',
description: 'repeat cycle',
repetitions: 5)
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: 'standard',
total: 10,
rest: 300000,
reps: Reps(
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: 'pull ups',
description: 'pull ups',
repetitions: 5,
weight: 100),
ActivityAction(
id: 2,
title: 'Rest',
description: 'rest for alotted time',
time: 300000),
ActivityAction(
id: 5,
title: 'Repeat',
description: 'repeat cycle',
repetitions: 5)
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: 'alternating',
total: 5,
rest: 5000,
reps: Reps(
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']),
],
@ -149,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),
@ -163,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: '',
@ -189,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),
@ -198,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),
@ -224,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: '',
@ -251,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(
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,221 +0,0 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
url: "https://pub.dev"
source: hosted
version: "2.10.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
characters:
dependency: transitive
description:
name: characters
sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
url: "https://pub.dev"
source: hosted
version: "1.2.1"
clock:
dependency: transitive
description:
name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
source: hosted
version: "1.1.1"
collection:
dependency: transitive
description:
name: collection
sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
url: "https://pub.dev"
source: hosted
version: "1.17.0"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
url: "https://pub.dev"
source: hosted
version: "1.0.5"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
source: hosted
version: "1.3.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"
intl:
dependency: "direct main"
description:
name: intl
sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6
url: "https://pub.dev"
source: hosted
version: "0.18.0"
js:
dependency: transitive
description:
name: js
sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
url: "https://pub.dev"
source: hosted
version: "0.6.5"
lints:
dependency: transitive
description:
name: lints
sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
matcher:
dependency: transitive
description:
name: matcher
sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72"
url: "https://pub.dev"
source: hosted
version: "0.12.13"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev"
source: hosted
version: "0.2.0"
meta:
dependency: transitive
description:
name: meta
sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
url: "https://pub.dev"
source: hosted
version: "1.8.0"
path:
dependency: transitive
description:
name: path
sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
url: "https://pub.dev"
source: hosted
version: "1.8.2"
scaler:
dependency: "direct main"
description:
name: scaler
sha256: d761b02e08445fa6617338c40903fb6cd2c77660000bafafe928b0287bd5f04a
url: "https://pub.dev"
source: hosted
version: "1.1.2+1"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
url: "https://pub.dev"
source: hosted
version: "1.9.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
url: "https://pub.dev"
source: hosted
version: "1.11.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
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: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206
url: "https://pub.dev"
source: hosted
version: "0.4.16"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
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: ">=2.19.2 <3.0.0"
flutter: ">=3.0.0"

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,8 +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"
@ -51,7 +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

1
sendtrain/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,40 @@
plugins {
id("com.android.application")
}
android {
namespace = "com.sendtrain.sendtrain"
compileSdk = 34
defaultConfig {
applicationId = "com.sendtrain.sendtrain"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
dependencies {
implementation("com.android.support:appcompat-v7:28.0.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("com.android.support.test:runner:1.0.2")
androidTestImplementation("com.android.support.test.espresso:espresso-core:3.0.2")
}

21
sendtrain/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,25 @@
package com.sendtrain.sendtrain;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.sendtrain.sendtrain", appContext.getPackageName());
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Sendtrain" />
</manifest>

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -0,0 +1,10 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.Sendtrain" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryDark">@color/purple_700</item>
<item name="colorAccent">@color/teal_200</item>
<!-- Customize your theme here. -->
</style>
</resources>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">SendTrain</string>
</resources>

View File

@ -0,0 +1,10 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.Sendtrain" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryDark">@color/purple_700</item>
<item name="colorAccent">@color/teal_200</item>
<!-- Customize your theme here. -->
</style>
</resources>

View File

@ -0,0 +1,17 @@
package com.sendtrain.sendtrain;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

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)