drift-impl #3

Merged
jshbrmn merged 6 commits from drift-impl into main 2024-12-10 00:19:54 -08:00
34 changed files with 5361 additions and 533 deletions

143
.gitignore vendored
View File

@ -9,6 +9,7 @@
.history .history
.svn/ .svn/
migrate_working_dir/ migrate_working_dir/
bkp
# IntelliJ related # IntelliJ related
*.iml *.iml
@ -44,3 +45,145 @@ app.*.map.json
/android/app/debug /android/app/debug
/android/app/profile /android/app/profile
/android/app/release /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. # This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc. # 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: version:
revision: 9944297138845a94256f1cf37beb88ff9a8e811a revision: "dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668"
channel: stable channel: "stable"
project_type: app project_type: app
@ -13,26 +13,26 @@ project_type: app
migration: migration:
platforms: platforms:
- platform: root - platform: root
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: android - platform: android
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: ios - platform: ios
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: linux - platform: linux
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: macos - platform: macos
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: web - platform: web
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: windows - platform: windows
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
# User provided section # 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);",
"});});}"
]
}
}

2
android/.gitignore vendored
View File

@ -7,7 +7,7 @@ gradle-wrapper.jar
GeneratedPluginRegistrant.java GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore. # 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 key.properties
**/*.keystore **/*.keystore
**/*.jks **/*.jks

View File

@ -1,71 +1,44 @@
def localProperties = new Properties() plugins {
def localPropertiesFile = rootProject.file('local.properties') id "com.android.application"
if (localPropertiesFile.exists()) { id "kotlin-android"
localPropertiesFile.withReader('UTF-8') { reader -> // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
localProperties.load(reader) 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 { android {
compileSdkVersion flutter.compileSdkVersion namespace = "com.example.sendtrain"
ndkVersion flutter.ndkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8
} }
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = JavaVersion.VERSION_1_8
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
} }
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // 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. // 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. // For more information, see: https://flutter.dev/to/review-gradle-config.
minSdkVersion flutter.minSdkVersion minSdk = flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion targetSdk = flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger() versionCode = flutter.versionCode
versionName flutterVersionName versionName = flutter.versionName
} }
buildTypes { buildTypes {
release { release {
// TODO: Add your own signing config for the release build. // TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works. // Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug signingConfig = signingConfigs.debug
} }
} }
} }
flutter { flutter {
source '../..' source = "../.."
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
} }

View File

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

View File

