diff --git a/trainerbox/android/app/src/main/AndroidManifest.xml b/trainerbox/android/app/src/main/AndroidManifest.xml index 36b6d34..092eddf 100644 --- a/trainerbox/android/app/src/main/AndroidManifest.xml +++ b/trainerbox/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,10 @@ + + + + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Trainerbox + TrainerBox CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -24,6 +24,10 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + NSPhotoLibraryUsageDescription + Diese App benötigt Zugriff auf die Galerie, um Bilder für Trainings hochzuladen. + NSCameraUsageDescription + Diese App benötigt Zugriff auf die Kamera, um Bilder für Trainings aufzunehmen. UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/trainerbox/lib/screens/search_tab.dart b/trainerbox/lib/screens/search_tab.dart index 81eaf08..7feac7b 100644 --- a/trainerbox/lib/screens/search_tab.dart +++ b/trainerbox/lib/screens/search_tab.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_storage/firebase_storage.dart'; +import 'package:image_picker/image_picker.dart'; +import 'dart:io'; import 'training_detail_screen.dart'; class SearchTab extends StatefulWidget { @@ -296,10 +299,36 @@ class _CreateTrainingDialogState extends State<_CreateTrainingDialog> { String? _title; String? _description; int? _duration; - String? _picture; - double? _rating; String? _year; bool _loading = false; + File? _imageFile; + final _picker = ImagePicker(); + + Future _pickImage() async { + final pickedFile = await _picker.pickImage(source: ImageSource.gallery); + if (pickedFile != null) { + setState(() { + _imageFile = File(pickedFile.path); + }); + } + } + + Future _uploadImage() async { + if (_imageFile == null) return null; + + final storageRef = FirebaseStorage.instance + .ref() + .child('training_images') + .child('${DateTime.now().millisecondsSinceEpoch}.jpg'); + + try { + final uploadTask = await storageRef.putFile(_imageFile!); + return await uploadTask.ref.getDownloadURL(); + } catch (e) { + print('Error uploading image: $e'); + return null; + } + } @override Widget build(BuildContext context) { @@ -311,6 +340,24 @@ class _CreateTrainingDialogState extends State<_CreateTrainingDialog> { child: Column( mainAxisSize: MainAxisSize.min, children: [ + if (_imageFile != null) + Container( + height: 200, + width: double.infinity, + margin: const EdgeInsets.only(bottom: 16), + decoration: BoxDecoration( + image: DecorationImage( + image: FileImage(_imageFile!), + fit: BoxFit.cover, + ), + ), + ), + ElevatedButton.icon( + onPressed: _pickImage, + icon: const Icon(Icons.image), + label: Text(_imageFile == null ? 'Bild auswählen' : 'Bild ändern'), + ), + const SizedBox(height: 16), DropdownButtonFormField( value: _category, items: widget.categories @@ -337,20 +384,6 @@ class _CreateTrainingDialogState extends State<_CreateTrainingDialog> { onChanged: (v) => _duration = int.tryParse(v), validator: (v) => v == null || int.tryParse(v) == null ? 'Zahl angeben' : null, ), - TextFormField( - decoration: const InputDecoration(labelText: 'Bild-URL (optional)'), - onChanged: (v) => _picture = v, - ), - TextFormField( - decoration: const InputDecoration(labelText: 'Bewertung (0-5)'), - keyboardType: TextInputType.number, - onChanged: (v) => _rating = double.tryParse(v), - validator: (v) { - final d = double.tryParse(v ?? ''); - if (d == null || d < 0 || d > 5) return '0-5 angeben'; - return null; - }, - ), TextFormField( decoration: const InputDecoration(labelText: 'Schwierigkeitslevel'), onChanged: (v) => _year = v, @@ -371,16 +404,26 @@ class _CreateTrainingDialogState extends State<_CreateTrainingDialog> { : () async { if (_formKey.currentState!.validate()) { setState(() => _loading = true); - await FirebaseFirestore.instance.collection('Training').add({ - 'category': _category, - 'title': _title, - 'description': _description, - 'duration': _duration, - 'picture': _picture, - 'rating overall': _rating, - 'year': _year, - }); - Navigator.pop(context); + try { + final imageUrl = await _uploadImage(); + await FirebaseFirestore.instance.collection('Training').add({ + 'category': _category, + 'title': _title, + 'description': _description, + 'duration': _duration, + 'picture': imageUrl, + 'rating overall': 0.0, + 'year': _year, + 'ratings': [], // Array für einzelne Bewertungen + }); + Navigator.pop(context); + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Fehler beim Erstellen: $e')), + ); + } finally { + setState(() => _loading = false); + } } }, child: _loading ? const CircularProgressIndicator() : const Text('Erstellen'), diff --git a/trainerbox/lib/screens/training_detail_screen.dart b/trainerbox/lib/screens/training_detail_screen.dart index 99f358c..cfd559f 100644 --- a/trainerbox/lib/screens/training_detail_screen.dart +++ b/trainerbox/lib/screens/training_detail_screen.dart @@ -1,11 +1,111 @@ import 'package:flutter/material.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; -class TrainingDetailScreen extends StatelessWidget { +class TrainingDetailScreen extends StatefulWidget { final String trainingId; const TrainingDetailScreen({super.key, required this.trainingId}); + @override + State createState() => _TrainingDetailScreenState(); +} + +class _TrainingDetailScreenState extends State { + double? _userRating; + bool _isLoading = false; + bool _isPlayer = false; + bool _userRoleChecked = false; + + @override + void initState() { + super.initState(); + _checkUserRole(); + } + + Future _checkUserRole() async { + final user = FirebaseAuth.instance.currentUser; + if (user == null) return; + + try { + final userDoc = await FirebaseFirestore.instance.collection('User').doc(user.uid).get(); + if (userDoc.exists) { + setState(() { + _isPlayer = userDoc.data()?['role'] == 'player'; + _userRoleChecked = true; + }); + } + } catch (e) { + print('Error checking user role: $e'); + } + } + + Future _submitRating(double rating) async { + if (!_isPlayer) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Nur Spieler können Übungen bewerten')), + ); + return; + } + + setState(() => _isLoading = true); + try { + final user = FirebaseAuth.instance.currentUser; + if (user == null) { + throw Exception('Nicht eingeloggt'); + } + + final trainingRef = FirebaseFirestore.instance.collection('Training').doc(widget.trainingId); + final trainingDoc = await trainingRef.get(); + + if (!trainingDoc.exists) { + throw Exception('Training nicht gefunden'); + } + + final data = trainingDoc.data() as Map; + List ratings = List.from(data['ratings'] ?? []); + + // Entferne alte Bewertung des Users falls vorhanden + ratings.removeWhere((r) => r['userId'] == user.uid); + + // Füge neue Bewertung hinzu + ratings.add({ + 'userId': user.uid, + 'rating': rating, + 'timestamp': DateTime.now().toIso8601String(), // Verwende ISO-String statt FieldValue + }); + + // Berechne neue Gesamtbewertung + double overallRating = 0; + if (ratings.isNotEmpty) { + overallRating = ratings.map((r) => (r['rating'] as num).toDouble()).reduce((a, b) => a + b) / ratings.length; + } + + // Aktualisiere das Dokument + await trainingRef.update({ + 'ratings': ratings, + 'rating overall': overallRating, + }); + + setState(() => _userRating = rating); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Bewertung erfolgreich gespeichert')), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Fehler beim Speichern der Bewertung: ${e.toString()}')), + ); + } + } finally { + if (mounted) { + setState(() => _isLoading = false); + } + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -13,7 +113,7 @@ class TrainingDetailScreen extends StatelessWidget { title: const Text('Training Details'), ), body: FutureBuilder( - future: FirebaseFirestore.instance.collection('Training').doc(trainingId).get(), + future: FirebaseFirestore.instance.collection('Training').doc(widget.trainingId).get(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); @@ -22,6 +122,20 @@ class TrainingDetailScreen extends StatelessWidget { return const Center(child: Text('Training nicht gefunden')); } final data = snapshot.data!.data() as Map; + + // Hole die Bewertung des aktuellen Users + final user = FirebaseAuth.instance.currentUser; + if (user != null && _userRating == null) { + final ratings = List.from(data['ratings'] ?? []); + final userRating = ratings.firstWhere( + (r) => r['userId'] == user.uid, + orElse: () => {'rating': null}, + )['rating']; + if (userRating != null) { + _userRating = (userRating as num).toDouble(); + } + } + return SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Column( @@ -31,9 +145,14 @@ class TrainingDetailScreen extends StatelessWidget { width: double.infinity, height: 200, color: Colors.grey[300], - child: const Center( - child: Icon(Icons.image, size: 64, color: Colors.grey), - ), + child: (data['picture'] is String && data['picture'] != '') + ? Image.network( + data['picture'], + fit: BoxFit.cover, + ) + : const Center( + child: Icon(Icons.image, size: 64, color: Colors.grey), + ), ), const SizedBox(height: 16), Text( @@ -58,11 +177,50 @@ class TrainingDetailScreen extends StatelessWidget { 'Level: ${data['year'] ?? '-'}', style: TextStyle(color: Colors.grey[600]), ), + const SizedBox(height: 16), + Row( + children: [ + const Icon(Icons.star, color: Colors.amber), + const SizedBox(width: 8), + Text( + 'Durchschnittliche Bewertung: ${(data['rating overall'] ?? 0.0).toStringAsFixed(1)}', + style: const TextStyle(fontSize: 16), + ), + ], + ), const SizedBox(height: 8), Text( - 'Bewertung: ${data['rating overall'] ?? '-'}', + 'Anzahl Bewertungen: ${(data['ratings'] ?? []).length}', style: TextStyle(color: Colors.grey[600]), ), + if (_userRoleChecked && _isPlayer) ...[ + const SizedBox(height: 16), + const Text( + 'Deine Bewertung:', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate(5, (index) { + return IconButton( + icon: Icon( + index < (_userRating ?? 0) ? Icons.star : Icons.star_border, + color: Colors.amber, + size: 32, + ), + onPressed: _isLoading + ? null + : () => _submitRating(index + 1.0), + ); + }), + ), + if (_isLoading) + const Padding( + padding: EdgeInsets.all(8.0), + child: Center(child: CircularProgressIndicator()), + ), + ], const SizedBox(height: 8), Text( 'Kategorie: ${data['category'] ?? '-'}', diff --git a/trainerbox/linux/flutter/generated_plugin_registrant.cc b/trainerbox/linux/flutter/generated_plugin_registrant.cc index e71a16d..64a0ece 100644 --- a/trainerbox/linux/flutter/generated_plugin_registrant.cc +++ b/trainerbox/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); + file_selector_plugin_register_with_registrar(file_selector_linux_registrar); } diff --git a/trainerbox/linux/flutter/generated_plugins.cmake b/trainerbox/linux/flutter/generated_plugins.cmake index 2e1de87..2db3c22 100644 --- a/trainerbox/linux/flutter/generated_plugins.cmake +++ b/trainerbox/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + file_selector_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/trainerbox/macos/Flutter/GeneratedPluginRegistrant.swift b/trainerbox/macos/Flutter/GeneratedPluginRegistrant.swift index 804ef2f..c313ce0 100644 --- a/trainerbox/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/trainerbox/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,11 +6,15 @@ import FlutterMacOS import Foundation import cloud_firestore +import file_selector_macos import firebase_auth import firebase_core +import firebase_storage func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin")) + FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) + FLTFirebaseStoragePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseStoragePlugin")) } diff --git a/trainerbox/pubspec.lock b/trainerbox/pubspec.lock index 3f1f3e5..4a289ff 100644 --- a/trainerbox/pubspec.lock +++ b/trainerbox/pubspec.lock @@ -73,6 +73,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + url: "https://pub.dev" + source: hosted + version: "0.3.4+2" cupertino_icons: dependency: "direct main" description: @@ -89,6 +97,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33" + url: "https://pub.dev" + source: hosted + version: "0.9.3+2" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: "271ab9986df0c135d45c3cdb6bd0faa5db6f4976d3e4b437cf7d0f258d941bfc" + url: "https://pub.dev" + source: hosted + version: "0.9.4+2" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b + url: "https://pub.dev" + source: hosted + version: "2.6.2" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b" + url: "https://pub.dev" + source: hosted + version: "0.9.3+4" firebase_auth: dependency: "direct main" description: @@ -137,6 +177,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.17.5" + firebase_storage: + dependency: "direct main" + description: + name: firebase_storage + sha256: "2ae478ceec9f458c1bcbf0ee3e0100e4e909708979e83f16d5d9fba35a5b42c1" + url: "https://pub.dev" + source: hosted + version: "11.7.7" + firebase_storage_platform_interface: + dependency: transitive + description: + name: firebase_storage_platform_interface + sha256: "4e18662e6a66e2e0e181c06f94707de06d5097d70cfe2b5141bf64660c5b5da9" + url: "https://pub.dev" + source: hosted + version: "5.1.22" + firebase_storage_web: + dependency: transitive + description: + name: firebase_storage_web + sha256: "3a44aacd38a372efb159f6fe36bb4a7d79823949383816457fd43d3d47602a53" + url: "https://pub.dev" + source: hosted + version: "3.9.7" flutter: dependency: "direct main" description: flutter @@ -146,10 +210,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "2.0.3" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e + url: "https://pub.dev" + source: hosted + version: "2.0.28" flutter_test: dependency: "direct dev" description: flutter @@ -160,6 +232,14 @@ packages: description: flutter source: sdk version: "0.0.0" + http: + dependency: transitive + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" http_parser: dependency: transitive description: @@ -168,6 +248,70 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + image_picker: + dependency: "direct main" + description: + name: image_picker + sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + sha256: "317a5d961cec5b34e777b9252393f2afbd23084aa6e60fcf601dcf6341b9ebeb" + url: "https://pub.dev" + source: hosted + version: "0.8.12+23" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100" + url: "https://pub.dev" + source: hosted + version: "0.8.12+2" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "34a65f6740df08bbbeb0a1abd8e6d32107941fd4868f67a507b25601651022c9" + url: "https://pub.dev" + source: hosted + version: "0.2.1+2" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1" + url: "https://pub.dev" + source: hosted + version: "0.2.1+2" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0" + url: "https://pub.dev" + source: hosted + version: "2.10.1" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" intl: dependency: transitive description: @@ -204,10 +348,10 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "2.1.1" matcher: dependency: transitive description: @@ -232,6 +376,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.16.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -248,6 +408,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + provider: + dependency: "direct main" + description: + name: provider + sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" + url: "https://pub.dev" + source: hosted + version: "6.1.5" simple_gesture_detector: dependency: transitive description: @@ -350,5 +518,5 @@ packages: source: hosted version: "0.5.1" sdks: - dart: ">=3.7.2 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.7.0-0 <4.0.0" + flutter: ">=3.27.0" diff --git a/trainerbox/pubspec.yaml b/trainerbox/pubspec.yaml index 88e1a1f..a1e2acf 100644 --- a/trainerbox/pubspec.yaml +++ b/trainerbox/pubspec.yaml @@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ^3.7.2 + sdk: '>=3.2.3 <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 @@ -33,11 +33,14 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.8 - firebase_core: ^2.32.0 + cupertino_icons: ^1.0.2 + firebase_core: ^2.24.2 table_calendar: ^3.0.9 - cloud_firestore: ^4.17.3 - firebase_auth: ^4.17.4 + cloud_firestore: ^4.13.6 + firebase_auth: ^4.15.3 + firebase_storage: ^11.5.6 + image_picker: ^1.0.7 + provider: ^6.1.1 dev_dependencies: flutter_test: @@ -48,7 +51,7 @@ 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: ^5.0.0 + flutter_lints: ^2.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/trainerbox/windows/flutter/generated_plugin_registrant.cc b/trainerbox/windows/flutter/generated_plugin_registrant.cc index bf6d21a..ec1e463 100644 --- a/trainerbox/windows/flutter/generated_plugin_registrant.cc +++ b/trainerbox/windows/flutter/generated_plugin_registrant.cc @@ -7,14 +7,20 @@ #include "generated_plugin_registrant.h" #include +#include #include #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { CloudFirestorePluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("CloudFirestorePluginCApi")); + FileSelectorWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSelectorWindows")); FirebaseAuthPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi")); FirebaseCorePluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); + FirebaseStoragePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseStoragePluginCApi")); } diff --git a/trainerbox/windows/flutter/generated_plugins.cmake b/trainerbox/windows/flutter/generated_plugins.cmake index b83b40a..767b528 100644 --- a/trainerbox/windows/flutter/generated_plugins.cmake +++ b/trainerbox/windows/flutter/generated_plugins.cmake @@ -4,8 +4,10 @@ list(APPEND FLUTTER_PLUGIN_LIST cloud_firestore + file_selector_windows firebase_auth firebase_core + firebase_storage ) list(APPEND FLUTTER_FFI_PLUGIN_LIST