diff --git a/.gitignore b/.gitignore index 24476c5..dc2c423 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ + # Miscellaneous *.class +*.lock *.log *.pyc *.swp @@ -8,7 +10,7 @@ .buildlog/ .history .svn/ -migrate_working_dir/ +/lib/models/*.g.dart # IntelliJ related *.iml @@ -16,29 +18,849 @@ migrate_working_dir/ *.iws .idea/ -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ +# Visual Studio Code related +.classpath +.project +.settings/ +.vscode/ + +# Flutter repo-specific +/bin/cache/ +/bin/internal/bootstrap.bat +/bin/internal/bootstrap.sh +/bin/mingit/ +/dev/benchmarks/mega_gallery/ +/dev/bots/.recipe_deps +/dev/bots/android_tools/ +/dev/devicelab/ABresults*.json +/dev/docs/doc/ +/dev/docs/flutter.docs.zip +/dev/docs/lib/ +/dev/docs/pubspec.yaml +/dev/integration_tests/**/xcuserdata +/dev/integration_tests/**/Pods +/packages/flutter/coverage/ +version +analysis_benchmark.json + +# packages file containing multi-root paths +.packages.generated # Flutter/Dart/Pub related **/doc/api/ -**/ios/Flutter/.last_build_id .dart_tool/ .flutter-plugins .flutter-plugins-dependencies +**/generated_plugin_registrant.dart .packages .pub-cache/ .pub/ -/build/ +build/ +flutter_*.png +linked_*.ds +unlinked.ds +unlinked_spec.ds -# Symbolication related +# Android related +**/android/**/gradle-wrapper.jar +.gradle/ +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java +**/android/key.properties +# *.jks + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/.last_build_id +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/ephemeral +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# macOS +**/macos/Flutter/GeneratedPluginRegistrant.swift + +# Coverage +coverage/ + +# Symbols app.*.symbols +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +!/dev/ci/**/Gemfile.lock + + +## Related .gitignores ## + +# Firebase configuration files / Google Services (e.g. APIs or Firebase) +ios/Runner/GoogleService-Info.plist +android/app/google-services.json + +# Google Maps API +google_maps_api.xml + +# Web related +lib/generated_plugin_registrant.dart + # Obfuscation related app.*.map.json -# Android Studio will place build artifacts here -/android/app/debug -/android/app/profile -/android/app/release + +### --------------------------- Dart.gitignore ------------------------- ### + +# See https://www.dartlang.org/guides/libraries/private-files + +# Files and directories created by pub +#.dart_tool/ +#.packages +#build/ +# If you're building an application, you may want to check-in your pubspec.lock +#pubspec.lock + +# Directory created by dartdoc +# If you don't generate documentation locally you can remove this line. +doc/api/ + +# dotenv environment variables file +.env* + +# Avoid committing generated Javascript files: +*.dart.js +*.info.json # Produced by the --dump-info flag. +*.js # When generated by dart2js. Don't specify *.js if your + # project includes source files written in JavaScript. +*.js_ +*.js.deps +*.js.map + +#.flutter-plugins +#.flutter-plugins-dependencies + + +### -------------------------- Swift.gitignore ------------------------ ### + +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +#xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +#*.xcscmblueprint +#*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +#build/ +#DerivedData/ +#*.moved-aside +#*.pbxuser +#!default.pbxuser +#*.mode1v3 +#!default.mode1v3 +#*.mode2v3 +#!default.mode2v3 +#*.perspectivev3 +#!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager + +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. + Packages/ + Package.pins + Package.resolved + *.xcodeproj + +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project + .swiftpm + +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ + + +### -------------------------- Java.gitignore -------------------------- ### + +# Compiled class file +#*.class + +# Log file +#*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +#*.jar +*.war +*.nar +*.ear +#*.zip +*.tar.gz +#*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + + +### -------------------------- Kotlin.gitignore ------------------------ ### + +#.DS_Store +.idea/shelf +/confluence/target +/dependencies/repo +/android.tests.dependencies +/dependencies/android.tests.dependencies +/dist +/local +/gh-pages +/ideaSDK +/clionSDK +/android-studio/sdk +#out/ +/tmp +/intellij +workspace.xml +*.versionsBackup +/idea/testData/debugger/tinyApp/classes* +/jps-plugin/testData/kannotator +/js/js.translator/testData/out/ +/js/js.translator/testData/out-min/ +/js/js.translator/testData/out-pir/ +#.gradle/ +#build/ +!**/src/**/build +!**/test/**/build +#*.iml +!**/testData/**/*.iml +.idea/remote-targets.xml +.idea/libraries/Gradle*.xml +.idea/libraries/Maven*.xml +.idea/artifacts/PILL_*.xml +.idea/artifacts/KotlinPlugin.xml +#.idea/modules +.idea/runConfigurations/JPS_*.xml +.idea/runConfigurations/PILL_*.xml +.idea/runConfigurations/_FP_*.xml +.idea/runConfigurations/_MT_*.xml +#.idea/libraries +#.idea/modules.xml +#.idea/gradle.xml +#.idea/compiler.xml +.idea/inspectionProfiles/profiles_settings.xml +.idea/.name +.idea/artifacts/dist_auto_* +.idea/artifacts/dist.xml +.idea/artifacts/ideaPlugin.xml +.idea/artifacts/kotlinc.xml +.idea/artifacts/kotlin_compiler_jar.xml +.idea/artifacts/kotlin_plugin_jar.xml +.idea/artifacts/kotlin_jps_plugin_jar.xml +.idea/artifacts/kotlin_daemon_client_jar.xml +.idea/artifacts/kotlin_imports_dumper_compiler_plugin_jar.xml +.idea/artifacts/kotlin_main_kts_jar.xml +.idea/artifacts/kotlin_compiler_client_embeddable_jar.xml +.idea/artifacts/kotlin_reflect_jar.xml +.idea/artifacts/kotlin_stdlib_js_ir_* +.idea/artifacts/kotlin_test_js_ir_* +.idea/artifacts/kotlin_stdlib_wasm_* +.idea/artifacts/kotlinx_atomicfu_runtime_* +#.idea/jarRepositories.xml +.idea/csv-plugin.xml +.idea/libraries-with-intellij-classes.xml +.idea/misc.xml +node_modules/ +.rpt2_cache/ +libraries/tools/kotlin-test-js-runner/lib/ +#local.properties +buildSrcTmp/ +distTmp/ +outTmp/ +/test.output +/kotlin-native/dist +kotlin-ide/ + + +### ------------------------- Android.gitignore ------------------------ ### + +# Built application files +*.apk +*.ap_ + +# Files for the ART/Dalvik VM +*.dex + +# Generated files +bin/ +gen/ +out/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +.idea/caches + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +#fastlane/report.xml +#fastlane/Preview.html +fastlane/screenshots +#fastlane/test_output +fastlane/readme.md + +# Keystore files +*.jks +*.keystore + + +### ------------------------- Gradle.gitignore ------------------------- ### + +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# Eclipse Gradle plugin generated files +# Eclipse Core +#.project +# JDT-specific (Eclipse Java Development Tools) +#.classpath + + +### ------------------------- Maven.gitignore -------------------------- ### + +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar + +# Eclipse m2e generated files +# Eclipse Core +#.project +# JDT-specific (Eclipse Java Development Tools) +#.classpath + + +### ------------------------ JetBrains.gitignore ----------------------- ### + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. + .idea/artifacts + .idea/compiler.xml + .idea/jarRepositories.xml + .idea/modules.xml + .idea/*.iml + .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +#*.iws + +# IntelliJ +#out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + + +### ------------------------- Xcode.gitignore -------------------------- ### + +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +#build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Gcc Patch +/*.gcno + + +### -------------------- VisualStudioCode.gitignore ------------------- ### + +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + + +### ----------------------- SublimeText.gitignore ---------------------- ### + +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json +sftp-config-alt*.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings + + +### -------------------------- Emacs.gitignore ------------------------- ### + +# -*- mode: gitignore; -*- +#*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + + +### -------------------------- Vim.gitignore -------------------------- ### + +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +#*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + + +### ------------------------ Windows.gitignore ------------------------ ### + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + + +### ------------------------- macOS.gitignore -------------------------- ### + +# General +#.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +### -------------------------- Linux.gitignore ------------------------- ### + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + + +### ------------------------ Archives.gitignore ------------------------ ### + +# It's better to unpack these files and commit the raw source because +# git has its own built in compression methods. +*.7z +*.jar +*.rar +*.zip +*.gz +*.gzip +*.tgz +*.bzip +*.bzip2 +*.bz2 +*.xz +*.lzma +#*.cab +*.xar + +# Packing-only formats +*.iso +*.tar + +# Package management formats +*.dmg +*.xpi +*.gem +*.egg +*.deb +*.rpm +#*.msi +#*.msm +#*.msp +*.txz + + +### ------------------------- Backup.gitignore ------------------------- ### + +*.bak +*.gho +*.ori +*.orig +*.tmp + + +### -------------------------- JEnv.gitignore -------------------------- ### + +# JEnv local Java version configuration file +.java-version + +# Used by previous versions of JEnv +.jenv-version + + +### ------------------------- Project Specific ------------------------- ### + +# Include any specific files here. +linux/flutter/generated_plugin_registrant.cc +linux/flutter/generated_plugin_registrant.h +linux/flutter/generated_plugins.cmake + + +### ---------------------------- References ---------------------------- ### + +# Flutter - https://github.com/flutter/flutter/blob/master/.gitignore +# Dart - https://github.com/github/gitignore/blob/main/Dart.gitignore +# Swift - https://github.com/github/gitignore/blob/main/Swift.gitignore +# Java - https://github.com/github/gitignore/blob/main/Java.gitignore +# Kotlin - https://github.com/JetBrains/kotlin/blob/master/.gitignore +# Android - https://github.com/github/gitignore/blob/main/Android.gitignore +# Gradle - https://github.com/github/gitignore/blob/main/Gradle.gitignore +# Maven - https://github.com/github/gitignore/blob/main/Maven.gitignore + +# JetBrains - https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# Xcode - https://github.com/github/gitignore/blob/main/Global/Xcode.gitignore +# VisualStudioCode - https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# SublimeText - https://github.com/github/gitignore/blob/main/Global/SublimeText.gitignore +# Emacs - https://github.com/github/gitignore/blob/main/Global/Emacs.gitignore +# Vim - https://github.com/github/gitignore/blob/main/Global/Vim.gitignore + +# Windows - https://github.com/github/gitignore/blob/main/Global/Windows.gitignore +# macOS - https://github.com/github/gitignore/blob/main/Global/macOS.gitignore +# Linux - https://github.com/github/gitignore/blob/main/Global/Linux.gitignore + +# Archives - https://github.com/github/gitignore/blob/main/Global/Archives.gitignore +# Backup - https://github.com/github/gitignore/blob/main/Global/Backup.gitignore +# JEnv - https://github.com/github/gitignore/blob/main/Global/JEnv.gitignore \ No newline at end of file diff --git a/.metadata b/.metadata index 620e877..c331aa0 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a" + revision: "2f708eb8396e362e280fac22cf171c2cb467343c" channel: "stable" project_type: app @@ -13,26 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - - platform: android - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - - platform: ios - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a + create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c - platform: linux - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - - platform: macos - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - - platform: web - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - - platform: windows - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a + create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c # User provided section diff --git a/android/app/build.gradle b/android/app/build.gradle index 145f023..a4dd702 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -45,7 +45,7 @@ android { applicationId "com.example.rezepte" // 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 23 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/lib/example_data.dart b/lib/example_data.dart index 08754e1..2cd2af1 100644 --- a/lib/example_data.dart +++ b/lib/example_data.dart @@ -1,5 +1,6 @@ import 'package:rezepte/models/difficulty.dart'; import 'package:rezepte/models/unit.dart'; +import 'package:rezepte/services/providers/db/dbhelper.dart'; import 'models/ingredient.dart'; import 'constants.dart' as constants; @@ -21,19 +22,33 @@ final List _fluid = constants.units.where((element) => element.type == UnitType.fluid).toList(); final List exampleIngredients = [ - Ingredient(title: 'Karotte', possibleUnits: _weightAndCount), - Ingredient(title: 'Kartoffel', possibleUnits: _weightAndCount), - Ingredient(title: 'Kaffeebohnen', possibleUnits: _weight), - Ingredient(title: 'Milch', possibleUnits: _fluid), - Ingredient(title: 'Limettenblätter', possibleUnits: _count), + Ingredient( + title: 'Karotte', + possibleUnits: _weightAndCount, + type: IngredientType.vegetable), + Ingredient( + title: 'Kartoffel', + possibleUnits: _weightAndCount, + type: IngredientType.vegetable), + Ingredient( + title: 'Kaffeebohnen', + possibleUnits: _weight, + type: IngredientType.other), + Ingredient(title: 'Milch', possibleUnits: _fluid, type: IngredientType.dairy), + Ingredient( + title: 'Limettenblätter', + possibleUnits: _count, + type: IngredientType.other), ]; final List exampleRecipes = [ Recipe( + id: DbHelper.nextRecipeId, title: 'Wraps', description: 'Nur ein paar Wraps', difficulty: Difficulty.hard), Recipe( + id: DbHelper.nextRecipeId, title: 'Burritos', description: 'Nur ein paar Burritos', difficulty: Difficulty.easy), diff --git a/lib/main.dart b/lib/main.dart index 491726d..d625ea2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,9 +6,12 @@ import 'package:rezepte/services/providers/recipe_list_provider.dart'; import 'package:rezepte/services/providers/recipe_provider.dart'; import 'pages/recipe_detail_page.dart'; +import 'services/providers/db/dbhelper.dart'; import 'theme.dart'; -void main() { +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await DbHelper.init(); runApp(MultiProvider(providers: [ ChangeNotifierProvider( create: (_) => RecipeListProvider(), diff --git a/lib/models/cooking_step.dart b/lib/models/cooking_step.dart index a1258f2..453d017 100644 --- a/lib/models/cooking_step.dart +++ b/lib/models/cooking_step.dart @@ -1,6 +1,21 @@ +import 'package:isar/isar.dart'; + +part 'cooking_step.g.dart'; + +@embedded class CookingStep { final String title; final String description; CookingStep({required this.title, this.description = ''}); + + factory CookingStep.fromJson(Map json) => CookingStep( + title: json['title'] as String, + description: json['description'] as String, + ); + + Map toJson() => { + 'title': title, + 'description': description, + }; } diff --git a/lib/models/difficulty.dart b/lib/models/difficulty.dart index 8465717..b49eca8 100644 --- a/lib/models/difficulty.dart +++ b/lib/models/difficulty.dart @@ -21,4 +21,4 @@ class DifficultyUtil { } // Only use camelCase or UpperCamelCase for names -enum Difficulty { veryEasy, easy, intermediate, hard, veryHard } +enum Difficulty { notSelected, veryEasy, easy, intermediate, hard, veryHard } diff --git a/lib/models/ingredient.dart b/lib/models/ingredient.dart index 4870f13..23a3007 100644 --- a/lib/models/ingredient.dart +++ b/lib/models/ingredient.dart @@ -1,16 +1,37 @@ +import 'package:isar/isar.dart'; + import 'unit.dart'; +part 'ingredient.g.dart'; + +@embedded class Ingredient { final String title; List possibleUnits; List preferredBrands; + IngredientType type; Ingredient({ required this.title, + required this.type, this.possibleUnits = const [], this.preferredBrands = const [], }); + factory Ingredient.fromJson(Map json) => Ingredient( + title: json['title'] as String, + type: json['ingredient_type'] as IngredientType, + possibleUnits: _unitsFromJson(json['possibleUnits']), + preferredBrands: json['preferredBrands'] as List, + ); + + Map toJson() => { + 'title': title, + 'ingredient_type': type, + 'possibleUnits': possibleUnits.map((e) => e.toJson()).toList(), + 'preferredBrands': preferredBrands, + }; + @override bool operator ==(other) { Ingredient i = other as Ingredient; @@ -21,4 +42,19 @@ class Ingredient { int get hashCode { return Object.hash(title, null); } + + static List _unitsFromJson(List> jsonList) => + jsonList.map((e) => Unit.fromJson(e)).toList(); +} + +enum IngredientType { + vegetable, + meat, + fish, + grain, + fruit, + dairy, + fatsAndOil, + spice, + other, } diff --git a/lib/models/ingredient_list_entry.dart b/lib/models/ingredient_list_entry.dart index ccb5531..769f5bc 100644 --- a/lib/models/ingredient_list_entry.dart +++ b/lib/models/ingredient_list_entry.dart @@ -1,6 +1,11 @@ +import 'package:isar/isar.dart'; + import 'ingredient.dart'; import 'unit.dart'; +part 'ingredient_list_entry.g.dart'; + +@embedded class IngredientListEntry { final Ingredient ingredient; final int amount; @@ -9,6 +14,21 @@ class IngredientListEntry { IngredientListEntry(this.ingredient, this.amount, this.unit, this.optional); + factory IngredientListEntry.fromJson(Map json) => + IngredientListEntry( + Ingredient.fromJson(json['ingredient']), + json['amount'] as int, + Unit.fromJson(json['unit']), + json['optional'] as bool, + ); + + Map toJson() => { + 'ingredient': ingredient.toJson(), + 'amount': amount, + 'unit': unit.toJson(), + 'optional': optional, + }; + @override operator ==(Object other) { final i = other as IngredientListEntry; diff --git a/lib/models/recipe.dart b/lib/models/recipe.dart index 69ccaf0..8bf135f 100644 --- a/lib/models/recipe.dart +++ b/lib/models/recipe.dart @@ -1,20 +1,48 @@ +import 'package:isar/isar.dart'; +import 'package:rezepte/models/ingredient.dart'; +import 'package:rezepte/models/unit.dart'; + import 'difficulty.dart'; import 'cooking_step.dart'; import 'ingredient_list_entry.dart'; +part 'recipe.g.dart'; + +@collection class Recipe { + final int id; String title; String description; - Difficulty? difficulty; - final List ingredients = []; - final List steps = []; + Difficulty difficulty; + List ingredients = []; + List steps = []; Recipe({ + required this.id, required this.title, this.description = '', - this.difficulty, + this.difficulty = Difficulty.notSelected, }); + factory Recipe.fromJson(json) => Recipe( + id: json['id'] as int, + title: json['title'] as String, + description: json['description'] as String, + difficulty: json['difficulty'] as Difficulty, + ) + ..ingredients = _ingredientsFromMap( + json['ingredients'] as List>) + ..steps = _stepsFromMap(json['steps']); + + Map toJson() => { + 'id': id, + 'title': title, + 'description': description, + 'difficulty': difficulty, + 'ingredients': ingredients.map((e) => e.toJson()).toList(), + 'steps': steps.map((e) => e.toJson()).toList(), + }; + void addIngredient(IngredientListEntry ingredient) => ingredients.add(ingredient); @@ -35,7 +63,7 @@ class Recipe { bool get isEmpty { return title.isEmpty && description.isEmpty && - difficulty == null && + difficulty == Difficulty.notSelected && ingredients.isEmpty && steps.isEmpty; } @@ -47,8 +75,15 @@ class Recipe { void clear() { title = ''; description = ''; - difficulty = null; + difficulty = Difficulty.notSelected; ingredients.clear(); steps.clear(); } + + static List _ingredientsFromMap( + List> jsonList) => + jsonList.map((e) => IngredientListEntry.fromJson(e)).toList(); + + static List _stepsFromMap(List> jsonList) => + jsonList.map((e) => CookingStep.fromJson(e)).toList(); } diff --git a/lib/models/unit.dart b/lib/models/unit.dart index eb584ab..54ea17f 100644 --- a/lib/models/unit.dart +++ b/lib/models/unit.dart @@ -1,9 +1,26 @@ +import 'package:isar/isar.dart'; + +part 'unit.g.dart'; + +@embedded class Unit { final String name; final UnitType type; final System system; Unit(this.name, this.type, {this.system = System.metric}); + + factory Unit.fromJson(Map json) => Unit( + json['name'] as String, + json['type'] as UnitType, + system: json['system'] as System, + ); + + Map toJson() => { + 'name': name, + 'type': type, + 'system': system, + }; } enum System { metric, imperial, neutral } diff --git a/lib/pages/create_recipe_page.dart b/lib/pages/create_recipe_page.dart index 15c048e..ee2d0f5 100644 --- a/lib/pages/create_recipe_page.dart +++ b/lib/pages/create_recipe_page.dart @@ -1,12 +1,13 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:rezepte/widgets/ingredients_bottomsheet.dart'; +import 'package:rezepte/services/providers/db/dbhelper.dart'; import 'package:rezepte/widgets/will_pop_scope.dart'; -import '../models/difficulty.dart'; -import '../models/ingredient_list_entry.dart'; import '../models/recipe.dart'; import '../services/providers/recipe_list_provider.dart'; import '../services/providers/recipe_provider.dart'; +import '../widgets/create_recipe_page/add_info_widget.dart'; +import '../widgets/create_recipe_page/add_ingredients_widget.dart'; +import '../widgets/create_recipe_page/add_steps_widget.dart'; class CreateRecipe extends StatefulWidget { const CreateRecipe({super.key}); @@ -20,11 +21,12 @@ class _CreateRecipeState extends State { late RecipeListProvider recipeListProvider; late RecipeProvider recipeProvider; late Recipe recipe; - - @override - void initState() { - super.initState(); - } + final List pages = [ + const AddRecipeInfoWidget(), + const AddIngredientsWidget(), + const AddCookingStepsWidget(), + ]; + int selectedTabIndex = 0; @override void dispose() { @@ -34,10 +36,17 @@ class _CreateRecipeState extends State { @override Widget build(BuildContext context) { - recipe = Provider.of(context).recipe; - recipeProvider = Provider.of(context, listen: false); + recipeProvider = Provider.of(context, listen: true); recipeListProvider = Provider.of(context, listen: false); + + if (recipeProvider.recipe == null) { + recipe = Recipe(id: DbHelper.nextRecipeId, title: ''); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + recipeProvider.recipe = recipe; + }); + } + return CustomWillPopScope( context, ignore: recipe.isEmpty, @@ -45,128 +54,26 @@ class _CreateRecipeState extends State { appBar: AppBar( title: const Text('Create Recipe'), ), - floatingActionButton: recipe.isNotEmpty - ? FloatingActionButton( - onPressed: _onRecipeSubmitted, - child: const Icon(Icons.save), - ) - : null, - body: Form( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Column( - children: [ - TextFormField( - onTapOutside: (event) => FocusScope.of(context).unfocus(), - onChanged: (value) => recipe.title = value, - decoration: const InputDecoration( - label: Text('Title'), - ), - style: TextStyle( - color: Theme.of(context).colorScheme.onBackground), - ), - TextFormField( - onTapOutside: (event) => FocusScope.of(context).unfocus(), - minLines: 1, - maxLines: 4, - onChanged: (value) => recipe.description = value, - decoration: const InputDecoration( - label: Text('Description'), - ), - style: TextStyle( - color: Theme.of(context).colorScheme.onBackground), - ), - DropdownMenu( - dropdownMenuEntries: DifficultyUtil.getDropdownList(), - onSelected: (value) => recipe.difficulty = value, - label: const Text('Difficulty'), - textStyle: TextStyle( - color: Theme.of(context).colorScheme.onBackground), - ), - ElevatedButton( - onPressed: _openIngredientBottomSheet, - child: const Text('Add Ingredient'), - ), - Expanded( - child: ListView.separated( - itemCount: recipe.ingredients.length, - itemBuilder: _ingredientListBuilder, - separatorBuilder: (context, index) => const Divider(), - ), - ) - ], - ), - ), + bottomNavigationBar: BottomNavigationBar( + currentIndex: selectedTabIndex, + showSelectedLabels: false, + items: const [ + BottomNavigationBarItem(icon: Icon(Icons.add_task), label: ''), + BottomNavigationBarItem(icon: Icon(Icons.add_task), label: ''), + BottomNavigationBarItem(icon: Icon(Icons.add_task), label: ''), + ], + onTap: (value) => setState(() => selectedTabIndex = value), ), - ), - ); - } - - void _openIngredientBottomSheet() { - showModalBottomSheet( - context: context, - builder: (context) => IngredientsBottomsheet( - onSubmitted: _onIngredientSubmitted, - ), - ); - } - - void _onIngredientSubmitted(IngredientListEntry ingredient) => setState(() { - recipe.ingredients.add(ingredient); - }); - - void _onIngredientRemoveTapped(int index) { - final removedIngredient = recipe.ingredients.elementAt(index); - - recipe.removeIngredientAt(index); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: const Text('Ingredient Removed'), - action: SnackBarAction( - label: 'Undo', - onPressed: () { - recipe.addIngredient(removedIngredient); - }), - ), - ); - } - - Widget? _ingredientListBuilder(BuildContext context, int index) { - final ingredient = recipe.ingredients.elementAt(index); - - return ListTile( - contentPadding: EdgeInsets.zero, - title: Text(ingredient.ingredient.title), - subtitle: ingredient.optional ? const Text('optional') : null, - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - ingredient.amount.toString(), - style: Theme.of(context) - .textTheme - .bodyLarge! - .copyWith(color: Theme.of(context).colorScheme.onBackground), - ), - const Padding(padding: EdgeInsets.symmetric(horizontal: 2)), - Text( - ingredient.unit.name, - style: Theme.of(context) - .textTheme - .bodyLarge! - .copyWith(color: Theme.of(context).colorScheme.onBackground), - ), - const Padding(padding: EdgeInsets.symmetric(horizontal: 5)), - SizedBox( - width: 30, - height: 30, - child: IconButton( - padding: EdgeInsets.zero, - onPressed: () => _onIngredientRemoveTapped(index), - icon: const Icon(Icons.delete), - ), - ), - ], + body: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: pages.elementAt(selectedTabIndex), + ), + // floatingActionButton: recipe.isNotEmpty + // ? FloatingActionButton( + // onPressed: _onRecipeSubmitted, + // child: const Icon(Icons.save), + // ) + // : null, ), ); } diff --git a/lib/pages/recipe_detail_page.dart b/lib/pages/recipe_detail_page.dart index 7b2e45a..50e8e02 100644 --- a/lib/pages/recipe_detail_page.dart +++ b/lib/pages/recipe_detail_page.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:rezepte/services/providers/recipe_provider.dart'; +import '../models/recipe.dart'; + class RecipeDetail extends StatelessWidget { const RecipeDetail({super.key}); static const routeName = '/recipeDetail'; @@ -10,6 +12,9 @@ class RecipeDetail extends StatelessWidget { Widget build(BuildContext context) { final recipe = Provider.of(context, listen: false).recipe; + if (recipe == null) Navigator.of(context).pop(); + recipe as Recipe; + return Scaffold( appBar: AppBar(), body: Center( diff --git a/lib/services/providers/db/dbhelper.dart b/lib/services/providers/db/dbhelper.dart new file mode 100644 index 0000000..228d5b0 --- /dev/null +++ b/lib/services/providers/db/dbhelper.dart @@ -0,0 +1,35 @@ +import 'dart:io'; +import 'package:path_provider/path_provider.dart'; +import 'package:isar/isar.dart'; +import '../../../models/recipe.dart'; + +class DbHelper { + static late Directory _dir; + static Isar get _isar => Isar.open( + schemas: _schemas, + directory: _dir.path, + ); + + static const List _schemas = [ + RecipeSchema, + ]; + static int get nextRecipeId => _isar.recipes.autoIncrement(); + + static Future init() async { + _dir = await getApplicationDocumentsDirectory(); + } + + static List fetchRecipes() { + return _isar.recipes.where().findAll().cast().toList(); + } + + static void putRecipe(Recipe recipe) { + _isar.write((isar) => isar.recipes.put(recipe)); + } + + static bool deleteRecipe(Recipe recipe) { + return _isar.write((isar) => isar.recipes.delete(recipe.id)); + } + + static Stream get recipesChangedStream => _isar.recipes.watchLazy(); +} diff --git a/lib/services/providers/recipe_list_provider.dart b/lib/services/providers/recipe_list_provider.dart index b400d12..6c7ef90 100644 --- a/lib/services/providers/recipe_list_provider.dart +++ b/lib/services/providers/recipe_list_provider.dart @@ -1,27 +1,31 @@ import 'package:flutter/foundation.dart'; +import 'package:rezepte/services/providers/db/dbhelper.dart'; import '../../models/recipe.dart'; -import 'package:rezepte/example_data.dart' as e; class RecipeListProvider extends ChangeNotifier { - final List _recipes = kDebugMode ? e.exampleRecipes : []; - - set recipes(List recipes) { - _recipes.clear(); - _recipes.addAll(recipes); - notifyListeners(); + RecipeListProvider() { + DbHelper.recipesChangedStream.listen((event) { + notifyListeners(); + }); } + // final List _recipes = []; - List get recipes => _recipes; + // set recipes(List recipes) { + // _recipes.clear(); + // _recipes.addAll(recipes); + // notifyListeners(); + // } - void clearRecipes({silent = false}) { - _recipes.clear(); + List get recipes => DbHelper.fetchRecipes(); // _recipes; - if (!silent) notifyListeners(); - } + // void clearRecipes({silent = false}) { + // _recipes.clear(); - void addRecipe(Recipe recipe, {silent = false}) { - _recipes.add(recipe); - if (!silent) notifyListeners(); + // if (!silent) notifyListeners(); + // } + + void addRecipe(Recipe recipe) { + DbHelper.putRecipe(recipe); } } diff --git a/lib/services/providers/recipe_provider.dart b/lib/services/providers/recipe_provider.dart index f811207..6e41601 100644 --- a/lib/services/providers/recipe_provider.dart +++ b/lib/services/providers/recipe_provider.dart @@ -5,9 +5,9 @@ import '../../models/recipe.dart'; class RecipeProvider extends ChangeNotifier { Recipe? _recipe; - Recipe get recipe => _recipe ??= Recipe(title: ''); + Recipe? get recipe => _recipe; - set recipe(Recipe recipe) { + set recipe(Recipe? recipe) { _recipe = recipe; notifyListeners(); } @@ -15,57 +15,4 @@ class RecipeProvider extends ChangeNotifier { void disposeRecipe() { _recipe = null; } - - // set description(String description) { - // _description = description; - // notifyListeners(); - // } - - // String get title => _title; - - // set title(String title) { - // _title = title; - // notifyListeners(); - // } - - // Difficulty? get difficulty => _difficulty; - - // set difficulty(Difficulty? difficulty) { - // _difficulty = difficulty; - // notifyListeners(); - // } - - // List get ingredients => _ingredients; - - // void addIngredient(IngredientListEntry ingredient) { - // _ingredients.add(ingredient); - // notifyListeners(); - // } - - // void clearIngredients({silent = false}) { - // ingredients.clear(); - // if (!silent) notifyListeners(); - // } - - // void removeIngredientAt(int index, {silent = false}) { - // ingredients.removeAt(index); - // if (!silent) notifyListeners(); - // } - - // void removeIngredient(IngredientListEntry ingredient, {silent = false}) { - // ingredients.removeWhere((element) => element == ingredient); - // if (!silent) notifyListeners(); - // } - - // List get steps => _steps; - - // void addStep(CookingStep step, {silent = false}) { - // steps.add(step); - // if (!silent) notifyListeners(); - // } - - // void removeStepAt(int index, {silent = false}) { - // steps.removeAt(index); - // if (!silent) notifyListeners(); - // } } diff --git a/lib/widgets/create_recipe_page/add_info_widget.dart b/lib/widgets/create_recipe_page/add_info_widget.dart new file mode 100644 index 0000000..1e832e2 --- /dev/null +++ b/lib/widgets/create_recipe_page/add_info_widget.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../../models/difficulty.dart'; +import '../../models/recipe.dart'; +import '../../services/providers/recipe_provider.dart'; + +class AddRecipeInfoWidget extends StatefulWidget { + const AddRecipeInfoWidget({super.key}); + + @override + State createState() => _AddRecipeInfoWidgetState(); +} + +class _AddRecipeInfoWidgetState extends State { + late RecipeProvider recipeProvider; + + @override + Widget build(BuildContext context) { + recipeProvider = Provider.of(context, listen: true); + + if (recipeProvider.recipe == null) { + return const Center( + child: CircularProgressIndicator(), + ); + } + + return Column( + children: [ + TextFormField( + initialValue: recipeProvider.recipe!.title, + onTapOutside: (event) => FocusScope.of(context).unfocus(), + onChanged: (value) => recipeProvider.recipe!.title = value, + decoration: const InputDecoration( + label: Text('Title'), + ), + style: TextStyle(color: Theme.of(context).colorScheme.onBackground), + ), + TextFormField( + initialValue: recipeProvider.recipe!.description, + onTapOutside: (event) => FocusScope.of(context).unfocus(), + minLines: 1, + maxLines: 4, + onChanged: (value) => recipeProvider.recipe!.description = value, + decoration: const InputDecoration( + label: Text('Description'), + ), + style: TextStyle(color: Theme.of(context).colorScheme.onBackground), + ), + DropdownMenu( + initialSelection: recipeProvider.recipe!.difficulty, + dropdownMenuEntries: DifficultyUtil.getDropdownList(), + onSelected: (value) => recipeProvider.recipe!.difficulty = + value ?? Difficulty.notSelected, + label: const Text('Difficulty'), + textStyle: + TextStyle(color: Theme.of(context).colorScheme.onBackground), + ), + // ElevatedButton( + // onPressed: _openIngredientBottomSheet, + // child: const Text('Add Ingred ient'), + // ), + // Expanded( + // child: ListView.separated( + // itemCount: recipe.ingredients.length, + // itemBuilder: _ingredientListBuilder, + // separatorBuilder: (context, index) => const Divider(), + // ), + // ) + ], + ); + } +} diff --git a/lib/widgets/create_recipe_page/add_ingredients_widget.dart b/lib/widgets/create_recipe_page/add_ingredients_widget.dart new file mode 100644 index 0000000..6c3830c --- /dev/null +++ b/lib/widgets/create_recipe_page/add_ingredients_widget.dart @@ -0,0 +1,124 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../../models/ingredient_list_entry.dart'; +import '../../models/recipe.dart'; +import '../../services/providers/recipe_provider.dart'; +import '../../widgets/ingredients_bottomsheet.dart'; + +class AddIngredientsWidget extends StatefulWidget { + const AddIngredientsWidget({super.key}); + + @override + State createState() => _AddIngredientsWidgetState(); +} + +class _AddIngredientsWidgetState extends State { + late RecipeProvider recipeProvider; + + void _onIngredientRemoveTapped(int index) { + final removedIngredient = + recipeProvider.recipe!.ingredients.elementAt(index); + + setState(() { + recipeProvider.recipe!.removeIngredientAt(index); + }); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: const Text('Ingredient Removed'), + action: SnackBarAction( + label: 'Undo', + onPressed: () { + setState(() { + recipeProvider.recipe!.addIngredient(removedIngredient); + }); + }, + ), + ), + ); + } + + Widget? _ingredientListBuilder(BuildContext context, int index) { + final ingredient = recipeProvider.recipe!.ingredients.elementAt(index); + + return ListTile( + contentPadding: EdgeInsets.zero, + title: Text(ingredient.ingredient.title), + subtitle: ingredient.optional ? const Text('optional') : null, + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + ingredient.amount.toString(), + style: Theme.of(context) + .textTheme + .bodyLarge! + .copyWith(color: Theme.of(context).colorScheme.onBackground), + ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 2)), + Text( + ingredient.unit.name, + style: Theme.of(context) + .textTheme + .bodyLarge! + .copyWith(color: Theme.of(context).colorScheme.onBackground), + ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 5)), + SizedBox( + width: 30, + height: 30, + child: IconButton( + padding: EdgeInsets.zero, + onPressed: () => _onIngredientRemoveTapped(index), + icon: const Icon(Icons.delete), + ), + ), + ], + ), + ); + } + + void _openIngredientBottomSheet() { + showModalBottomSheet( + context: context, + builder: (context) => IngredientsBottomsheet( + onSubmitted: _onIngredientSubmitted, + ), + ); + } + + void _onIngredientSubmitted(IngredientListEntry ingredient) => setState(() { + recipeProvider.recipe!.ingredients.add(ingredient); + }); + + @override + Widget build(BuildContext context) { + recipeProvider = Provider.of(context, listen: true); + if (recipeProvider.recipe == null) { + return const Center( + child: CircularProgressIndicator(), + ); + } + + return Stack( + children: [ + ListView.separated( + padding: const EdgeInsets.only(bottom: 88), + itemCount: recipeProvider.recipe!.ingredients.length, + itemBuilder: _ingredientListBuilder, + separatorBuilder: (context, index) => const Divider(), + ), + Align( + alignment: Alignment.bottomRight, + child: Padding( + padding: const EdgeInsets.only(bottom: 16, right: 16), + child: FloatingActionButton( + onPressed: _openIngredientBottomSheet, + child: const Icon(Icons.add), + ), + ), + ), + ], + ); + } +} diff --git a/lib/widgets/create_recipe_page/add_steps_widget.dart b/lib/widgets/create_recipe_page/add_steps_widget.dart new file mode 100644 index 0000000..03ac1c2 --- /dev/null +++ b/lib/widgets/create_recipe_page/add_steps_widget.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class AddCookingStepsWidget extends StatelessWidget { + const AddCookingStepsWidget({super.key}); + + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} diff --git a/lib/widgets/ingredients_bottomsheet.dart b/lib/widgets/ingredients_bottomsheet.dart index 674139b..f35430c 100644 --- a/lib/widgets/ingredients_bottomsheet.dart +++ b/lib/widgets/ingredients_bottomsheet.dart @@ -169,7 +169,8 @@ class _IngredientsBottomsheetState extends State { } bool _submit() { - final ingredient = Ingredient(title: _ingredientController.text); + final ingredient = Ingredient( + title: _ingredientController.text, type: IngredientType.other); final unit = selectedUnit; final amount = int.tryParse(_amountController.text); if (ingredient.title.isEmpty || unit == null || amount == null) { diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index e71a16d..0000000 --- a/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void fl_register_plugins(FlPluginRegistry* registry) { -} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47..0000000 --- a/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake deleted file mode 100644 index 2e1de87..0000000 --- a/linux/flutter/generated_plugins.cmake +++ /dev/null @@ -1,23 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 5bec6c9..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,205 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 - url: "https://pub.dev" - source: hosted - version: "1.17.2" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d - url: "https://pub.dev" - source: hosted - version: "1.0.6" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 - url: "https://pub.dev" - source: hosted - version: "2.0.3" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - lints: - dependency: transitive - description: - name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - matcher: - dependency: transitive - description: - name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" - url: "https://pub.dev" - source: hosted - version: "0.12.16" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" - url: "https://pub.dev" - source: hosted - version: "0.5.0" - meta: - dependency: transitive - description: - name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - nested: - dependency: transitive - description: - name: nested - sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - path: - dependency: transitive - description: - name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" - url: "https://pub.dev" - source: hosted - version: "1.8.3" - provider: - dependency: "direct main" - description: - name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f - url: "https://pub.dev" - source: hosted - version: "6.0.5" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 - url: "https://pub.dev" - source: hosted - version: "1.11.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" - url: "https://pub.dev" - source: hosted - version: "0.6.0" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - web: - dependency: transitive - description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 - url: "https://pub.dev" - source: hosted - version: "0.1.4-beta" -sdks: - dart: ">=3.1.0 <4.0.0" - flutter: ">=1.16.0" diff --git a/pubspec.yaml b/pubspec.yaml index c1da76d..146a75f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,14 +10,17 @@ dependencies: flutter: sdk: flutter - cupertino_icons: ^1.0.2 provider: ^6.0.5 + isar: ^4.0.0-dev.13 + isar_flutter_libs: ^4.0.0-dev.13 + path_provider: ^2.1.0 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.0 + flutter_lints: ^3.0.1 + build_runner: ^2.4.6 flutter: diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..865c184 --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// 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:rezepte/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +}