Android possible

main
joschy2002 2025-06-14 15:24:23 +02:00
parent b7ec276347
commit 753d6af4c5
12 changed files with 2659 additions and 48 deletions

View File

@ -10,16 +10,16 @@ plugins {
android {
namespace = "com.example.trainerbox"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileSdk = 35 // Aktualisiert auf SDK 35 für Plugin-Kompatibilität
ndkVersion = "27.0.12077973" // Aktualisiert auf NDK 27.0.12077973 für Plugin-Kompatibilität
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
jvmTarget = "1.8"
}
defaultConfig {
@ -27,21 +27,49 @@ android {
applicationId = "com.example.trainerbox"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
minSdk = 21 // Mindest-SDK-Version für moderne Android-Geräte
targetSdk = 35 // Aktualisiert auf SDK 35
versionCode = 1
versionName = "1.0"
multiDexEnabled = true // Für Apps mit vielen Dependencies
}
buildTypes {
release {
isMinifyEnabled = true // Code-Optimierung für Release-Builds
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
// Unterstützung für verschiedene Bildschirmgrößen
splits {
abi {
isEnable = true
reset()
include("armeabi-v7a", "arm64-v8a", "x86_64")
isUniversalApk = true
}
}
}
flutter {
source = "../.."
}
dependencies {
implementation(platform("com.google.firebase:firebase-bom:32.7.2"))
implementation("com.google.firebase:firebase-analytics")
implementation("com.google.firebase:firebase-auth")
implementation("com.google.firebase:firebase-firestore")
implementation("com.android.support:multidex:1.0.3")
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("com.google.firebase:firebase-core:21.1.1")
implementation("com.google.firebase:firebase-auth:22.3.1")
implementation("com.google.firebase:firebase-firestore:24.10.2")
}

View File

@ -1,12 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
android:label="TrainerBox"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/ic_launcher"
android:label="TrainerBox"
android:usesCleartextTraffic="true">
<activity
android:name=".MainActivity"
android:exported="true"

View File

@ -1,4 +1,8 @@
{
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"flutter": {
"platforms": {
"android": {

View File

@ -0,0 +1,4 @@
{
"indexes": [],
"fieldOverrides": []
}

View File

@ -0,0 +1,24 @@
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Benutzer können ihre eigenen Daten lesen und schreiben
match /User/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
// Trainer können ihre eigenen Trainings verwalten
match /Training/{trainingId} {
allow read: if request.auth != null;
allow write: if request.auth != null &&
get(/databases/$(database)/documents/User/$(request.auth.uid)).data.role == 'trainer';
}
// Übungen können von allen gelesen werden
match /Exercise/{exerciseId} {
allow read: if request.auth != null;
allow write: if request.auth != null &&
get(/databases/$(database)/documents/User/$(request.auth.uid)).data.role == 'trainer';
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
@ -54,6 +55,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"

View File

@ -35,8 +35,6 @@
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
@ -49,5 +47,11 @@
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIRequiresFullScreen</key>
<true/>
<key>UIStatusBarHidden</key>
<false/>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
</dict>
</plist>

View File

@ -23,41 +23,96 @@ class _LoginScreenState extends State<LoginScreen> {
Future<void> _submit() async {
setState(() { _loading = true; _error = null; });
try {
print('Login-Versuch gestartet...'); // Debug
if (_isLogin) {
print('Login-Modus: E-Mail: ${_emailController.text.trim()}'); // Debug
// Login
UserCredential cred = await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passwordController.text.trim(),
);
// Firestore-Check
final uid = cred.user!.uid;
final userDoc = await FirebaseFirestore.instance.collection('User').doc(uid).get();
if (userDoc.exists) {
widget.onLoginSuccess();
} else {
setState(() { _error = 'Kein Benutzerprofil in der Datenbank gefunden!'; });
await FirebaseAuth.instance.signOut();
try {
UserCredential cred = await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passwordController.text.trim(),
);
print('Firebase Auth erfolgreich: ${cred.user?.uid}'); // Debug
// Firestore-Check
final uid = cred.user!.uid;
print('Firestore-Check für UID: $uid'); // Debug
final userDoc = await FirebaseFirestore.instance.collection('User').doc(uid).get();
print('Firestore-Dokument existiert: ${userDoc.exists}'); // Debug
if (userDoc.exists) {
print('Login erfolgreich, Benutzer gefunden'); // Debug
widget.onLoginSuccess();
} else {
print('Kein Benutzerprofil in Firestore gefunden'); // Debug
setState(() { _error = 'Kein Benutzerprofil in der Datenbank gefunden!'; });
await FirebaseAuth.instance.signOut();
}
} catch (e) {
print('Fehler beim Firebase Auth: $e'); // Debug
rethrow;
}
} else {
print('Registrierungs-Modus: E-Mail: ${_emailController.text.trim()}'); // Debug
// Registrierung
UserCredential cred = await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passwordController.text.trim(),
);
// User-Datensatz in Firestore anlegen
final uid = cred.user!.uid;
await FirebaseFirestore.instance.collection('User').doc(uid).set({
'email': _emailController.text.trim(),
'name': _nameController.text.trim(),
'role': _isTrainer ? 'trainer' : 'player',
'createdAt': FieldValue.serverTimestamp(),
});
widget.onLoginSuccess();
try {
UserCredential cred = await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passwordController.text.trim(),
);
print('Firebase Auth Registrierung erfolgreich: ${cred.user?.uid}'); // Debug
// User-Datensatz in Firestore anlegen
final uid = cred.user!.uid;
print('Erstelle Firestore-Dokument für UID: $uid'); // Debug
await FirebaseFirestore.instance.collection('User').doc(uid).set({
'email': _emailController.text.trim(),
'name': _nameController.text.trim(),
'role': _isTrainer ? 'trainer' : 'player',
'createdAt': FieldValue.serverTimestamp(),
});
print('Firestore-Dokument erstellt'); // Debug
widget.onLoginSuccess();
} catch (e) {
print('Fehler bei der Registrierung: $e'); // Debug
rethrow;
}
}
} on FirebaseAuthException catch (e) {
setState(() { _error = e.message ?? 'Fehler'; });
print('FirebaseAuthException: ${e.code} - ${e.message}'); // Debug
String errorMessage;
switch (e.code) {
case 'user-not-found':
errorMessage = 'Kein Benutzer mit dieser E-Mail-Adresse gefunden.';
break;
case 'wrong-password':
errorMessage = 'Falsches Passwort.';
break;
case 'invalid-email':
errorMessage = 'Ungültige E-Mail-Adresse.';
break;
case 'user-disabled':
errorMessage = 'Dieser Account wurde deaktiviert.';
break;
case 'email-already-in-use':
errorMessage = 'Diese E-Mail-Adresse wird bereits verwendet.';
break;
case 'weak-password':
errorMessage = 'Das Passwort ist zu schwach.';
break;
case 'operation-not-allowed':
errorMessage = 'Diese Operation ist nicht erlaubt.';
break;
default:
errorMessage = 'Ein Fehler ist aufgetreten: ${e.message}';
}
setState(() { _error = errorMessage; });
} catch (e) {
setState(() { _error = 'Unbekannter Fehler'; });
print('Unerwarteter Fehler: $e'); // Debug
setState(() { _error = 'Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.'; });
} finally {
setState(() { _loading = false; });
}

View File

@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
class Responsive {
static bool isMobile(BuildContext context) =>
MediaQuery.of(context).size.width < 600;
static bool isTablet(BuildContext context) =>
MediaQuery.of(context).size.width >= 600 &&
MediaQuery.of(context).size.width < 1200;
static bool isDesktop(BuildContext context) =>
MediaQuery.of(context).size.width >= 1200;
static double getWidth(BuildContext context) =>
MediaQuery.of(context).size.width;
static double getHeight(BuildContext context) =>
MediaQuery.of(context).size.height;
static double getScaledWidth(BuildContext context, double percentage) =>
getWidth(context) * (percentage / 100);
static double getScaledHeight(BuildContext context, double percentage) =>
getHeight(context) * (percentage / 100);
static EdgeInsets getPadding(BuildContext context) {
if (isMobile(context)) {
return const EdgeInsets.all(16.0);
} else if (isTablet(context)) {
return const EdgeInsets.all(24.0);
} else {
return const EdgeInsets.all(32.0);
}
}
static double getFontSize(BuildContext context, double baseSize) {
if (isMobile(context)) {
return baseSize;
} else if (isTablet(context)) {
return baseSize * 1.2;
} else {
return baseSize * 1.4;
}
}
static double getIconSize(BuildContext context, double baseSize) {
if (isMobile(context)) {
return baseSize;
} else if (isTablet(context)) {
return baseSize * 1.2;
} else {
return baseSize * 1.4;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -13,10 +13,10 @@ packages:
dependency: transitive
description:
name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev"
source: hosted
version: "2.12.0"
version: "2.13.0"
boolean_selector:
dependency: transitive
description:
@ -101,10 +101,10 @@ packages:
dependency: transitive
description:
name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.2"
version: "1.3.3"
file_selector_linux:
dependency: transitive
description:
@ -340,10 +340,10 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
url: "https://pub.dev"
source: hosted
version: "10.0.8"
version: "10.0.9"
leak_tracker_flutter_testing:
dependency: transitive
description:
@ -537,10 +537,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev"
source: hosted
version: "14.3.1"
version: "15.0.0"
web:
dependency: transitive
description: