Compare commits
3 Commits
master
...
old_master
Author | SHA1 | Date |
---|---|---|
Thomas Hassenstein | ff4c06fa2d | |
Thomas Hassenstein | e458c5da3f | |
Thomas Hassenstein | 21cec1e6c5 |
31
README.md
31
README.md
|
@ -1,27 +1,18 @@
|
||||||
# kochkomplize
|
# kochkomplize
|
||||||
Helfer zur Digitalisierung von alten Kochrezepten.
|
|
||||||
|
|
||||||
**Bild/Ablauf:**
|
**Bild/Skizze:**
|
||||||
![Startseite](assets/images/Kochkomplize_Startseite.PNG)
|
Ein Bild wie die Applikation aussehen soll findet man unter assets/images mit dem Namen skizze.png
|
||||||
![Rezept Übersicht](assets/images/Kochkomplize_Rezept_Overview.PNG)
|
|
||||||
![Rezept anlegen](assets/images/Kochkomplize_Rezept_anlegen.PNG)
|
**Allgemeine Beschreibung:**
|
||||||
![Rezept löschen](assets/images/Kochkomplize_Löschen.PNG)
|
Kochkomplize solle eine Koch- und Backapp sein die auch bei der Digitalisierung von alten Handgeschrieben Rezepten mit Hilfe von Texterkennung helfen soll.
|
||||||
|
|
||||||
**Funktionalität:**
|
**Funktionalität:**
|
||||||
1. Die App soll grundsätzlich im Browser als auch auf Mobilen Endgeräten laufen. (entweder iOS oder Android)
|
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.
|
2. Es soll möglich sein Rezepte abzuspeichern und neue anzulegen.
|
||||||
3. Man soll Bilder für Rezepte hochladen können.
|
3. Man soll Bilder für Rezepte hochladen können.
|
||||||
4. Es soll möglich sein alte handgeschriebene Texte Bildern zu importieren.
|
4. Es soll möglich sein alte handgeschriebene Rezepte mit Texterkennung auf Bildern zu importieren und zu formatieren.
|
||||||
5. Rezepte sollen gelöscht werden können.
|
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.
|
||||||
6. Bonus: Texterkennung wenn die Packages es auch für Web zulassen.
|
|
||||||
|
|
||||||
**Eingesetzte Technologien:**
|
|
||||||
Flutter
|
|
||||||
Dart
|
|
||||||
Firebase
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,28 @@
|
||||||
|
# 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
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
linter:
|
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:
|
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"
|
applicationId "com.example.kochkomplize"
|
||||||
// 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://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||||
minSdkVersion 21
|
minSdkVersion flutter.minSdkVersion
|
||||||
targetSdkVersion flutter.targetSdkVersion
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
{
|
|
||||||
"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,5 +30,4 @@
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
</application>
|
</application>
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 64 KiB |
Binary file not shown.
Before Width: | Height: | Size: 76 KiB |
Binary file not shown.
Before Width: | Height: | Size: 71 KiB |
Binary file not shown.
Before Width: | Height: | Size: 325 KiB |
Binary file not shown.
Before Width: | Height: | Size: 681 KiB |
|
@ -1,30 +0,0 @@
|
||||||
<?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,9 +45,5 @@
|
||||||
<true/>
|
<true/>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<true/>
|
<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>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
// 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',
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
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,18 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'startpage.dart';
|
|
||||||
import 'recipeformpage.dart';
|
|
||||||
import 'menu.dart';
|
import 'menu.dart';
|
||||||
import 'menu_content.dart';
|
import 'menu_content.dart';
|
||||||
import 'recipesoverview.dart';
|
import 'recipes.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());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,32 +35,18 @@ class MyHomePage extends StatefulWidget {
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
class _MyHomePageState extends State<MyHomePage> {
|
||||||
MenuContent? _selectedMenuContent;
|
MenuContent? _selectedMenuContent;
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_selectedMenuContent = Startpage(); // Initialisiere mit Startseite
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onMenuItemSelected(String menuItem) {
|
void _onMenuItemSelected(String menuItem) {
|
||||||
setState(() {
|
setState(() {
|
||||||
switch (menuItem) {
|
switch (menuItem) {
|
||||||
case 'Meine Rezepte':
|
case 'Meine Rezepte':
|
||||||
_selectedMenuContent = RecipesOverview();
|
_selectedMenuContent = RecipesContent();
|
||||||
break;
|
break;
|
||||||
case 'Startseite':
|
|
||||||
_selectedMenuContent = Startpage();
|
|
||||||
break;
|
|
||||||
// case 'Texterkennung Bild':
|
|
||||||
// _selectedMenuContent = TextScan();
|
|
||||||
// break;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addRecipe() {
|
void _importImage() {
|
||||||
Navigator.of(context).push(
|
//todo
|
||||||
MaterialPageRoute(builder: (context) => const Recipeformpage()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -81,14 +58,11 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||||
),
|
),
|
||||||
drawer: Menu(onMenuItemSelected: _onMenuItemSelected),
|
drawer: Menu(onMenuItemSelected: _onMenuItemSelected),
|
||||||
body: _selectedMenuContent?.build(context) ?? Container(),
|
body: _selectedMenuContent?.build(context) ?? Container(),
|
||||||
floatingActionButton: _selectedMenuContent is RecipesOverview
|
floatingActionButton: FloatingActionButton(
|
||||||
? FloatingActionButton(
|
onPressed: _importImage,
|
||||||
onPressed: _addRecipe,
|
tooltip: 'Import',
|
||||||
tooltip: 'Rezept hinzufügen',
|
|
||||||
child: const Icon(Icons.add),
|
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)),
|
child: Text('Hauptmenü', style: TextStyle(color: Colors.black, fontSize: 20.0)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_buildListTile(context, 'Startseite'),
|
|
||||||
_buildListTile(context, 'Meine Rezepte'),
|
_buildListTile(context, 'Meine Rezepte'),
|
||||||
//_buildListTile(context, 'Rezept importieren'),
|
_buildListTile(context, 'Rezept importieren'),
|
||||||
//_buildListTile(context, 'Texterkennung Bild'),
|
_buildListTile(context, 'Texterkennung Bild'),
|
||||||
//_buildListTile(context, '(Beta) Rezepte finden'),
|
_buildListTile(context, '(Beta) Rezepte finden'),
|
||||||
|
// Füge hier weitere ListTiles für zusätzliche Menüpunkte hinzu
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
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
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,110 +0,0 @@
|
||||||
// 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),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,175 +0,0 @@
|
||||||
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),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
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"]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
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());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
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),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// 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,14 +1,6 @@
|
||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
_flutterfire_internals:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: _flutterfire_internals
|
|
||||||
sha256: f5628cd9c92ed11083f425fd1f8f1bc60ecdda458c81d73b143aeda036c35fe7
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.3.16"
|
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -49,14 +41,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
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:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -73,118 +57,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
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:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -198,128 +70,11 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.3"
|
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:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -352,86 +107,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.10.0"
|
||||||
mime:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: mime
|
|
||||||
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.4"
|
|
||||||
path:
|
path:
|
||||||
dependency: "direct main"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.3"
|
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:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -485,14 +168,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.1"
|
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:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -509,22 +184,5 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.0"
|
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:
|
sdks:
|
||||||
dart: ">=3.2.0 <4.0.0"
|
dart: ">=3.2.0 <4.0.0"
|
||||||
flutter: ">=3.10.0"
|
|
||||||
|
|
59
pubspec.yaml
59
pubspec.yaml
|
@ -1,32 +1,27 @@
|
||||||
name: kochkomplize
|
name: kochkomplize
|
||||||
description: "Kochhilfe mit Rezepten und Texterkennung"
|
description: "A new Flutter project."
|
||||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
# The following line prevents the package from being accidentally published to
|
||||||
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
version: 1.0.0+1
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
|
|
||||||
environment:
|
version: 1.0.0+1
|
||||||
sdk: '>=3.2.0 <4.0.0'
|
|
||||||
|
environment:
|
||||||
dependencies:
|
sdk: '>=3.2.0 <4.0.0'
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
dependencies:
|
||||||
cupertino_icons: ^1.0.2
|
flutter:
|
||||||
firebase_core: ^2.24.2
|
sdk: flutter
|
||||||
firebase_database: ^10.4.0
|
cupertino_icons: ^1.0.2
|
||||||
firebase_storage: ^11.6.0
|
|
||||||
path_provider: ^2.1.2
|
dev_dependencies:
|
||||||
image_picker: ^1.0.7
|
flutter_test:
|
||||||
path: ^1.8.0
|
sdk: flutter
|
||||||
|
flutter_lints: ^2.0.0
|
||||||
dev_dependencies:
|
|
||||||
flutter_test:
|
flutter:
|
||||||
sdk: flutter
|
uses-material-design: true
|
||||||
flutter_lints: ^2.0.0
|
assets:
|
||||||
|
- assets/images/
|
||||||
flutter:
|
|
||||||
uses-material-design: true
|
|
||||||
assets:
|
|
||||||
- assets/images/
|
|
||||||
- assets/database/Kochkomplize.db
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,108 +1,50 @@
|
||||||
import 'package:flutter/material.dart';
|
// This is a basic Flutter widget test.
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
//
|
||||||
import 'package:kochkomplize/main.dart';
|
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||||
import 'package:kochkomplize/menu.dart';
|
// utility in the flutter_test package. For example, you can send tap and scroll
|
||||||
import 'package:kochkomplize/recipeformpage.dart';
|
// 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.
|
||||||
void main() {
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
testWidgets('Menu renders correctly', (WidgetTester tester) async {
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
void onMenuItemSelectedMock(String item) {}
|
|
||||||
|
import 'package:kochkomplize/main.dart';
|
||||||
// Verwenden Sie einen GlobalKey, um auf den Scaffold zuzugreifen
|
import 'package:kochkomplize/menu.dart';
|
||||||
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
|
|
||||||
|
Widget buildMenu() {
|
||||||
await tester.pumpWidget(MaterialApp(home: Scaffold(key: scaffoldKey, drawer: Menu(onMenuItemSelected: onMenuItemSelectedMock))));
|
return MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
// Öffnen Sie den Drawer programmatisch
|
body: Menu(onMenuItemSelected: (menuItem) {}),
|
||||||
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);
|
void main() {
|
||||||
expect(find.text('Hauptmenü'), findsOneWidget);
|
testWidgets('Check menu text', (WidgetTester tester) async {
|
||||||
expect(find.text('Startseite'), findsOneWidget);
|
await tester.pumpWidget(buildMenu());
|
||||||
expect(find.text('Meine Rezepte'), findsOneWidget);
|
expect(find.text('Meine Rezepte'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Tapping each menu item calls onMenuItemSelected with correct item', (WidgetTester tester) async {
|
testWidgets('Check number of list items', (WidgetTester tester) async {
|
||||||
String selectedMenuItem = '';
|
await tester.pumpWidget(const MyApp());
|
||||||
|
expect(find.byType(GestureDetector), findsNWidgets(3));
|
||||||
void onMenuItemSelectedMock(String item) {
|
});
|
||||||
selectedMenuItem = item;
|
|
||||||
}
|
// testWidgets('Check display of recipe titles', (WidgetTester tester) async {
|
||||||
|
// await tester.pumpWidget(const MyApp());
|
||||||
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
|
// expect(find.text('Rezept 1'), findsOneWidget);
|
||||||
|
// expect(find.text('Rezept 2'), findsOneWidget);
|
||||||
await tester.pumpWidget(MaterialApp(home: Scaffold(key: scaffoldKey, drawer: Menu(onMenuItemSelected: onMenuItemSelectedMock))));
|
// });
|
||||||
|
//
|
||||||
scaffoldKey.currentState!.openDrawer();
|
// testWidgets('Check display of recipe images', (WidgetTester tester) async {
|
||||||
await tester.pumpAndSettle(); // Warten, bis die Animation abgeschlossen ist
|
// await tester.pumpWidget(const MyApp());
|
||||||
|
// expect(find.byType(Image), findsNWidgets(2));
|
||||||
// Tippen Sie auf das Menüelement 'Startseite' und überprüfen Sie die Funktionalität
|
// });
|
||||||
await tester.tap(find.text('Startseite'));
|
//
|
||||||
await tester.pumpAndSettle();
|
// testWidgets('Check navigation to recipe detail page', (WidgetTester tester) async {
|
||||||
expect(selectedMenuItem, 'Startseite');
|
// await tester.pumpWidget(const MyApp());
|
||||||
|
// await tester.tap(find.text('Rezept 1'));
|
||||||
// Wiederholen Sie den Vorgang für 'Meine Rezepte'
|
// await tester.pumpAndSettle(); // Warte darauf, dass die Navigation abgeschlossen ist
|
||||||
scaffoldKey.currentState!.openDrawer();
|
// expect(find.text('Details für Rezept 1'), findsOneWidget);
|
||||||
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('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