@ -1,13 +1,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android">
package="com.sendtrain.sendtrain"> <application
<application android:label="sendtrain"
android:label="SendTrain"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
@ -31,4 +31,15 @@
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
</application> </application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest> </manifest>

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" <manifest xmlns:android="http://schemas.android.com/apk/res/android">
package="com.sendtrain.sendtrain">
<!-- The INTERNET permission is required for development. Specifically, <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. 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 { allprojects {
repositories { repositories {
google() google()
@ -18,12 +5,12 @@ allprojects {
} }
} }
rootProject.buildDir = '../build' rootProject.buildDir = "../build"
subprojects { subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}" project.buildDir = "${rootProject.buildDir}/${project.name}"
} }
subprojects { subprojects {
project.evaluationDependsOn(':app') project.evaluationDependsOn(":app")
} }
tasks.register("clean", Delete) { 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.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true

View File

@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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") includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
def properties = new Properties()
assert localPropertiesFile.exists() repositories {
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } google()
mavenCentral()
gradlePluginPortal()
}
}
def flutterSdkPath = properties.getProperty("flutter.sdk") plugins {
assert flutterSdkPath != null, "flutter.sdk not set in local.properties" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 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.
}
}

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,5 +1,7 @@
import 'package:drift/drift.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:sendtrain/database.dart';
import 'package:sendtrain/models/activity_timer_model.dart'; import 'package:sendtrain/models/activity_timer_model.dart';
import 'package:sendtrain/screens/activities_screen.dart'; import 'package:sendtrain/screens/activities_screen.dart';
import 'package:sendtrain/screens/sessions_screen.dart'; import 'package:sendtrain/screens/sessions_screen.dart';
@ -44,7 +46,7 @@ class _AppState extends State<App> {
body: Padding( body: Padding(
padding: const EdgeInsets.fromLTRB(0, 50, 0, 0), padding: const EdgeInsets.fromLTRB(0, 50, 0, 0),
child: <Widget>[ child: <Widget>[
const SessionsScreen(), SessionsScreen(),
const ActivitiesScreen(), const ActivitiesScreen(),
Container( Container(
alignment: Alignment.center, alignment: Alignment.center,
@ -90,6 +92,82 @@ class _AppState extends State<App> {
} }
void main() { void main() {
// 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));
runApp( runApp(
ChangeNotifierProvider( ChangeNotifierProvider(
create: (context) => ActivityTimerModel(), create: (context) => ActivityTimerModel(),

View File

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

View File

@ -1,52 +1,113 @@
import 'package:drift/drift.dart' hide Column;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sendtrain/database.dart';
import '../widgets/session_card.dart'; import '../widgets/session_card.dart';
class SessionsScreen extends StatelessWidget { 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
List<Widget> previousSessions = return FutureBuilder<List<Session>>(
List.generate(10, (i) => const SessionCard(state: 1, type: 1)); future: getSessions(),
Widget upcomingSession = const SessionCard(state: 2); builder: (context, snapshot) {
Widget currentSession = const SessionCard(); 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);
return Column( List<Widget> previousSessions = List.generate(pending.length, (i) => SessionCard(type: 1, session: pending.elementAt(i)));
crossAxisAlignment: CrossAxisAlignment.start, Widget upcomingSession =
children: <Widget>[ SessionCard(session: upcoming);
const Padding( Widget currentSession = SessionCard(session: current);
padding: EdgeInsets.fromLTRB(15, 5, 0, 0),
child: Text( database.close();
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
'Current:')), return Column(
currentSession, crossAxisAlignment: CrossAxisAlignment.start,
const Padding( children: <Widget>[
padding: EdgeInsets.fromLTRB(15, 30, 0, 0), const Padding(
child: Text( padding: EdgeInsets.fromLTRB(15, 5, 0, 0),
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold), child: Text(
'Upcoming:')), style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
upcomingSession, 'Current:')),
const Padding( currentSession,
padding: EdgeInsets.fromLTRB(15, 30, 0, 0), const Padding(
child: Text( padding: EdgeInsets.fromLTRB(15, 30, 0, 0),
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold), child: Text(
'Previous:')), style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
SizedBox( 'Upcoming:')),
width: double.infinity, upcomingSession,
height: 160, const Padding(
child: GridView.count( padding: EdgeInsets.fromLTRB(15, 30, 0, 0),
padding: const EdgeInsets.fromLTRB(15, 10, 0, 0), child: Text(
scrollDirection: Axis.horizontal, style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
crossAxisSpacing: 5, 'Previous:')),
mainAxisSpacing: 5, SizedBox(
crossAxisCount: 1, width: double.infinity,
children: previousSessions)) height: 160,
// Flexible( child: GridView.count(
// child: ListView( padding: const EdgeInsets.fromLTRB(15, 10, 0, 0),
// scrollDirection: Axis.vertical, scrollDirection: Axis.horizontal,
// children: previousSessions, crossAxisSpacing: 5,
// )), mainAxisSpacing: 5,
], crossAxisCount: 1,
); 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

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

View File

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

View File

@ -3,23 +3,24 @@ import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart'; import 'package:intl/date_symbol_data_local.dart';
import 'package:sendtrain/classes/activity_action.dart'; import 'package:sendtrain/classes/activity_action.dart';
import 'package:sendtrain/classes/media.dart'; import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/database.dart' hide ActivityAction;
import 'package:sendtrain/models/activity_model.dart'; import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/models/session_model.dart'; import 'package:sendtrain/models/session_model.dart';
import 'package:sendtrain/widgets/session_view.dart'; import 'package:sendtrain/widgets/session_view.dart';
class SessionCard extends StatelessWidget { class SessionCard extends StatelessWidget {
final int state;
final int type; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
initializeDateFormatting('en'); initializeDateFormatting('en');
final DateFormat dateFormat = DateFormat('yyyy-MM-dd'); final DateFormat dateFormat = DateFormat('yyyy-MM-dd');
Color color = (state == 0) Color color = (session.status == SessionStatus.started)
? const Color(0xff3A5FB6) ? Theme.of(context).colorScheme.primaryContainer
: ThemeData.dark(useMaterial3: true).colorScheme.surfaceBright; : Theme.of(context).colorScheme.surfaceContainerLow;
// place holder until we can retrieve real data // place holder until we can retrieve real data
final data = SessionModel( final data = SessionModel(
@ -40,7 +41,8 @@ class SessionCard extends StatelessWidget {
ActivityAction( ActivityAction(
id: 1, id: 1,
title: '1, 3, 5', title: '1, 3, 5',
description: 'Move between the first, third, and fifth rungs, alternating hands. Rest and alternate sides, to start', description:
'Move between the first, third, and fifth rungs, alternating hands. Rest and alternate sides, to start',
media: [ media: [
Media( Media(
id: 1, id: 1,
@ -77,7 +79,8 @@ class SessionCard extends StatelessWidget {
ActivityAction( ActivityAction(
id: 1, id: 1,
title: 'Attempt Climb', 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.', 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: [
Media( Media(
id: 1, id: 1,
@ -114,7 +117,8 @@ class SessionCard extends StatelessWidget {
ActivityAction( ActivityAction(
id: 1, id: 1,
title: 'Long Pulls', 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.', 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: [
Media( Media(
id: 1, id: 1,
@ -126,7 +130,8 @@ class SessionCard extends StatelessWidget {
id: 1, id: 1,
reference: 'sZVAEy9UmoY', reference: 'sZVAEy9UmoY',
type: 'youtube', type: 'youtube',
description: 'Principals of Grip gains, and related protocols') description:
'Principals of Grip gains, and related protocols')
], ],
activityActionSet: Set( activityActionSet: Set(
type: 'alternating', type: 'alternating',
@ -186,7 +191,7 @@ class SessionCard extends StatelessWidget {
.animate(animation); .animate(animation);
return SlideTransition( return SlideTransition(
position: custom, position: custom,
child: Dialog.fullscreen(child: SessionView(data: data))); child: Dialog.fullscreen(child: SessionView(data: data, session: session)));
}, },
barrierDismissible: true, barrierDismissible: true,
barrierLabel: '', barrierLabel: '',
@ -212,8 +217,32 @@ class SessionCard extends StatelessWidget {
BorderRadius.all(Radius.elliptical(10, 10)), BorderRadius.all(Radius.elliptical(10, 10)),
), ),
)), )),
title: Text(maxLines: 1, data.title), title: Text(maxLines: 1, session.title),
subtitle: Text(maxLines: 1, dateFormat.format(data.date)), 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( ListTile(
contentPadding: const EdgeInsets.fromLTRB(15, 0, 15, 15), contentPadding: const EdgeInsets.fromLTRB(15, 0, 15, 15),
@ -221,14 +250,14 @@ class SessionCard extends StatelessWidget {
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w300), style: const TextStyle(fontWeight: FontWeight.w300),
data.content), session.content),
), ),
], ],
)), )),
); );
} else { } else {
return Card( return Card(
color: const Color.fromARGB(125, 0, 0, 0), color: color,
child: InkWell( child: InkWell(
// overlayColor: MaterialStateColor(Colors.deepPurple as int), // overlayColor: MaterialStateColor(Colors.deepPurple as int),
splashColor: Colors.deepPurple, splashColor: Colors.deepPurple,
@ -247,7 +276,7 @@ class SessionCard extends StatelessWidget {
return SlideTransition( return SlideTransition(
position: custom, position: custom,
child: child:
Dialog.fullscreen(child: SessionView(data: data))); Dialog.fullscreen(child: SessionView(data: data, session: session)));
}, },
barrierDismissible: true, barrierDismissible: true,
barrierLabel: '', barrierLabel: '',
@ -274,11 +303,11 @@ class SessionCard extends StatelessWidget {
ListTile( ListTile(
title: Text( title: Text(
maxLines: 3, maxLines: 3,
data.title, session.title,
textAlign: TextAlign.center), textAlign: TextAlign.center),
subtitle: Text( subtitle: Text(
maxLines: 1, maxLines: 1,
dateFormat.format(data.date), dateFormat.format(session.date as DateTime),
textAlign: TextAlign.center), textAlign: TextAlign.center),
), ),
]))))); ])))));

View File

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

View File

@ -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,12 +0,0 @@
//
// Generated file. Do not edit.
//
import FlutterMacOS
import Foundation
import flutter_inappwebview_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
}

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

@ -41,6 +41,9 @@ dependencies:
json_annotation: ^4.9.0 json_annotation: ^4.9.0
provider: ^6.1.2 provider: ^6.1.2
scrollable_positioned_list: ^0.3.8 scrollable_positioned_list: ^0.3.8
drift: ^2.22.1
flutter_expandable_fab: ^2.3.0
drift_flutter: ^0.2.2
flutter_launcher_name: flutter_launcher_name:
name: "SendTrain" name: "SendTrain"
@ -57,6 +60,7 @@ dev_dependencies:
flutter_lints: ^5.0.0 flutter_lints: ^5.0.0
build_runner: ^2.4.13 build_runner: ^2.4.13
json_serializable: ^6.9.0 json_serializable: ^6.9.0
drift_dev: ^2.22.1
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec

View File

@ -1,14 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
}

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,24 +0,0 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
flutter_inappwebview_windows
)
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)