Compare commits
15 Commits
old_master
...
master
Author | SHA1 | Date |
---|---|---|
Thomas Hassenstein | bc0cba7286 | |
Thomas Hassenstein | 388e2deb61 | |
Thomas Hassenstein | 06e62dc7e5 | |
Thomas Hassenstein | d409005e4c | |
Thomas Hassenstein | 83b02eca1a | |
Thomas Hassenstein | e3d9c8666c | |
Thomas Hassenstein | 684d7fc15c | |
Thomas Hassenstein | b89ff0770e | |
Thomas Hassenstein | 918b51258d | |
Thomas Hassenstein | 5d7eb7ec3b | |
Thomas Hassenstein | 6fc8322202 | |
Thomas Hassenstein | daec63e58b | |
Thomas Hassenstein | f7f1d8855a | |
Thomas Hassenstein | 81105d0067 | |
Thomas Hassenstein | d1a83c8a9d |
25
README.md
25
README.md
|
@ -1,18 +1,27 @@
|
|||
# kochkomplize
|
||||
Helfer zur Digitalisierung von alten Kochrezepten.
|
||||
|
||||
**Bild/Skizze:**
|
||||
Ein Bild wie die Applikation aussehen soll findet man unter assets/images mit dem Namen skizze.png
|
||||
|
||||
**Allgemeine Beschreibung:**
|
||||
Kochkomplize solle eine Koch- und Backapp sein die auch bei der Digitalisierung von alten Handgeschrieben Rezepten mit Hilfe von Texterkennung helfen soll.
|
||||
**Bild/Ablauf:**
|
||||
![Startseite](assets/images/Kochkomplize_Startseite.PNG)
|
||||
![Rezept Übersicht](assets/images/Kochkomplize_Rezept_Overview.PNG)
|
||||
![Rezept anlegen](assets/images/Kochkomplize_Rezept_anlegen.PNG)
|
||||
![Rezept löschen](assets/images/Kochkomplize_Löschen.PNG)
|
||||
|
||||
**Funktionalität:**
|
||||
1. Die App soll grundsätzlich im Browser als auch auf Mobilen Endgeräten laufen. (entweder iOS oder Android)
|
||||
2. Es soll möglich sein Rezepte abzuspeichern und neue anzulegen.
|
||||
3. Man soll Bilder für Rezepte hochladen können.
|
||||
4. Es soll möglich sein alte handgeschriebene Rezepte mit Texterkennung auf Bildern zu importieren und zu formatieren.
|
||||
5. Rezepte sollen beliebig sortiert und ergänzt werden können.
|
||||
6. Falls der Umfang der App nicht umfangreich genug sein sollte dann noch die Möglichkeit von anderen Nutzern Rezepte anzuschauen.
|
||||
4. Es soll möglich sein alte handgeschriebene Texte Bildern zu importieren.
|
||||
5. Rezepte sollen gelöscht werden können.
|
||||
|
||||
6. Bonus: Texterkennung wenn die Packages es auch für Web zulassen.
|
||||
|
||||
**Eingesetzte Technologien:**
|
||||
Flutter
|
||||
Dart
|
||||
Firebase
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,28 +1,5 @@
|
|||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at https://dart.dev/lints.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
|
|
|
@ -45,7 +45,7 @@ android {
|
|||
applicationId "com.example.kochkomplize"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||
minSdkVersion flutter.minSdkVersion
|
||||
minSdkVersion 21
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"project_info": {
|
||||
"project_number": "460915828512",
|
||||
"firebase_url": "https://kochkomplize-default-rtdb.europe-west1.firebasedatabase.app",
|
||||
"project_id": "kochkomplize",
|
||||
"storage_bucket": "kochkomplize.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:460915828512:android:73914cecf1d06ff0771945",
|
||||
"android_client_info": {
|
||||
"package_name": "com.example.kochkomplize"
|
||||
}
|
||||
},
|
||||
"oauth_client": [],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyBzHwDZscVmyTuyJx9darx_8KkhpiCcNNQ"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": []
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "2"
|
||||
}
|
|
@ -30,4 +30,5 @@
|
|||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
</manifest>
|
||||
|
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
Binary file not shown.
After Width: | Height: | Size: 71 KiB |
Binary file not shown.
After Width: | Height: | Size: 325 KiB |
Binary file not shown.
After Width: | Height: | Size: 681 KiB |
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>API_KEY</key>
|
||||
<string>AIzaSyDn1MhJq7gjUtyFJaewgC425jwJmAVCrDk</string>
|
||||
<key>GCM_SENDER_ID</key>
|
||||
<string>460915828512</string>
|
||||
<key>PLIST_VERSION</key>
|
||||
<string>1</string>
|
||||
<key>BUNDLE_ID</key>
|
||||
<string>com.example.kochkomplize</string>
|
||||
<key>PROJECT_ID</key>
|
||||
<string>kochkomplize</string>
|
||||
<key>STORAGE_BUCKET</key>
|
||||
<string>kochkomplize.appspot.com</string>
|
||||
<key>IS_ADS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_ANALYTICS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_APPINVITE_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_GCM_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_SIGNIN_ENABLED</key>
|
||||
<true></true>
|
||||
<key>GOOGLE_APP_ID</key>
|
||||
<string>1:460915828512:ios:ec4610516aa6bb0d771945</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -45,5 +45,9 @@
|
|||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Wir benötigen Zugriff auf Ihre Fotos, um Bilder auszuwählen.</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Wir benötigen Zugriff auf Ihre Kamera, um Fotos aufzunehmen.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"file_generated_by": "FlutterFire CLI",
|
||||
"purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory",
|
||||
"GOOGLE_APP_ID": "1:460915828512:ios:ec4610516aa6bb0d771945",
|
||||
"FIREBASE_PROJECT_ID": "kochkomplize",
|
||||
"GCM_SENDER_ID": "460915828512"
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
// File generated by FlutterFire CLI.
|
||||
// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/foundation.dart'
|
||||
show defaultTargetPlatform, kIsWeb, TargetPlatform;
|
||||
|
||||
class DefaultFirebaseOptions {
|
||||
static FirebaseOptions get currentPlatform {
|
||||
if (kIsWeb) {
|
||||
return web;
|
||||
}
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.android:
|
||||
return android;
|
||||
case TargetPlatform.iOS:
|
||||
return ios;
|
||||
case TargetPlatform.macOS:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for macos - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
case TargetPlatform.windows:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for windows - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
case TargetPlatform.linux:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for linux - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
default:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions are not supported for this platform.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static const FirebaseOptions web = FirebaseOptions(
|
||||
apiKey: 'AIzaSyAACZYqWGQre2FqeXxC5qid1E0K2yuXD5Q',
|
||||
appId: '1:460915828512:web:4db5d6cfa1e11481771945',
|
||||
messagingSenderId: '460915828512',
|
||||
projectId: 'kochkomplize',
|
||||
authDomain: 'kochkomplize.firebaseapp.com',
|
||||
databaseURL: 'https://kochkomplize-default-rtdb.europe-west1.firebasedatabase.app',
|
||||
storageBucket: 'kochkomplize.appspot.com',
|
||||
measurementId: 'G-0W73YW74KY',
|
||||
);
|
||||
|
||||
static const FirebaseOptions android = FirebaseOptions(
|
||||
apiKey: 'AIzaSyBzHwDZscVmyTuyJx9darx_8KkhpiCcNNQ',
|
||||
appId: '1:460915828512:android:73914cecf1d06ff0771945',
|
||||
messagingSenderId: '460915828512',
|
||||
projectId: 'kochkomplize',
|
||||
databaseURL: 'https://kochkomplize-default-rtdb.europe-west1.firebasedatabase.app',
|
||||
storageBucket: 'kochkomplize.appspot.com',
|
||||
);
|
||||
|
||||
static const FirebaseOptions ios = FirebaseOptions(
|
||||
apiKey: 'AIzaSyDn1MhJq7gjUtyFJaewgC425jwJmAVCrDk',
|
||||
appId: '1:460915828512:ios:ec4610516aa6bb0d771945',
|
||||
messagingSenderId: '460915828512',
|
||||
projectId: 'kochkomplize',
|
||||
databaseURL: 'https://kochkomplize-default-rtdb.europe-west1.firebasedatabase.app',
|
||||
storageBucket: 'kochkomplize.appspot.com',
|
||||
iosBundleId: 'com.example.kochkomplize',
|
||||
);
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'menu_content.dart';
|
||||
|
||||
//todo implement logik for image import
|
||||
|
||||
class ImportContent implements MenuContent {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Hier wird der Inhalt für Menüpunkt 2 erstellt
|
||||
return const Text('Inhalt für Menüpunkt 2 - Import');
|
||||
}
|
||||
}
|
|
@ -1,9 +1,18 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'startpage.dart';
|
||||
import 'recipeformpage.dart';
|
||||
import 'menu.dart';
|
||||
import 'menu_content.dart';
|
||||
import 'recipes.dart';
|
||||
import 'recipesoverview.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'firebase_options.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform,
|
||||
);
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
|
@ -35,18 +44,32 @@ class MyHomePage extends StatefulWidget {
|
|||
class _MyHomePageState extends State<MyHomePage> {
|
||||
MenuContent? _selectedMenuContent;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedMenuContent = Startpage(); // Initialisiere mit Startseite
|
||||
}
|
||||
|
||||
void _onMenuItemSelected(String menuItem) {
|
||||
setState(() {
|
||||
switch (menuItem) {
|
||||
case 'Meine Rezepte':
|
||||
_selectedMenuContent = RecipesContent();
|
||||
_selectedMenuContent = RecipesOverview();
|
||||
break;
|
||||
case 'Startseite':
|
||||
_selectedMenuContent = Startpage();
|
||||
break;
|
||||
// case 'Texterkennung Bild':
|
||||
// _selectedMenuContent = TextScan();
|
||||
// break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _importImage() {
|
||||
//todo
|
||||
void _addRecipe() {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (context) => const Recipeformpage()),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -58,11 +81,14 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||
),
|
||||
drawer: Menu(onMenuItemSelected: _onMenuItemSelected),
|
||||
body: _selectedMenuContent?.build(context) ?? Container(),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _importImage,
|
||||
tooltip: 'Import',
|
||||
floatingActionButton: _selectedMenuContent is RecipesOverview
|
||||
? FloatingActionButton(
|
||||
onPressed: _addRecipe,
|
||||
tooltip: 'Rezept hinzufügen',
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
)
|
||||
: null, // Kein Button für andere Seiten
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,11 +20,11 @@ class Menu extends StatelessWidget {
|
|||
child: Text('Hauptmenü', style: TextStyle(color: Colors.black, fontSize: 20.0)),
|
||||
),
|
||||
),
|
||||
_buildListTile(context, 'Startseite'),
|
||||
_buildListTile(context, 'Meine Rezepte'),
|
||||
_buildListTile(context, 'Rezept importieren'),
|
||||
_buildListTile(context, 'Texterkennung Bild'),
|
||||
_buildListTile(context, '(Beta) Rezepte finden'),
|
||||
// Füge hier weitere ListTiles für zusätzliche Menüpunkte hinzu
|
||||
//_buildListTile(context, 'Rezept importieren'),
|
||||
//_buildListTile(context, 'Texterkennung Bild'),
|
||||
//_buildListTile(context, '(Beta) Rezepte finden'),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class RecipeDetailPage extends StatelessWidget {
|
||||
final Map<String, dynamic> recipe;
|
||||
|
||||
const RecipeDetailPage({Key? key, required this.recipe}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(recipe["title"]),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.network(recipe["image"]),
|
||||
Text(recipe["title"]),
|
||||
// Weitere Details oder Inhalte für die Detailseite
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:firebase_database/firebase_database.dart';
|
||||
import 'package:firebase_storage/firebase_storage.dart';
|
||||
|
||||
class RecipeDetailPage extends StatelessWidget {
|
||||
final String recipeId;
|
||||
|
||||
const RecipeDetailPage({Key? key, required this.recipeId, required Map recipe}) : super(key: key);
|
||||
|
||||
Future<void> _showDeleteDialog(BuildContext context) async {
|
||||
bool confirmDelete = await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Rezept löschen'),
|
||||
content: const Text('Sind Sie sicher, dass Sie dieses Rezept löschen möchten?'),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: const Text('Abbrechen'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: const Text('Löschen'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
) ?? false; // Dialog-Abbruch auch als "Nicht löschen" werten
|
||||
|
||||
if (confirmDelete) {
|
||||
await _deleteRecipe(context);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _deleteRecipe(BuildContext context) async {
|
||||
DatabaseReference recipeRef = FirebaseDatabase.instance.ref('rezepte/$recipeId');
|
||||
DataSnapshot snapshot = await recipeRef.get();
|
||||
if (snapshot.exists) {
|
||||
Map<dynamic, dynamic> recipe = snapshot.value as Map<dynamic, dynamic>;
|
||||
|
||||
// Lösche die Bilder aus dem Storage, wenn URLs vorhanden sind
|
||||
for (final key in ['bild1', 'bild2', 'bild3']) {
|
||||
final String? imageUrl = recipe[key];
|
||||
if (imageUrl != null && imageUrl.isNotEmpty) {
|
||||
try {
|
||||
await FirebaseStorage.instance.refFromURL(imageUrl).delete();
|
||||
} catch (e) {
|
||||
debugPrint("Fehler beim Löschen von $imageUrl: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lösche den Rezepteintrag aus der Datenbank
|
||||
await recipeRef.remove();
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Rezept konnte nicht gefunden werden.')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Rezept Details'),
|
||||
),
|
||||
body: FutureBuilder<DataSnapshot>(
|
||||
future: FirebaseDatabase.instance.ref('rezepte/$recipeId').get(),
|
||||
builder: (BuildContext context, AsyncSnapshot<DataSnapshot> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
if (!snapshot.hasData || snapshot.data?.value == null) {
|
||||
return const Center(child: Text('Rezept nicht gefunden'));
|
||||
}
|
||||
|
||||
var recipeData = snapshot.data!.value as Map<dynamic, dynamic>;
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
if (recipeData['bild1'] != null)
|
||||
Image.network(recipeData['bild1']),
|
||||
Text(recipeData['titel'] ?? 'Kein Titel', style: Theme.of(context).textTheme.titleLarge),
|
||||
Text(recipeData['beschreibung1'] ?? 'Keine Beschreibung'),
|
||||
Text(recipeData['beschreibung2'] ?? 'Keine Beschreibung'),
|
||||
Text(recipeData['beschreibung3'] ?? 'Keine Beschreibung'),
|
||||
if (recipeData['bild2'] != null && recipeData['bild2'].isNotEmpty)
|
||||
Image.network(recipeData['bild2']),
|
||||
if (recipeData['bild3'] != null && recipeData['bild3'].isNotEmpty)
|
||||
Image.network(recipeData['bild3']),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () => _showDeleteDialog(context),
|
||||
tooltip: 'Löschen',
|
||||
child: const Icon(Icons.delete),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:firebase_storage/firebase_storage.dart';
|
||||
import 'package:firebase_database/firebase_database.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
class Recipeformpage extends StatefulWidget {
|
||||
const Recipeformpage({super.key});
|
||||
|
||||
@override
|
||||
RecipeformpageState createState() => RecipeformpageState();
|
||||
}
|
||||
|
||||
class RecipeformpageState extends State<Recipeformpage> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
String title = '';
|
||||
String description1 = '';
|
||||
String description2 = '';
|
||||
String description3 = '';
|
||||
String image1 = '';
|
||||
String image2 = '';
|
||||
String image3 = '';
|
||||
|
||||
final ImagePicker _picker = ImagePicker();
|
||||
|
||||
Future<void> _pickAndUploadImage(String fieldName) async {
|
||||
final XFile? pickedFile = await _picker.pickImage(source: ImageSource.gallery);
|
||||
if (pickedFile != null) {
|
||||
// Lese die Datei als Byte-Array ein
|
||||
Uint8List fileBytes = await pickedFile.readAsBytes();
|
||||
String fileName = basename(pickedFile.path);
|
||||
Reference storageRef = FirebaseStorage.instance.ref().child('rezeptbilder/$fileName');
|
||||
|
||||
// Starte den Upload-Prozess
|
||||
UploadTask uploadTask = storageRef.putData(fileBytes);
|
||||
|
||||
// Warte auf das Ende des Uploads
|
||||
await uploadTask.whenComplete(() {});
|
||||
|
||||
// Erhalte die Download-URL
|
||||
String downloadUrl = await storageRef.getDownloadURL();
|
||||
|
||||
setState(() {
|
||||
// Aktualisiere den entsprechenden Bild-URL-Zustand
|
||||
switch (fieldName) {
|
||||
case 'image1':
|
||||
image1 = downloadUrl;
|
||||
break;
|
||||
case 'image2':
|
||||
image2 = downloadUrl;
|
||||
break;
|
||||
case 'image3':
|
||||
image3 = downloadUrl;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _saveForm(BuildContext context) async {
|
||||
|
||||
if (_formKey.currentState!.validate()) {
|
||||
_formKey.currentState!.save();
|
||||
|
||||
DatabaseReference databaseRef = FirebaseDatabase.instance.ref().child('rezepte');
|
||||
String? rezeptId = databaseRef.push().key; // Eindeutiger Schlüssel für das neue Rezept
|
||||
|
||||
databaseRef.child(rezeptId!).set({
|
||||
'titel': title,
|
||||
'beschreibung1': description1,
|
||||
'beschreibung2': description2,
|
||||
'beschreibung3': description3,
|
||||
'bild1': image1,
|
||||
'bild2': image2,
|
||||
'bild3': image3,
|
||||
});
|
||||
|
||||
Navigator.of(context).pop(); // Schließt das Formular nach dem Speichern
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("Neues Rezept"),
|
||||
),
|
||||
body: Stack(
|
||||
children: <Widget>[
|
||||
Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: <Widget>[
|
||||
TextFormField(
|
||||
decoration: const InputDecoration(labelText: 'Titel'),
|
||||
onSaved: (value) => title = value ?? '',
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Bitte einen Titel eingeben';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
TextFormField(
|
||||
decoration: const InputDecoration(labelText: 'Zutaten: '),
|
||||
onSaved: (value) => description1 = value ?? '',
|
||||
),
|
||||
TextFormField(
|
||||
decoration: const InputDecoration(labelText: 'Anleitung: '),
|
||||
onSaved: (value) => description2 = value ?? '',
|
||||
),
|
||||
TextFormField(
|
||||
decoration: const InputDecoration(labelText: 'Anmerkungen: '),
|
||||
onSaved: (value) => description3 = value ?? '',
|
||||
),
|
||||
_imageField('Anzeigebild', 'image1'),
|
||||
_imageField('Handgeschriebenes Rezept', 'image2'),
|
||||
_imageField('Textscan', 'image3'),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
child: FloatingActionButton(
|
||||
onPressed: () => _saveForm(context),
|
||||
tooltip: 'Rezept speichern',
|
||||
child: const Icon(Icons.save),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
Widget _imageField(String label, String fieldName) {
|
||||
String? imageUrl;
|
||||
switch (fieldName) {
|
||||
case 'image1':
|
||||
imageUrl = image1;
|
||||
break;
|
||||
case 'image2':
|
||||
imageUrl = image2;
|
||||
break;
|
||||
case 'image3':
|
||||
imageUrl = image3;
|
||||
break;
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (imageUrl != null && imageUrl.isNotEmpty)
|
||||
Image.network(imageUrl, width: 100, height: 100),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
decoration: InputDecoration(labelText: label),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.photo_camera),
|
||||
onPressed: () => _pickAndUploadImage(fieldName),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'menu_content.dart';
|
||||
import 'recipe.dart';
|
||||
|
||||
class RecipesContent implements MenuContent {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Datenquelle exemplarisch definiert
|
||||
List<Map<String, dynamic>> recipes = [
|
||||
{"title": "Rezept 1", "image": "assets/images/burger.png"},
|
||||
{"title": "Rezept 2", "image": "assets/images/spaghetti.png"},
|
||||
// weitere Rezepte
|
||||
];
|
||||
|
||||
return ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: recipes.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => RecipeDetailPage(recipe: recipes[index]),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Image.network(recipes[index]["image"], width: 100, height: 100),
|
||||
Text(recipes[index]["title"]),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:firebase_database/firebase_database.dart';
|
||||
import 'menu_content.dart';
|
||||
import 'recipedetailpage.dart';
|
||||
|
||||
class RecipesOverview implements MenuContent {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamBuilder(
|
||||
stream: FirebaseDatabase.instance.ref('rezepte').onValue,
|
||||
builder: (context, AsyncSnapshot<DatabaseEvent> snapshot) {
|
||||
if (snapshot.hasData && !snapshot.hasError && snapshot.data!.snapshot.value != null) {
|
||||
Map<dynamic, dynamic> recipes = snapshot.data!.snapshot.value as Map<dynamic, dynamic>;
|
||||
return ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: recipes.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
String key = recipes.keys.elementAt(index);
|
||||
var value = recipes[key];
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => RecipeDetailPage(recipeId: key, recipe: const {},),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Image.network(value["bild1"], width: 100, height: 100),
|
||||
Text(value["titel"]),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'menu_content.dart';
|
||||
|
||||
class Startpage implements MenuContent {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1 / 1, // Für ein quadratisches Logo
|
||||
child: Image.asset('assets/images/Logo.png', fit: BoxFit.contain),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
// textscan.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'menu_content.dart';
|
||||
|
||||
//todo Logik implementieren
|
||||
//links
|
||||
//https://davidserrano.io/text-recognition-in-flutter-create-ocr-scanner-app
|
||||
//https://www.google.com/search?q=textscan+flutter&oq=textscan+flutter&gs_lcrp=EgZjaHJvbWUyBggAEEUYOTIICAEQABgWGB7SAQgzMDg0ajBqN6gCALACAA&sourceid=chrome&ie=UTF-8
|
||||
|
||||
class TextScanContent implements MenuContent {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Hier wird der Inhalt für Menüpunkt 3 erstellt
|
||||
return const Text('Inhalt für Menüpunkt 3 - Text Scan');
|
||||
}
|
||||
}
|
344
pubspec.lock
344
pubspec.lock
|
@ -1,6 +1,14 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_flutterfire_internals:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _flutterfire_internals
|
||||
sha256: f5628cd9c92ed11083f425fd1f8f1bc60ecdda458c81d73b143aeda036c35fe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.16"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -41,6 +49,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cross_file
|
||||
sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.3+8"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -57,6 +73,118 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
file_selector_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_linux
|
||||
sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.2+1"
|
||||
file_selector_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_macos
|
||||
sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.3+3"
|
||||
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: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.3+1"
|
||||
firebase_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_core
|
||||
sha256: "96607c0e829a581c2a483c658f04e8b159964c3bae2730f73297070bc85d40bb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.24.2"
|
||||
firebase_core_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_platform_interface
|
||||
sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
firebase_core_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_web
|
||||
sha256: d585bdf3c656c3f7821ba1bd44da5f13365d22fcecaf5eb75c4295246aaa83c0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.10.0"
|
||||
firebase_database:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_database
|
||||
sha256: "8568ad41f9312ab1f162f70c1e3e7cb7420b8bc8d07e4d543e575bb0cb41f8a5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.4.0"
|
||||
firebase_database_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_database_platform_interface
|
||||
sha256: "4366ade2390f8799a317bb13af29c2a1fdfc84f4d04372094756b86a6cbfd305"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.5+16"
|
||||
firebase_database_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_database_web
|
||||
sha256: "4920a83b917493b37fd408cbb01c289ef8a422d9ed48982f908a9850290262f9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.3+16"
|
||||
firebase_storage:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_storage
|
||||
sha256: "75e6cb6bed65138b5bbd86bfd7cf9bc9a175fb0c31aacc400e9203df117ffbe6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.6.0"
|
||||
firebase_storage_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_storage_platform_interface
|
||||
sha256: "545a3a8edf337850403bb0fa03c8074a53deb87c0107d19755c77a82ce07919e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.3"
|
||||
firebase_storage_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_storage_web
|
||||
sha256: ee6870ff79aa304b8996ba18a4aefe1e8b3fc31fd385eab6574180267aa8d393
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.17"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -70,11 +198,128 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.17"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
google_mlkit_commons:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_mlkit_commons
|
||||
sha256: "046586b381cdd139f7f6a05ad6998f7e339d061bd70158249907358394b5f496"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.1"
|
||||
google_mlkit_text_recognition:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_mlkit_text_recognition
|
||||
sha256: d484de2a10961a6f0ff8b54cc92b71bfbb0e65509be0903edca0e1f9256ca4c2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
image_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: image_picker
|
||||
sha256: "26222b01a0c9a2c8fe02fc90b8208bd3325da5ed1f4a2acabf75939031ac0bdd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.7"
|
||||
image_picker_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_android
|
||||
sha256: "1a27bf4cc0330389cebe465bab08fe6dec97e44015b4899637344bb7297759ec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.9+2"
|
||||
image_picker_for_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_for_web
|
||||
sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
image_picker_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_ios
|
||||
sha256: eac0a62104fa12feed213596df0321f57ce5a572562f72a68c4ff81e9e4caacf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.9"
|
||||
image_picker_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_linux
|
||||
sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1+1"
|
||||
image_picker_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_macos
|
||||
sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1+1"
|
||||
image_picker_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_platform_interface
|
||||
sha256: fa4e815e6fcada50e35718727d83ba1c92f1edf95c0b4436554cec301b56233b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.9.3"
|
||||
image_picker_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_windows
|
||||
sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1+1"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -107,14 +352,86 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
path:
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
path:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.4"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -168,6 +485,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.1"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -184,5 +509,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.2.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
sdks:
|
||||
dart: ">=3.2.0 <4.0.0"
|
||||
flutter: ">=3.10.0"
|
||||
|
|
11
pubspec.yaml
11
pubspec.yaml
|
@ -1,7 +1,5 @@
|
|||
name: kochkomplize
|
||||
description: "A new Flutter project."
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
description: "Kochhilfe mit Rezepten und Texterkennung"
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
version: 1.0.0+1
|
||||
|
@ -13,6 +11,12 @@ dependencies:
|
|||
flutter:
|
||||
sdk: flutter
|
||||
cupertino_icons: ^1.0.2
|
||||
firebase_core: ^2.24.2
|
||||
firebase_database: ^10.4.0
|
||||
firebase_storage: ^11.6.0
|
||||
path_provider: ^2.1.2
|
||||
image_picker: ^1.0.7
|
||||
path: ^1.8.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -23,5 +27,6 @@ flutter:
|
|||
uses-material-design: true
|
||||
assets:
|
||||
- assets/images/
|
||||
- assets/database/Kochkomplize.db
|
||||
|
||||
|
||||
|
|
|
@ -1,50 +1,108 @@
|
|||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility in the flutter_test package. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:kochkomplize/main.dart';
|
||||
import 'package:kochkomplize/menu.dart';
|
||||
|
||||
Widget buildMenu() {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Menu(onMenuItemSelected: (menuItem) {}),
|
||||
),
|
||||
);
|
||||
}
|
||||
import 'package:kochkomplize/recipeformpage.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Check menu text', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(buildMenu());
|
||||
|
||||
testWidgets('Menu renders correctly', (WidgetTester tester) async {
|
||||
void onMenuItemSelectedMock(String item) {}
|
||||
|
||||
// Verwenden Sie einen GlobalKey, um auf den Scaffold zuzugreifen
|
||||
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(home: Scaffold(key: scaffoldKey, drawer: Menu(onMenuItemSelected: onMenuItemSelectedMock))));
|
||||
|
||||
// Öffnen Sie den Drawer programmatisch
|
||||
scaffoldKey.currentState!.openDrawer();
|
||||
await tester.pumpAndSettle(); // Warten, bis die Animation abgeschlossen ist
|
||||
|
||||
// Überprüfen Sie nun, ob die Elemente im Drawer vorhanden sind
|
||||
expect(find.byType(Drawer), findsOneWidget);
|
||||
expect(find.text('Hauptmenü'), findsOneWidget);
|
||||
expect(find.text('Startseite'), findsOneWidget);
|
||||
expect(find.text('Meine Rezepte'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Check number of list items', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const MyApp());
|
||||
expect(find.byType(GestureDetector), findsNWidgets(3));
|
||||
testWidgets('Tapping each menu item calls onMenuItemSelected with correct item', (WidgetTester tester) async {
|
||||
String selectedMenuItem = '';
|
||||
|
||||
void onMenuItemSelectedMock(String item) {
|
||||
selectedMenuItem = item;
|
||||
}
|
||||
|
||||
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(home: Scaffold(key: scaffoldKey, drawer: Menu(onMenuItemSelected: onMenuItemSelectedMock))));
|
||||
|
||||
scaffoldKey.currentState!.openDrawer();
|
||||
await tester.pumpAndSettle(); // Warten, bis die Animation abgeschlossen ist
|
||||
|
||||
// Tippen Sie auf das Menüelement 'Startseite' und überprüfen Sie die Funktionalität
|
||||
await tester.tap(find.text('Startseite'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(selectedMenuItem, 'Startseite');
|
||||
|
||||
// Wiederholen Sie den Vorgang für 'Meine Rezepte'
|
||||
scaffoldKey.currentState!.openDrawer();
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Meine Rezepte'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(selectedMenuItem, 'Meine Rezepte');
|
||||
|
||||
// Hier können Sie weitere Tests für andere Menüpunkte hinzufügen
|
||||
});
|
||||
|
||||
// testWidgets('Check display of recipe titles', (WidgetTester tester) async {
|
||||
// await tester.pumpWidget(const MyApp());
|
||||
// expect(find.text('Rezept 1'), findsOneWidget);
|
||||
// expect(find.text('Rezept 2'), findsOneWidget);
|
||||
// });
|
||||
//
|
||||
// testWidgets('Check display of recipe images', (WidgetTester tester) async {
|
||||
// await tester.pumpWidget(const MyApp());
|
||||
// expect(find.byType(Image), findsNWidgets(2));
|
||||
// });
|
||||
//
|
||||
// testWidgets('Check navigation to recipe detail page', (WidgetTester tester) async {
|
||||
// await tester.pumpWidget(const MyApp());
|
||||
// await tester.tap(find.text('Rezept 1'));
|
||||
// await tester.pumpAndSettle(); // Warte darauf, dass die Navigation abgeschlossen ist
|
||||
// expect(find.text('Details für Rezept 1'), findsOneWidget);
|
||||
// });
|
||||
testWidgets('Drawer closes after an item is selected', (WidgetTester tester) async {
|
||||
void onMenuItemSelectedMock(String item) {}
|
||||
|
||||
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(home: Scaffold(key: scaffoldKey, drawer: Menu(onMenuItemSelected: onMenuItemSelectedMock))));
|
||||
|
||||
scaffoldKey.currentState!.openDrawer();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Startseite'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Überprüfen Sie, ob der Drawer geschlossen ist
|
||||
expect(scaffoldKey.currentState!.isDrawerOpen, isFalse);
|
||||
});
|
||||
|
||||
testWidgets('Drawer header renders correctly', (WidgetTester tester) async {
|
||||
// Erstellen Sie eine Mock-Funktion, die einen String als Parameter erwartet
|
||||
void onMenuItemSelectedMock(String item) {}
|
||||
|
||||
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(key: scaffoldKey, drawer: Menu(onMenuItemSelected: onMenuItemSelectedMock))
|
||||
));
|
||||
|
||||
scaffoldKey.currentState!.openDrawer();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Überprüfen Sie, ob der DrawerHeader vorhanden ist
|
||||
expect(find.byType(DrawerHeader), findsOneWidget);
|
||||
expect(find.text('Hauptmenü'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Recipeformpage has form fields', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const MaterialApp(home: Recipeformpage()));
|
||||
|
||||
// Prüfen Sie, ob die Formularfelder vorhanden sind
|
||||
expect(find.byType(TextFormField), findsWidgets);
|
||||
// Prüfen Sie den Speichern-Button
|
||||
expect(find.byType(FloatingActionButton), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('MyHomePage renders correctly', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const MaterialApp(home: MyHomePage(title: 'Kochkomplize')));
|
||||
|
||||
// Überprüfen Sie, ob der Titel korrekt angezeigt wird
|
||||
expect(find.text('Kochkomplize'), findsOneWidget);
|
||||
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue