├── .github └── workflows │ ├── clean-workflow.yaml │ └── release-workflow.yaml ├── .gitignore ├── .metadata ├── LICENSE.txt ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── de │ │ │ │ └── thenus │ │ │ │ └── cookbook_app │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable-mdpi │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable-xhdpi │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable-xxhdpi │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable-xxxhdpi │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ └── ic_launcher.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── documents │ └── changelog │ │ ├── de.json │ │ └── en.json ├── icons │ ├── bring.png │ └── logo_transparent.png ├── images │ └── no_image.png └── web │ └── backend_url.txt ├── build_assets ├── docker │ ├── Dockerfile │ └── entrypoint.sh ├── icons │ ├── android │ │ ├── adaptive_background.png │ │ ├── adaptive_foreground.png │ │ ├── light_icon.png │ │ └── monochrome.png │ ├── ios │ │ ├── dark_icon.png │ │ ├── light_icon.png │ │ └── tinted_icon.png │ ├── macos │ │ └── light_icon.png │ ├── web │ │ └── light_icon.png │ └── windows │ │ └── light_icon.png └── ios │ └── ExportOptions.plist ├── devtools_options.yaml ├── docs └── features │ ├── dashboard │ ├── phone.png │ └── tablet.png │ ├── diet │ ├── phone.png │ └── tablet.png │ ├── editor │ ├── phone.png │ └── tablet.png │ ├── library │ ├── phone.png │ └── tablet.png │ ├── recipe │ ├── phone.png │ └── tablet.png │ └── theme │ ├── phone.png │ └── tablet.png ├── examples ├── docker-compose-traefik.yaml └── docker-compose.yaml ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── InfoPlist.xcstrings ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-38x38@2x.png │ │ │ ├── Icon-App-38x38@3x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-64x64@2x.png │ │ │ ├── Icon-App-64x64@3x.png │ │ │ ├── Icon-App-68x68@2x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ ├── Icon-App-Dark-1024x1024@1x.png │ │ │ ├── Icon-App-Dark-20x20@2x.png │ │ │ ├── Icon-App-Dark-20x20@3x.png │ │ │ ├── Icon-App-Dark-29x29@2x.png │ │ │ ├── Icon-App-Dark-29x29@3x.png │ │ │ ├── Icon-App-Dark-38x38@2x.png │ │ │ ├── Icon-App-Dark-38x38@3x.png │ │ │ ├── Icon-App-Dark-40x40@2x.png │ │ │ ├── Icon-App-Dark-40x40@3x.png │ │ │ ├── Icon-App-Dark-60x60@2x.png │ │ │ ├── Icon-App-Dark-60x60@3x.png │ │ │ ├── Icon-App-Dark-64x64@2x.png │ │ │ ├── Icon-App-Dark-64x64@3x.png │ │ │ ├── Icon-App-Dark-68x68@2x.png │ │ │ ├── Icon-App-Dark-76x76@2x.png │ │ │ ├── Icon-App-Dark-83.5x83.5@2x.png │ │ │ ├── Icon-App-Tinted-1024x1024@1x.png │ │ │ ├── Icon-App-Tinted-20x20@2x.png │ │ │ ├── Icon-App-Tinted-20x20@3x.png │ │ │ ├── Icon-App-Tinted-29x29@2x.png │ │ │ ├── Icon-App-Tinted-29x29@3x.png │ │ │ ├── Icon-App-Tinted-38x38@2x.png │ │ │ ├── Icon-App-Tinted-38x38@3x.png │ │ │ ├── Icon-App-Tinted-40x40@2x.png │ │ │ ├── Icon-App-Tinted-40x40@3x.png │ │ │ ├── Icon-App-Tinted-60x60@2x.png │ │ │ ├── Icon-App-Tinted-60x60@3x.png │ │ │ ├── Icon-App-Tinted-64x64@2x.png │ │ │ ├── Icon-App-Tinted-64x64@3x.png │ │ │ ├── Icon-App-Tinted-68x68@2x.png │ │ │ ├── Icon-App-Tinted-76x76@2x.png │ │ │ └── Icon-App-Tinted-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── Runner-Bridging-Header.h │ ├── Runner.entitlements │ └── de.lproj │ │ ├── LaunchScreen.strings │ │ └── Main.strings └── RunnerTests │ └── RunnerTests.swift ├── l10n.yaml ├── lib ├── clients │ ├── api_client.dart │ ├── authors_client.dart │ ├── bring_client.dart │ ├── categories_client.dart │ ├── category_group_client.dart │ ├── features_client.dart │ ├── files_client.dart │ ├── highlights_client.dart │ ├── interceptor_methods.dart │ ├── library_client.dart │ ├── public_recipe_client.dart │ ├── recipes_client.dart │ ├── self_service_client.dart │ ├── stories_client.dart │ ├── tags_client.dart │ ├── token_client.dart │ ├── units_client.dart │ └── user_client.dart ├── components │ ├── dashboard │ │ ├── highlights_viewer.dart │ │ ├── latest_recipe_viewer.dart │ │ ├── quick_actions.dart │ │ └── stories_viewer.dart │ ├── dialogs │ │ ├── t_alert_dialog.dart │ │ ├── t_confirm_dialog.dart │ │ ├── t_full_dialog.dart │ │ └── t_loading_dialog.dart │ ├── go_router │ │ └── wrapper.dart │ ├── library │ │ └── dialogs │ │ │ ├── create_book_dialog.dart │ │ │ └── edit_book_dialog.dart │ ├── login │ │ ├── login_form.dart │ │ ├── server_form.dart │ │ ├── server_major_incompatible.dart │ │ └── server_minor_incompatible.dart │ ├── public_recipe │ │ ├── public_recipe_author.dart │ │ ├── public_recipe_categories.dart │ │ └── public_recipe_tags.dart │ ├── recipe │ │ ├── bring_button.dart │ │ ├── dialogs │ │ │ ├── change_owner_dialog.dart │ │ │ └── library_dialog.dart │ │ ├── recipe_action_button.dart │ │ ├── recipe_author.dart │ │ ├── recipe_categories.dart │ │ ├── recipe_description.dart │ │ ├── recipe_durations.dart │ │ ├── recipe_informations.dart │ │ ├── recipe_ingredients.dart │ │ ├── recipe_instructions.dart │ │ ├── recipe_nutrition_chart.dart │ │ ├── recipe_nutrition_desktop.dart │ │ ├── recipe_nutrition_mobile.dart │ │ ├── recipe_nutrition_table.dart │ │ ├── recipe_published.dart │ │ ├── recipe_tags.dart │ │ └── recipe_title.dart │ ├── recipe_drafts │ │ └── d_scrape.dart │ ├── recipe_editor │ │ ├── dialogs │ │ │ ├── d_categories.dart │ │ │ ├── d_common.dart │ │ │ ├── d_course.dart │ │ │ ├── d_diet.dart │ │ │ ├── d_duration.dart │ │ │ ├── d_durations.dart │ │ │ ├── d_images.dart │ │ │ ├── d_ingredient.dart │ │ │ ├── d_ingredient_group.dart │ │ │ ├── d_ingredient_groups.dart │ │ │ ├── d_instruction.dart │ │ │ ├── d_instruction_group.dart │ │ │ ├── d_instruction_groups.dart │ │ │ ├── d_nutrition.dart │ │ │ ├── d_preview.dart │ │ │ ├── d_serving.dart │ │ │ └── d_tags.dart │ │ ├── preview │ │ │ ├── preview_categories.dart │ │ │ ├── preview_ingredients.dart │ │ │ ├── preview_instructions.dart │ │ │ ├── preview_tags.dart │ │ │ └── variations │ │ │ │ ├── desktop.dart │ │ │ │ └── mobile.dart │ │ └── t_progress.dart │ ├── riverpod │ │ ├── error_page.dart │ │ ├── loading_page.dart │ │ ├── r_feature.dart │ │ ├── r_scaffold.dart │ │ └── r_struct.dart │ ├── search │ │ ├── loading_search.dart │ │ ├── nothing_found.dart │ │ └── search_term_too_short.dart │ ├── settings │ │ └── dialogs │ │ │ ├── d_changelog.dart │ │ │ ├── d_theme.dart │ │ │ ├── manage_avatar.dart │ │ │ ├── manage_diet.dart │ │ │ ├── manage_mail.dart │ │ │ └── manage_password.dart │ ├── story │ │ └── story_action_button.dart │ ├── story_editor │ │ ├── d_preview.dart │ │ └── recipe_search.dart │ ├── t_app_bar.dart │ ├── t_avatar_viewer.dart │ ├── t_book_card.dart │ ├── t_bubble.dart │ ├── t_button.dart │ ├── t_card.dart │ ├── t_carousel.dart │ ├── t_circular_avatar.dart │ ├── t_column.dart │ ├── t_content_card.dart │ ├── t_data_table.dart │ ├── t_empty_message.dart │ ├── t_gradient.dart │ ├── t_icon_button.dart │ ├── t_image.dart │ ├── t_image_label.dart │ ├── t_pageable.dart │ ├── t_pageable_bar.dart │ ├── t_pageable_content.dart │ ├── t_recipe_card.dart │ ├── t_responsive.dart │ ├── t_rounded_list_tile.dart │ ├── t_row.dart │ ├── t_scrollable_h.dart │ ├── t_search.dart │ ├── t_slide.dart │ ├── t_table.dart │ ├── t_text.dart │ ├── t_text_form_field.dart │ ├── t_wrap.dart │ └── user_management │ │ └── dialog │ │ ├── change_password_dialog.dart │ │ └── create_user_dialog.dart ├── drift │ ├── app_database.dart │ └── tables │ │ ├── draft_table.dart │ │ └── story_draft_table.dart ├── extensions │ ├── e_build_context.dart │ ├── e_date_time.dart │ ├── e_drift.dart │ ├── e_duration.dart │ ├── e_list.dart │ ├── e_number.dart │ ├── e_string.dart │ └── e_uri.dart ├── gen │ └── assets.gen.dart ├── interfaces │ ├── a_base_client.dart │ ├── a_base_page_provider.dart │ ├── a_filter_search_client.dart │ ├── a_pageable_client.dart │ ├── a_pageable_l10n_client.dart │ ├── a_search_client.dart │ └── a_search_l10n_client.dart ├── l10n │ ├── arb │ │ ├── app_de.arb │ │ └── app_en.arb │ └── generated │ │ ├── l10n.dart │ │ ├── l10n_de.dart │ │ └── l10n_en.dart ├── layouts │ └── main_layout.dart ├── main.dart ├── models │ ├── api │ │ └── login.dart │ ├── appLink │ │ └── app_link.dart │ ├── author │ │ └── author.dart │ ├── categories │ │ ├── category.dart │ │ └── category_group.dart │ ├── changelog │ │ └── changelog.dart │ ├── destination.dart │ ├── entity.dart │ ├── features │ │ └── features_response.dart │ ├── file │ │ ├── file.dart │ │ └── file_draft.dart │ ├── highlight.dart │ ├── library │ │ └── book.dart │ ├── pageable │ │ └── pageable.dart │ ├── recipe │ │ ├── course.dart │ │ ├── diet.dart │ │ ├── ingredients │ │ │ ├── ingredient.dart │ │ │ └── ingredient_group.dart │ │ ├── instructions │ │ │ ├── instruction.dart │ │ │ └── instruction_group.dart │ │ ├── nutrition │ │ │ └── nutrition.dart │ │ ├── recipe.dart │ │ ├── serving │ │ │ └── serving.dart │ │ └── unit_ref │ │ │ ├── unit_conversion.dart │ │ │ ├── unit_localized.dart │ │ │ └── unit_ref.dart │ ├── recipe_draft │ │ ├── ingredients │ │ │ ├── ingredient_draft.dart │ │ │ └── ingredient_group_draft.dart │ │ ├── instructions │ │ │ ├── instruction_draft.dart │ │ │ └── instruction_group_draft.dart │ │ ├── nutrition │ │ │ └── nutrition_draft.dart │ │ ├── recipe_draft.dart │ │ └── serving_draft │ │ │ └── serving_draft.dart │ ├── recipe_draft_wrapper │ │ ├── recipe_draft_wrapper.dart │ │ └── scrape_response.dart │ ├── search │ │ └── search_result.dart │ ├── story.dart │ ├── story_draft │ │ └── story_draft.dart │ ├── t_theme.dart │ ├── tag │ │ └── tag.dart │ ├── tag_draft │ │ └── tag_draft.dart │ ├── unit.dart │ ├── user │ │ ├── role.dart │ │ ├── token.dart │ │ └── user.dart │ └── version │ │ └── version.dart ├── pages │ ├── authors │ │ └── _id.dart │ ├── categories │ │ ├── _id.dart │ │ └── index.dart │ ├── editor │ │ └── _id.dart │ ├── home │ │ └── index.dart │ ├── library │ │ ├── _id.dart │ │ └── index.dart │ ├── login │ │ └── login_page.dart │ ├── more │ │ └── index.dart │ ├── no_connection │ │ └── index.dart │ ├── public_recipe │ │ ├── index.dart │ │ └── variations │ │ │ ├── desktop.dart │ │ │ └── mobile.dart │ ├── recipe │ │ ├── index.dart │ │ └── variations │ │ │ ├── desktop.dart │ │ │ └── mobile.dart │ ├── recipe_drafts │ │ └── index.dart │ ├── recipes │ │ └── _id.dart │ ├── recovery │ │ └── index.dart │ ├── registration │ │ └── index.dart │ ├── server_outdated │ │ └── index.dart │ ├── settings │ │ └── index.dart │ ├── share_management │ │ └── index.dart │ ├── splash │ │ └── index.dart │ ├── story │ │ └── _id.dart │ ├── story_drafts │ │ └── index.dart │ ├── story_editor │ │ └── _id.dart │ ├── tags │ │ ├── _id.dart │ │ └── index.dart │ └── user_management │ │ └── index.dart ├── riverpod │ ├── api │ │ └── p_api.dart │ ├── app_links │ │ └── p_app_links.dart │ ├── auth_state │ │ └── p_auth_state.dart │ ├── authors │ │ ├── p_author.dart │ │ ├── p_author_page.dart │ │ ├── p_authors.dart │ │ └── p_authors_page.dart │ ├── categories │ │ ├── p_categories.dart │ │ ├── p_categories_page.dart │ │ ├── p_category.dart │ │ ├── p_category_page.dart │ │ └── p_raw_categories.dart │ ├── category_group │ │ ├── p_category_groups.dart │ │ └── p_category_groups_page.dart │ ├── changelog │ │ └── p_changelog.dart │ ├── drift │ │ └── p_drift.dart │ ├── features │ │ ├── p_compatibility.dart │ │ ├── p_feature_bring.dart │ │ ├── p_feature_open_food_facts.dart │ │ ├── p_feature_recovery.dart │ │ ├── p_feature_registration.dart │ │ ├── p_feature_share.dart │ │ ├── p_feature_story.dart │ │ └── p_features.dart │ ├── go_router │ │ └── p_go_router.dart │ ├── highlights │ │ └── p_highlight.dart │ ├── library │ │ ├── p_book.dart │ │ ├── p_book_page.dart │ │ ├── p_book_recipes.dart │ │ ├── p_library.dart │ │ ├── p_library_page.dart │ │ └── p_whole_library.dart │ ├── package_info │ │ └── p_package_info.dart │ ├── public_recipes │ │ └── p_public_recipe.dart │ ├── recipe_draft │ │ ├── p_recipe_draft.dart │ │ ├── p_recipe_draft_categories.dart │ │ └── p_recipe_drafts.dart │ ├── recipes │ │ ├── p_action_button.dart │ │ ├── p_latest_recipes.dart │ │ ├── p_recipe.dart │ │ ├── p_recipes.dart │ │ └── p_recipes_page.dart │ ├── root_bundle │ │ └── p_backend_url.dart │ ├── search │ │ ├── p_recipe_search.dart │ │ ├── p_recipe_search_term.dart │ │ ├── p_search.dart │ │ └── p_search_term.dart │ ├── shared_preferences │ │ ├── p_server.dart │ │ ├── p_shared_preferences.dart │ │ └── p_tokens.dart │ ├── stories │ │ ├── p_action_button.dart │ │ ├── p_stories.dart │ │ └── p_story.dart │ ├── story_draft │ │ ├── p_story_draft.dart │ │ └── p_story_drafts.dart │ ├── tags │ │ ├── p_tag.dart │ │ ├── p_tag_page.dart │ │ ├── p_tags.dart │ │ └── p_tags_page.dart │ ├── theme │ │ ├── p_custom_color.dart │ │ ├── p_dynamic_color.dart │ │ ├── p_theme.dart │ │ └── p_theme_mode.dart │ ├── tokens │ │ └── p_tokens.dart │ ├── units │ │ ├── p_unit_conversions.dart │ │ └── p_units.dart │ └── user │ │ ├── p_user.dart │ │ └── p_users.dart ├── router │ └── routes.dart └── utils │ ├── constants.dart │ ├── custom_mappers │ ├── custom_mappers.dart │ ├── duration_mapper.dart │ ├── uri_mapper.dart │ └── version_converter.dart │ ├── u_app_link.dart │ ├── u_double.dart │ ├── u_duration.dart │ ├── u_go_router.dart │ ├── u_image.dart │ ├── u_localizations.dart │ ├── u_os.dart │ └── u_validator.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── main.cc ├── my_application.cc └── my_application.h ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── InfoPlist.xcstrings ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ └── app_icon_64.png │ ├── Base.lproj │ │ └── MainMenu.xib │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── Info.plist │ ├── MainFlutterWindow.swift │ ├── Release.entitlements │ └── de.lproj │ │ └── MainMenu.strings └── RunnerTests │ └── RunnerTests.swift ├── pubspec.lock ├── pubspec.yaml ├── test └── widget_test.dart ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html └── manifest.json └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.github/workflows/clean-workflow.yaml: -------------------------------------------------------------------------------- 1 | name: Clean workflow 2 | 3 | on: workflow_dispatch 4 | 5 | env: 6 | REGISTRY: ghcr.io 7 | IMAGE_NAME: ${{ github.repository }} 8 | 9 | jobs: 10 | clean_images: 11 | runs-on: ubuntu-latest 12 | 13 | permissions: 14 | contents: read 15 | packages: write 16 | id-token: write 17 | 18 | steps: 19 | - name: Login to GitHub Container Registry 20 | uses: docker/login-action@v3 21 | with: 22 | registry: ${{ env.REGISTRY }} 23 | username: ${{ github.actor }} 24 | password: ${{ secrets.GITHUB_TOKEN }} 25 | 26 | - name: Remove untagged images 27 | uses: dataaxiom/ghcr-cleanup-action@v1 28 | with: 29 | delete-untagged: true 30 | token: ${{ secrets.GITHUB_TOKEN }} 31 | package: flavormate-webapp 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | .build.env 16 | 17 | *.g.dart 18 | *.mapper.dart 19 | 20 | # build assets 21 | build_assets/android/keystore 22 | android/key.properties 23 | 24 | # IntelliJ related 25 | *.iml 26 | *.ipr 27 | *.iws 28 | .idea/ 29 | 30 | # The .vscode folder contains launch configuration and tasks you configure in 31 | # VS Code which you may wish to be included in version control, so this line 32 | # is commented out by default. 33 | #.vscode/ 34 | 35 | # Flutter/Dart/Pub related 36 | **/doc/api/ 37 | **/ios/Flutter/.last_build_id 38 | .dart_tool/ 39 | .flutter-plugins 40 | .flutter-plugins-dependencies 41 | .pub-cache/ 42 | .pub/ 43 | /build/ 44 | 45 | # Symbolication related 46 | app.*.symbols 47 | 48 | # Obfuscation related 49 | app.*.map.json 50 | 51 | # Android Studio will place build artifacts here 52 | /android/app/debug 53 | /android/app/profile 54 | /android/app/release 55 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/to/reference-keystore 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/de/thenus/cookbook_app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package de.flavormate 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity : FlutterActivity() 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-hdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-mdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-xhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-xxhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = "../build" 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(":app") 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | android.defaults.buildfeatures.buildconfig=true 5 | android.nonTransitiveRClass=false 6 | android.nonFinalResIds=false 7 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Sep 28 21:27:14 CEST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version '8.6.1' apply false 22 | id "org.jetbrains.kotlin.android" version "2.0.20" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /assets/icons/bring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/assets/icons/bring.png -------------------------------------------------------------------------------- /assets/icons/logo_transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/assets/icons/logo_transparent.png -------------------------------------------------------------------------------- /assets/images/no_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/assets/images/no_image.png -------------------------------------------------------------------------------- /assets/web/backend_url.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/assets/web/backend_url.txt -------------------------------------------------------------------------------- /build_assets/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginxinc/nginx-unprivileged 2 | 3 | COPY build/web /usr/share/nginx/html/ 4 | 5 | USER root 6 | 7 | RUN chown 101:101 /usr/share/nginx/html/assets/assets/web/backend_url.txt 8 | 9 | USER 101 10 | 11 | COPY build_assets/docker/entrypoint.sh / 12 | 13 | SHELL ["/bin/bash", "-c"] 14 | ENTRYPOINT sh /entrypoint.sh 15 | -------------------------------------------------------------------------------- /build_assets/docker/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Setting backend url to $BACKEND_URL" 4 | echo "$BACKEND_URL" > /usr/share/nginx/html/assets/assets/web/backend_url.txt 5 | nginx -g 'daemon off;' 6 | -------------------------------------------------------------------------------- /build_assets/icons/android/adaptive_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/build_assets/icons/android/adaptive_background.png -------------------------------------------------------------------------------- /build_assets/icons/android/adaptive_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/build_assets/icons/android/adaptive_foreground.png -------------------------------------------------------------------------------- /build_assets/icons/android/light_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/build_assets/icons/android/light_icon.png -------------------------------------------------------------------------------- /build_assets/icons/android/monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/build_assets/icons/android/monochrome.png -------------------------------------------------------------------------------- /build_assets/icons/ios/dark_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/build_assets/icons/ios/dark_icon.png -------------------------------------------------------------------------------- /build_assets/icons/ios/light_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/build_assets/icons/ios/light_icon.png -------------------------------------------------------------------------------- /build_assets/icons/ios/tinted_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/build_assets/icons/ios/tinted_icon.png -------------------------------------------------------------------------------- /build_assets/icons/macos/light_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/build_assets/icons/macos/light_icon.png -------------------------------------------------------------------------------- /build_assets/icons/web/light_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/build_assets/icons/web/light_icon.png -------------------------------------------------------------------------------- /build_assets/icons/windows/light_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/build_assets/icons/windows/light_icon.png -------------------------------------------------------------------------------- /build_assets/ios/ExportOptions.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | destination 6 | export 7 | manageAppVersionAndBuildNumber 8 | 9 | method 10 | app-store-connect 11 | provisioningProfiles 12 | 13 | de.flavormate 14 | FlavorMate (Connect) 15 | 16 | signingCertificate 17 | Apple Distribution 18 | signingStyle 19 | manual 20 | stripSwiftSymbols 21 | 22 | teamID 23 | 7LZRXMAGPW 24 | uploadSymbols 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /devtools_options.yaml: -------------------------------------------------------------------------------- 1 | description: This file stores settings for Dart & Flutter DevTools. 2 | documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states 3 | extensions: 4 | -------------------------------------------------------------------------------- /docs/features/dashboard/phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/docs/features/dashboard/phone.png -------------------------------------------------------------------------------- /docs/features/dashboard/tablet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/docs/features/dashboard/tablet.png -------------------------------------------------------------------------------- /docs/features/diet/phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/docs/features/diet/phone.png -------------------------------------------------------------------------------- /docs/features/diet/tablet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/docs/features/diet/tablet.png -------------------------------------------------------------------------------- /docs/features/editor/phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/docs/features/editor/phone.png -------------------------------------------------------------------------------- /docs/features/editor/tablet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/docs/features/editor/tablet.png -------------------------------------------------------------------------------- /docs/features/library/phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/docs/features/library/phone.png -------------------------------------------------------------------------------- /docs/features/library/tablet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/docs/features/library/tablet.png -------------------------------------------------------------------------------- /docs/features/recipe/phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/docs/features/recipe/phone.png -------------------------------------------------------------------------------- /docs/features/recipe/tablet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/docs/features/recipe/tablet.png -------------------------------------------------------------------------------- /docs/features/theme/phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/docs/features/theme/phone.png -------------------------------------------------------------------------------- /docs/features/theme/tablet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/docs/features/theme/tablet.png -------------------------------------------------------------------------------- /examples/docker-compose-traefik.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | frontend: 3 | container_name: flavormate_webapp 4 | image: ghcr.io/flavormate/flavormate-webapp 5 | restart: unless-stopped 6 | environment: 7 | - BACKEND_URL=https://example.com 8 | labels: 9 | - "traefik.enable=true" 10 | - "traefik.http.routers.flavormate_webapp.rule=(Host(`app.example.com`))" 11 | - "traefik.http.routers.flavormate_webapp.tls=true" 12 | - "traefik.docker.network=proxy" 13 | - "traefik.http.services.flavormate_webapp.loadbalancer.server.port=8080" 14 | networks: 15 | - proxy 16 | healthcheck: 17 | test: [ "CMD-SHELL", "curl -so /dev/null http://localhost:8080/ || exit 1" ] 18 | interval: 20s 19 | timeout: 5s 20 | retries: 5 21 | start_period: 5s 22 | 23 | 24 | networks: 25 | proxy: 26 | external: true 27 | -------------------------------------------------------------------------------- /examples/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | frontend: 3 | container_name: flavormate_webapp 4 | image: ghcr.io/flavormate/flavormate-webapp 5 | restart: unless-stopped 6 | environment: 7 | - BACKEND_URL=https://example.com 8 | ports: 9 | - 8080:8080 10 | healthcheck: 11 | test: [ "CMD-SHELL", "curl -so /dev/null http://localhost:8080/ || exit 1" ] 12 | interval: 20s 13 | timeout: 5s 14 | retries: 5 15 | start_period: 5s 16 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-68x68@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-68x68@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-68x68@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-68x68@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-68x68@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-68x68@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by cookbook_appacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, 6 | selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. 7 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /ios/Runner/Runner.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.associated-domains 6 | 7 | webcredentials:flavormate.de 8 | applinks:flavormate.de 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ios/Runner/de.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ios/Runner/de.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /l10n.yaml: -------------------------------------------------------------------------------- 1 | arb-dir: lib/l10n/arb 2 | output-dir: lib/l10n/generated 3 | template-arb-file: app_en.arb 4 | output-localization-file: l10n.dart 5 | output-class: L10n 6 | synthetic-package: false 7 | nullable-getter: false 8 | -------------------------------------------------------------------------------- /lib/clients/authors_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_search_client.dart'; 2 | import 'package:flavormate/models/author/author.dart'; 3 | import 'package:flavormate/models/pageable/pageable.dart'; 4 | import 'package:flavormate/models/recipe/recipe.dart'; 5 | 6 | class AuthorsClient extends ASearchClient { 7 | AuthorsClient({ 8 | required super.httpClient, 9 | required super.baseURL, 10 | required super.parser, 11 | }); 12 | 13 | Future> findRecipesInAuthor( 14 | int id, { 15 | required int page, 16 | }) async { 17 | final params = getParams({ 18 | 'size': 6, 19 | 'sortBy': 'r.label', 20 | 'sortDirection': 'ASC', 21 | 'page': page, 22 | }); 23 | 24 | final response = await httpClient.get('$baseURL/$id/recipes?$params'); 25 | 26 | return Pageable.fromMap(response.data!, RecipeMapper.fromMap); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/clients/bring_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | class BringClient { 4 | final Dio httpClient; 5 | final String baseURL; 6 | 7 | BringClient({required this.httpClient, required this.baseURL}); 8 | 9 | Future post( 10 | String server, 11 | int id, 12 | int baseServing, 13 | int requestedServing, 14 | ) async { 15 | final api = '$server$baseURL/$id/$requestedServing'; 16 | 17 | final response = await httpClient.post( 18 | 'https://api.getbring.com/rest/bringrecipes/deeplink', 19 | data: { 20 | 'url': api, 21 | 'baseQuantity': requestedServing, 22 | 'requestedQuantity': requestedServing, 23 | 'source': 'app', 24 | }, 25 | ); 26 | 27 | return response.data['deeplink']; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/clients/category_group_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flavormate/models/categories/category_group.dart'; 3 | import 'package:flutter/foundation.dart'; 4 | 5 | class CategoryGroupClient { 6 | @protected 7 | final Dio httpClient; 8 | @protected 9 | final String baseURL; 10 | @protected 11 | final CategoryGroup Function(Map) parser; 12 | 13 | CategoryGroupClient({ 14 | required this.httpClient, 15 | required this.baseURL, 16 | required this.parser, 17 | }); 18 | 19 | Future> findAll({required String language}) async { 20 | final response = await httpClient.get>( 21 | '$baseURL/?language=$language', 22 | ); 23 | 24 | List> data = List.from(response.data!); 25 | 26 | return data.map(parser).toList(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/clients/features_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_client.dart'; 2 | import 'package:flavormate/models/features/features_response.dart'; 3 | 4 | class FeaturesClient extends ABaseClient { 5 | FeaturesClient({ 6 | required super.httpClient, 7 | required super.baseURL, 8 | required super.parser, 9 | }); 10 | 11 | Future get() async { 12 | final response = await httpClient.get('$baseURL/'); 13 | return FeaturesResponseMapper.fromMap(response.data!); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/clients/files_client.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flavormate/interfaces/a_base_client.dart'; 5 | import 'package:flavormate/models/file/file.dart'; 6 | 7 | class FilesClient extends ABaseClient { 8 | FilesClient({ 9 | required super.httpClient, 10 | required super.baseURL, 11 | required super.parser, 12 | }); 13 | 14 | Future downloadRaw(String url) async { 15 | final response = await httpClient.get( 16 | url, 17 | options: Options(responseType: ResponseType.bytes), 18 | ); 19 | 20 | return base64Encode(response.data); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/clients/highlights_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_pageable_client.dart'; 2 | import 'package:flavormate/models/highlight.dart'; 3 | import 'package:flavormate/models/pageable/pageable.dart'; 4 | import 'package:flavormate/models/recipe/diet.dart'; 5 | 6 | class HighlightsClient extends APageableClient { 7 | HighlightsClient({ 8 | required super.httpClient, 9 | required super.baseURL, 10 | required super.parser, 11 | }); 12 | 13 | Future> findAllByDiet({ 14 | int? page, 15 | int? size, 16 | String? sortBy, 17 | String? sortDirection, 18 | required Diet filter, 19 | }) async { 20 | final params = getParams({ 21 | 'size': size, 22 | 'sortBy': sortBy, 23 | 'sortDirection': sortDirection, 24 | 'page': page, 25 | }); 26 | 27 | final response = await httpClient.get( 28 | '$baseURL/list/${filter.name}?$params', 29 | ); 30 | 31 | return Pageable.fromMap(response.data!, parser); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/clients/interceptor_methods.dart: -------------------------------------------------------------------------------- 1 | class InterceptorMethods { 2 | final void Function() onUnauthenticated; 3 | final void Function() onNoConnection; 4 | 5 | InterceptorMethods({ 6 | required this.onUnauthenticated, 7 | required this.onNoConnection, 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /lib/clients/public_recipe_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_client.dart'; 2 | import 'package:flavormate/models/recipe/recipe.dart'; 3 | 4 | class PublicRecipeClient extends ABaseClient { 5 | PublicRecipeClient({ 6 | required super.httpClient, 7 | required super.baseURL, 8 | required super.parser, 9 | }); 10 | 11 | Future findByIdL10n(String token, int id, String language) async { 12 | final params = getParams({'token': token, 'language': language}); 13 | final response = await httpClient.get('$baseURL/$id/l10n?$params'); 14 | return parser(response.data); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/clients/self_service_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | class SelfServiceClient { 4 | final Dio httpClient; 5 | final String baseURL; 6 | 7 | SelfServiceClient({required this.httpClient, required this.baseURL}); 8 | 9 | Future recovery(String mail) async { 10 | try { 11 | final response = await httpClient.put( 12 | '$baseURL/recovery/$mail/password/reset', 13 | ); 14 | return response.data!; 15 | } catch (_) { 16 | return false; 17 | } 18 | } 19 | 20 | Future registration({ 21 | required String displayName, 22 | required String username, 23 | required String mail, 24 | required String password, 25 | }) async { 26 | try { 27 | final response = await httpClient.post( 28 | '$baseURL/registration/', 29 | data: { 30 | 'displayName': displayName, 31 | 'username': username, 32 | 'mail': mail, 33 | 'password': password, 34 | }, 35 | ); 36 | return response.data!; 37 | } catch (e) { 38 | return false; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/clients/stories_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_search_client.dart'; 2 | import 'package:flavormate/models/story.dart'; 3 | 4 | class StoriesClient extends ASearchClient { 5 | StoriesClient({ 6 | required super.httpClient, 7 | required super.baseURL, 8 | required super.parser, 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /lib/clients/tags_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_search_client.dart'; 2 | import 'package:flavormate/models/pageable/pageable.dart'; 3 | import 'package:flavormate/models/recipe/recipe.dart'; 4 | import 'package:flavormate/models/tag/tag.dart'; 5 | 6 | class TagsClient extends ASearchClient { 7 | TagsClient({ 8 | required super.httpClient, 9 | required super.baseURL, 10 | required super.parser, 11 | }); 12 | 13 | Future> findAllRecipesInTag(int id, int page) async { 14 | final params = getParams({ 15 | 'size': 6, 16 | 'sortBy': 'label', 17 | 'sortDirection': 'ASC', 18 | 'page': page, 19 | }); 20 | 21 | final response = await httpClient.get('$baseURL/$id/recipes?$params'); 22 | 23 | return Pageable.fromMap(response.data!, RecipeMapper.fromMap); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/clients/token_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_client.dart'; 2 | import 'package:flavormate/models/user/token.dart'; 3 | 4 | class TokenClient extends ABaseClient { 5 | TokenClient({ 6 | required super.httpClient, 7 | required super.baseURL, 8 | required super.parser, 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /lib/clients/units_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_client.dart'; 2 | import 'package:flavormate/models/recipe/unit_ref/unit_conversion.dart'; 3 | import 'package:flavormate/models/recipe/unit_ref/unit_localized.dart'; 4 | 5 | class UnitsClient extends ABaseClient { 6 | UnitsClient({ 7 | required super.httpClient, 8 | required super.baseURL, 9 | required super.parser, 10 | }); 11 | 12 | Future> getAllConversions() async { 13 | final response = await httpClient.get>( 14 | '$baseURL/conversions', 15 | ); 16 | 17 | return response.data! 18 | .map((ul) => UnitConversionMapper.fromMap(ul)) 19 | .toList(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/clients/user_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_search_client.dart'; 2 | import 'package:flavormate/models/user/user.dart'; 3 | 4 | class UserClient extends ASearchClient { 5 | UserClient({ 6 | required super.httpClient, 7 | required super.baseURL, 8 | required super.parser, 9 | }); 10 | 11 | Future getUser() async { 12 | final response = await httpClient.get('$baseURL/info'); 13 | 14 | return UserMapper.fromMap(response.data); 15 | } 16 | 17 | Future setPassword(int id, Map password) async { 18 | final response = await httpClient.put( 19 | '$baseURL/$id/password', 20 | data: password, 21 | ); 22 | 23 | return response.data ?? false; 24 | } 25 | 26 | Future forcePassword(int id, Map password) async { 27 | final response = await httpClient.put( 28 | '$baseURL/$id/password/force', 29 | data: password, 30 | ); 31 | 32 | return response.data ?? false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/components/dialogs/t_confirm_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/l10n/generated/l10n.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:go_router/go_router.dart'; 4 | 5 | class TConfirmDialog extends StatelessWidget { 6 | final String title; 7 | final String? content; 8 | 9 | const TConfirmDialog({super.key, required this.title, this.content}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return AlertDialog( 14 | title: Text(title), 15 | content: content != null ? Text(content!) : null, 16 | actions: [ 17 | TextButton( 18 | onPressed: () => context.pop(false), 19 | child: Text(L10n.of(context).btn_cancel), 20 | ), 21 | FilledButton( 22 | onPressed: () => context.pop(true), 23 | child: Text(L10n.of(context).btn_yes), 24 | ), 25 | ], 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/components/dialogs/t_loading_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_card.dart'; 2 | import 'package:flavormate/components/t_column.dart'; 3 | import 'package:flavormate/components/t_text.dart'; 4 | import 'package:flavormate/l10n/generated/l10n.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class TLoadingDialog extends StatelessWidget { 8 | const TLoadingDialog({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Dialog.fullscreen( 13 | backgroundColor: Colors.black.withAlpha(50), 14 | child: Center( 15 | child: SizedBox( 16 | height: 200, 17 | width: 350, 18 | child: TCard( 19 | child: TColumn( 20 | mainAxisSize: MainAxisSize.min, 21 | mainAxisAlignment: MainAxisAlignment.center, 22 | children: [ 23 | const CircularProgressIndicator(), 24 | TText(L10n.of(context).c_search_loading, TextStyles.titleLarge), 25 | ], 26 | ), 27 | ), 28 | ), 29 | ), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/components/go_router/wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:go_router/go_router.dart'; 3 | 4 | // Should fix https://github.com/flutter/flutter/issues/120353 5 | class Wrapper extends StatelessWidget { 6 | const Wrapper({super.key, required this.child}); 7 | 8 | final Widget child; 9 | 10 | bool canPop(BuildContext context) { 11 | final lastMatch = 12 | GoRouter.of( 13 | context, 14 | ).routerDelegate.currentConfiguration.matches.lastOrNull; 15 | 16 | if (lastMatch is ShellRouteMatch) { 17 | return lastMatch.matches.length == 1; 18 | } 19 | return true; 20 | } 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return PopScope(canPop: canPop(context), child: child); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/components/login/server_major_incompatible.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_card.dart'; 2 | import 'package:flavormate/components/t_row.dart'; 3 | import 'package:flavormate/components/t_text.dart'; 4 | import 'package:flavormate/l10n/generated/l10n.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; 7 | 8 | class ServerMajorIncompatible extends StatelessWidget { 9 | const ServerMajorIncompatible({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return TCard( 14 | color: Colors.red, 15 | child: TRow( 16 | children: [ 17 | Icon(MdiIcons.closeOctagonOutline), 18 | SizedBox( 19 | child: TText( 20 | L10n.of( 21 | context, 22 | ).p_login_server_outdated_major.replaceAll('\\n', '\n'), 23 | TextStyles.bodyMedium, 24 | color: TextColor.white, 25 | ), 26 | ), 27 | ], 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/components/login/server_minor_incompatible.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_card.dart'; 2 | import 'package:flavormate/components/t_row.dart'; 3 | import 'package:flavormate/components/t_text.dart'; 4 | import 'package:flavormate/l10n/generated/l10n.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; 7 | 8 | class ServerMinorIncompatible extends StatelessWidget { 9 | const ServerMinorIncompatible({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return TCard( 14 | color: Colors.orange, 15 | child: TRow( 16 | children: [ 17 | Icon(MdiIcons.alertCircleOutline), 18 | TText( 19 | L10n.of( 20 | context, 21 | ).p_login_server_outdated_minor.replaceAll('\\n', '\n'), 22 | TextStyles.bodyMedium, 23 | color: TextColor.white, 24 | ), 25 | ], 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/components/public_recipe/public_recipe_categories.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_text.dart'; 2 | import 'package:flavormate/l10n/generated/l10n.dart'; 3 | import 'package:flavormate/models/categories/category.dart'; 4 | import 'package:flavormate/utils/constants.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class PublicRecipeCategories extends StatelessWidget { 8 | final List categories; 9 | 10 | const PublicRecipeCategories({super.key, required this.categories}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Column( 15 | children: [ 16 | TText(L10n.of(context).c_recipe_categories, TextStyles.headlineMedium), 17 | const SizedBox(height: PADDING), 18 | Wrap( 19 | spacing: PADDING, 20 | runSpacing: PADDING, 21 | alignment: WrapAlignment.center, 22 | runAlignment: WrapAlignment.center, 23 | children: 24 | categories 25 | .map((category) => Chip(label: Text(category.label))) 26 | .toList(), 27 | ), 28 | ], 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/components/public_recipe/public_recipe_tags.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_text.dart'; 2 | import 'package:flavormate/l10n/generated/l10n.dart'; 3 | import 'package:flavormate/models/tag/tag.dart'; 4 | import 'package:flavormate/utils/constants.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class PublicRecipeTags extends StatelessWidget { 8 | final List tags; 9 | 10 | const PublicRecipeTags({super.key, required this.tags}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Column( 15 | children: [ 16 | TText(L10n.of(context).c_recipe_tags, TextStyles.headlineMedium), 17 | const SizedBox(height: PADDING), 18 | Wrap( 19 | spacing: PADDING, 20 | runSpacing: PADDING, 21 | alignment: WrapAlignment.center, 22 | runAlignment: WrapAlignment.center, 23 | children: 24 | tags 25 | .map((category) => Chip(label: Text(category.label))) 26 | .toList(), 27 | ), 28 | ], 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/components/recipe/bring_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/gen/assets.gen.dart'; 2 | import 'package:flavormate/l10n/generated/l10n.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class BringButton extends StatelessWidget { 6 | final VoidCallback onPressed; 7 | final double? width, height; 8 | 9 | static const Color _color = Color.fromARGB(255, 51, 69, 78); 10 | 11 | const BringButton({ 12 | required this.onPressed, 13 | this.height = 48, 14 | this.width, 15 | super.key, 16 | }); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return SizedBox( 21 | height: height, 22 | width: width, 23 | child: FilledButton( 24 | onPressed: onPressed, 25 | style: FilledButton.styleFrom( 26 | backgroundColor: _color, 27 | foregroundColor: Colors.white, 28 | ), 29 | child: Row( 30 | children: [ 31 | Assets.icons.bring.image(height: 32), 32 | Expanded( 33 | child: Text( 34 | L10n.of(context).c_bring_btn, 35 | textAlign: TextAlign.center, 36 | ), 37 | ), 38 | ], 39 | ), 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/components/recipe/recipe_description.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_text.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class RecipeDescription extends StatelessWidget { 5 | final String description; 6 | 7 | const RecipeDescription({super.key, required this.description}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return TText(description, TextStyles.bodyMedium); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/components/recipe/recipe_informations.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_text.dart'; 2 | import 'package:flavormate/l10n/generated/l10n.dart'; 3 | import 'package:flavormate/models/recipe/course.dart'; 4 | import 'package:flavormate/models/recipe/diet.dart'; 5 | import 'package:flavormate/utils/constants.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | class RecipeInformations extends StatelessWidget { 9 | final Course course; 10 | final Diet diet; 11 | 12 | const RecipeInformations({ 13 | super.key, 14 | required this.course, 15 | required this.diet, 16 | }); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Column( 21 | children: [ 22 | TText( 23 | L10n.of(context).c_recipe_informations, 24 | TextStyles.headlineMedium, 25 | ), 26 | const SizedBox(height: PADDING), 27 | Wrap( 28 | runSpacing: PADDING, 29 | spacing: PADDING, 30 | children: [ 31 | Chip(avatar: Icon(diet.icon), label: Text(diet.getName(context))), 32 | Chip( 33 | avatar: Icon(course.icon), 34 | label: Text(course.getName(context)), 35 | ), 36 | ], 37 | ), 38 | ], 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/components/recipe/recipe_published.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_column.dart'; 2 | import 'package:flavormate/components/t_text.dart'; 3 | import 'package:flavormate/extensions/e_date_time.dart'; 4 | import 'package:flavormate/l10n/generated/l10n.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:url_launcher/url_launcher.dart'; 7 | 8 | class RecipePublished extends StatelessWidget { 9 | final DateTime createdOn; 10 | final Uri? url; 11 | final int version; 12 | 13 | const RecipePublished({ 14 | super.key, 15 | required this.createdOn, 16 | required this.url, 17 | required this.version, 18 | }); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return TColumn( 23 | children: [ 24 | TText(L10n.of(context).c_recipe_published, TextStyles.headlineMedium), 25 | Row( 26 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 27 | children: [ 28 | Text(createdOn.toLocalDateString(context)), 29 | Text('${L10n.of(context).c_recipe_published_version}: $version'), 30 | ], 31 | ), 32 | if (url != null) 33 | OutlinedButton( 34 | onPressed: () => launchUrl((url!)), 35 | child: Text(L10n.of(context).c_recipe_published_origin), 36 | ), 37 | ], 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/components/recipe/recipe_title.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_text.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class RecipeTitle extends StatelessWidget { 5 | final String title; 6 | 7 | const RecipeTitle({super.key, required this.title}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return TText(title, TextStyles.displayMedium, textAlign: TextAlign.center); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/components/recipe_editor/dialogs/d_course.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/dialogs/t_alert_dialog.dart'; 2 | import 'package:flavormate/components/t_button.dart'; 3 | import 'package:flavormate/components/t_column.dart'; 4 | import 'package:flavormate/l10n/generated/l10n.dart'; 5 | import 'package:flavormate/models/recipe/course.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; 8 | import 'package:go_router/go_router.dart'; 9 | 10 | class DCourse extends StatelessWidget { 11 | final Course? course; 12 | 13 | const DCourse({super.key, required this.course}); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return TAlertDialog( 18 | title: L10n.of(context).d_editor_course_title, 19 | child: TColumn( 20 | mainAxisSize: MainAxisSize.min, 21 | children: [ 22 | for (final course in Course.values) 23 | TButton( 24 | onPressed: () => context.pop(course), 25 | leading: Icon(course.icon), 26 | label: course.getName(context), 27 | trailing: 28 | this.course == course 29 | ? const Icon(MdiIcons.checkCircleOutline) 30 | : null, 31 | ), 32 | ], 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/components/recipe_editor/dialogs/d_diet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/dialogs/t_alert_dialog.dart'; 2 | import 'package:flavormate/components/t_button.dart'; 3 | import 'package:flavormate/components/t_column.dart'; 4 | import 'package:flavormate/l10n/generated/l10n.dart'; 5 | import 'package:flavormate/models/recipe/diet.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; 8 | import 'package:go_router/go_router.dart'; 9 | 10 | class DDiet extends StatelessWidget { 11 | final Diet? diet; 12 | 13 | const DDiet({super.key, required this.diet}); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return TAlertDialog( 18 | title: L10n.of(context).d_editor_diet_title, 19 | child: TColumn( 20 | mainAxisSize: MainAxisSize.min, 21 | children: [ 22 | for (final diet in Diet.values) 23 | TButton( 24 | onPressed: () => context.pop(diet), 25 | leading: Icon(diet.icon), 26 | label: diet.getName(context), 27 | trailing: 28 | this.diet == diet 29 | ? const Icon(MdiIcons.checkCircleOutline) 30 | : null, 31 | ), 32 | ], 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/components/recipe_editor/preview/preview_tags.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_text.dart'; 2 | import 'package:flavormate/l10n/generated/l10n.dart'; 3 | import 'package:flavormate/models/tag_draft/tag_draft.dart'; 4 | import 'package:flavormate/utils/constants.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class PreviewTags extends StatelessWidget { 8 | const PreviewTags({super.key, required this.tags}); 9 | 10 | final List tags; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Column( 15 | children: [ 16 | TText(L10n.of(context).c_recipe_tags, TextStyles.headlineMedium), 17 | const SizedBox(height: PADDING), 18 | Wrap( 19 | spacing: PADDING, 20 | runSpacing: PADDING, 21 | alignment: WrapAlignment.center, 22 | runAlignment: WrapAlignment.center, 23 | children: 24 | tags 25 | .map((category) => Chip(label: Text(category.label))) 26 | .toList(), 27 | ), 28 | ], 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/components/recipe_editor/t_progress.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_text.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; 4 | 5 | class TProgress extends StatelessWidget { 6 | final bool optional; 7 | final double completed; 8 | 9 | const TProgress({super.key, required this.completed, this.optional = false}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | if (completed <= 0) { 14 | if (optional) { 15 | return const Icon(MdiIcons.minusCircleOutline); 16 | } else { 17 | return const Icon(MdiIcons.alertCircleOutline); 18 | } 19 | } else if (completed >= 100) { 20 | return const Icon(MdiIcons.checkCircleOutline); 21 | } else { 22 | return Padding( 23 | padding: const EdgeInsets.only(right: 4), 24 | child: SizedBox( 25 | height: 16, 26 | width: 16, 27 | child: CircularProgressIndicator( 28 | value: completed / 100, 29 | strokeWidth: 3, 30 | color: TText.getColor(context, TextColor.filledButton), 31 | ), 32 | ), 33 | ); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/components/riverpod/error_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ErrorPage extends StatelessWidget { 4 | final String error; 5 | const ErrorPage(this.error, {super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | backgroundColor: Theme.of(context).colorScheme.inversePrimary, 12 | ), 13 | body: Center(child: Text('Error: $error')), 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/components/riverpod/loading_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class LoadingPage extends StatelessWidget { 4 | const LoadingPage({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return Scaffold( 9 | appBar: AppBar( 10 | backgroundColor: Theme.of(context).colorScheme.inversePrimary, 11 | ), 12 | body: const Center(child: CircularProgressIndicator()), 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/components/riverpod/r_feature.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/riverpod/r_struct.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | 5 | class RFeature extends StatelessWidget { 6 | final AsyncValue provider; 7 | final Widget Function(BuildContext) builder; 8 | final Widget? loadingChild; 9 | final Widget? errorChild; 10 | 11 | const RFeature( 12 | this.provider, 13 | this.builder, { 14 | this.loadingChild, 15 | this.errorChild, 16 | super.key, 17 | }); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return RStruct( 22 | provider, 23 | (ctx, visible) => Visibility(visible: visible, child: builder(ctx)), 24 | loadingChild: loadingChild, 25 | errorChild: errorChild, 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/components/riverpod/r_struct.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | 4 | class RStruct extends StatelessWidget { 5 | final AsyncValue provider; 6 | final Widget Function(BuildContext, T) builder; 7 | final Widget? loadingChild; 8 | final Widget? errorChild; 9 | 10 | const RStruct( 11 | this.provider, 12 | this.builder, { 13 | this.loadingChild, 14 | this.errorChild, 15 | super.key, 16 | }); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return switch (provider) { 21 | AsyncData(value: final value) => builder(context, value), 22 | AsyncError(:final error) => errorChild ?? Text('$error'), 23 | _ => loadingChild ?? const Center(child: CircularProgressIndicator()), 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/components/search/loading_search.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_text.dart'; 2 | import 'package:flavormate/l10n/generated/l10n.dart'; 3 | import 'package:flavormate/utils/constants.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class LoadingSearch extends StatelessWidget { 7 | const LoadingSearch({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Center( 12 | child: Column( 13 | mainAxisAlignment: MainAxisAlignment.center, 14 | children: [ 15 | const CircularProgressIndicator(), 16 | const SizedBox(height: PADDING), 17 | TText(L10n.of(context).c_search_loading, TextStyles.titleLarge), 18 | ], 19 | ), 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/components/search/nothing_found.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_text.dart'; 2 | import 'package:flavormate/l10n/generated/l10n.dart'; 3 | import 'package:flavormate/utils/constants.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; 6 | 7 | class NothingFound extends StatelessWidget { 8 | const NothingFound({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Center( 13 | child: Column( 14 | mainAxisAlignment: MainAxisAlignment.center, 15 | children: [ 16 | const Icon(MdiIcons.mushroomOffOutline, size: 64), 17 | const SizedBox(height: PADDING), 18 | TText(L10n.of(context).c_search_nothing_found, TextStyles.titleLarge), 19 | ], 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/components/search/search_term_too_short.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_text.dart'; 2 | import 'package:flavormate/l10n/generated/l10n.dart'; 3 | import 'package:flavormate/utils/constants.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; 6 | 7 | class SearchTermTooShort extends StatelessWidget { 8 | const SearchTermTooShort({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Center( 13 | child: Column( 14 | mainAxisAlignment: MainAxisAlignment.center, 15 | children: [ 16 | const Icon(MdiIcons.magnifyMinusOutline, size: 64), 17 | const SizedBox(height: PADDING), 18 | TText( 19 | L10n.of(context).c_search_search_term_too_short, 20 | TextStyles.titleLarge, 21 | ), 22 | ], 23 | ), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/components/t_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; 3 | import 'package:go_router/go_router.dart'; 4 | 5 | class TAppBar extends StatelessWidget implements PreferredSizeWidget { 6 | final String title; 7 | final List? actions; 8 | final bool showHome; 9 | 10 | const TAppBar({ 11 | super.key, 12 | this.title = '', 13 | this.actions, 14 | this.showHome = true, 15 | }); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return AppBar( 20 | leading: showHome ? getBackButton(context) : null, 21 | centerTitle: true, 22 | actions: actions, 23 | title: Text(title), 24 | backgroundColor: Theme.of(context).colorScheme.inversePrimary, 25 | ); 26 | } 27 | 28 | @override 29 | Size get preferredSize => const Size.fromHeight(kToolbarHeight); 30 | 31 | Widget? getBackButton(BuildContext context) { 32 | if (!context.canPop()) { 33 | return IconButton( 34 | onPressed: () => context.replaceNamed('home'), 35 | icon: const Icon(MdiIcons.home), 36 | ); 37 | } 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/components/t_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/utils/constants.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class TCard extends StatelessWidget { 5 | final Widget child; 6 | final Color? color; 7 | final VoidCallback? onTap; 8 | final double padding; 9 | 10 | const TCard({ 11 | super.key, 12 | required this.child, 13 | this.color, 14 | this.onTap, 15 | this.padding = PADDING, 16 | }); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Card( 21 | color: color ?? Theme.of(context).colorScheme.primaryContainer, 22 | margin: EdgeInsets.zero, 23 | child: InkWell( 24 | borderRadius: BorderRadius.circular(BORDER_RADIUS), 25 | onTap: onTap, 26 | child: Container( 27 | padding: EdgeInsets.all(padding), 28 | width: double.infinity, 29 | child: child, 30 | ), 31 | ), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/components/t_empty_message.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_column.dart'; 2 | import 'package:flavormate/components/t_text.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; 5 | 6 | class TEmptyMessage extends StatelessWidget { 7 | final IconData icon; 8 | final String title; 9 | final String? subtitle; 10 | 11 | const TEmptyMessage({ 12 | super.key, 13 | required this.title, 14 | this.icon = MdiIcons.folderQuestionOutline, 15 | this.subtitle, 16 | }); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return TColumn( 21 | mainAxisAlignment: MainAxisAlignment.center, 22 | mainAxisSize: MainAxisSize.min, 23 | children: [ 24 | Icon(icon, size: 96), 25 | TText(title, TextStyles.titleLarge, textAlign: TextAlign.center), 26 | if (subtitle != null) 27 | TText(subtitle!, TextStyles.titleSmall, textAlign: TextAlign.center), 28 | ], 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/components/t_gradient.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/utils/constants.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class TGradient extends StatelessWidget { 5 | final int alpha; 6 | final Color color; 7 | final List steps; 8 | final bool showHeader; 9 | 10 | const TGradient({ 11 | required this.showHeader, 12 | this.alpha = 175, 13 | this.color = Colors.black, 14 | this.steps = const [0, 0.2, 0.25, 0.75, 0.8, 1], 15 | super.key, 16 | }); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | assert(alpha >= 100); 21 | return Container( 22 | decoration: BoxDecoration( 23 | borderRadius: BorderRadius.circular(BORDER_RADIUS), 24 | gradient: LinearGradient( 25 | begin: Alignment.topCenter, 26 | end: Alignment.bottomCenter, 27 | stops: steps, 28 | colors: [ 29 | showHeader ? color.withAlpha(alpha) : Colors.transparent, 30 | showHeader ? color.withAlpha(alpha - 100) : Colors.transparent, 31 | Colors.transparent, 32 | Colors.transparent, 33 | color.withAlpha(alpha - 100), 34 | color.withAlpha(alpha), 35 | ], 36 | ), 37 | ), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/components/t_icon_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_text.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class TIconButton extends StatelessWidget { 5 | final VoidCallback? onPressed; 6 | final IconData icon; 7 | 8 | final String? label; 9 | final double? width, height; 10 | 11 | const TIconButton({ 12 | required this.onPressed, 13 | required this.icon, 14 | this.label, 15 | this.height = 48, 16 | this.width, 17 | super.key, 18 | }); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | if (label == null) { 23 | return CircleAvatar( 24 | backgroundColor: Theme.of(context).colorScheme.primary, 25 | child: IconButton( 26 | color: TText.getColor(context, TextColor.filledButton), 27 | icon: Icon(icon), 28 | onPressed: onPressed, 29 | ), 30 | ); 31 | } else { 32 | return SizedBox( 33 | height: height, 34 | width: width, 35 | child: FilledButton( 36 | onPressed: onPressed, 37 | child: Row( 38 | children: [ 39 | Icon(icon), 40 | Expanded(child: Text(label!, textAlign: TextAlign.center)), 41 | ], 42 | ), 43 | ), 44 | ); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/components/t_pageable_content.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/utils/constants.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class TPageableContent extends StatelessWidget { 5 | final Widget child; 6 | 7 | const TPageableContent({super.key, required this.child}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return SingleChildScrollView( 12 | child: SizedBox( 13 | width: double.infinity, 14 | child: Padding(padding: const EdgeInsets.all(PADDING), child: child), 15 | ), 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/components/t_responsive.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/utils/constants.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class TResponsive extends StatelessWidget { 5 | final double maxWidth; 6 | final Widget child; 7 | 8 | const TResponsive({ 9 | required this.child, 10 | this.maxWidth = Breakpoints.sm, 11 | super.key, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return SingleChildScrollView( 17 | child: Center( 18 | child: Container( 19 | padding: const EdgeInsets.all(PADDING), 20 | constraints: BoxConstraints(maxWidth: maxWidth), 21 | child: child, 22 | ), 23 | ), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/components/t_rounded_list_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/utils/constants.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class TRoundedListTile extends StatelessWidget { 5 | final Widget? leading; 6 | final Widget title; 7 | final Function()? onTap; 8 | 9 | const TRoundedListTile({ 10 | super.key, 11 | this.leading, 12 | required this.title, 13 | this.onTap, 14 | }); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return ListTile( 19 | leading: leading, 20 | title: title, 21 | onTap: onTap, 22 | shape: RoundedRectangleBorder( 23 | borderRadius: BorderRadius.circular(BORDER_RADIUS), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/components/t_scrollable_h.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/utils/u_os.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class TScrollableH extends StatefulWidget { 5 | final Widget child; 6 | 7 | const TScrollableH({super.key, required this.child}); 8 | 9 | @override 10 | State createState() => _TScrollableHState(); 11 | } 12 | 13 | class _TScrollableHState extends State { 14 | final _controller = ScrollController(); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Scrollbar( 19 | controller: _controller, 20 | trackVisibility: UOS.isDesktop, 21 | thumbVisibility: UOS.isDesktop, 22 | thickness: 8, 23 | child: Padding( 24 | padding: const EdgeInsets.only(bottom: 16), 25 | child: SingleChildScrollView( 26 | controller: _controller, 27 | scrollDirection: Axis.horizontal, 28 | child: widget.child, 29 | ), 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/components/t_slide.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/t_image.dart'; 2 | 3 | class TSlide { 4 | final String? imageSrc; 5 | final TImageType type; 6 | final String? title; 7 | final String? description; 8 | final DateTime? date; 9 | final Function()? onTap; 10 | 11 | TSlide({ 12 | required this.imageSrc, 13 | required this.type, 14 | this.title, 15 | this.onTap, 16 | this.description, 17 | this.date, 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /lib/components/t_wrap.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/utils/constants.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class TWrap extends StatelessWidget { 5 | final List children; 6 | 7 | const TWrap({super.key, required this.children}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Wrap( 12 | alignment: WrapAlignment.center, 13 | crossAxisAlignment: WrapCrossAlignment.center, 14 | spacing: PADDING, 15 | runSpacing: PADDING, 16 | children: children, 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/drift/tables/story_draft_table.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:flavormate/models/recipe/recipe.dart'; 3 | 4 | class StoryDraftTable extends Table { 5 | IntColumn get id => integer().autoIncrement()(); 6 | 7 | IntColumn get originId => integer().nullable()(); 8 | 9 | TextColumn get label => text().nullable()(); 10 | 11 | TextColumn get content => text().nullable()(); 12 | 13 | TextColumn get recipe => text().map(const RecipeConverter()).nullable()(); 14 | 15 | IntColumn get version => integer().withDefault(const Constant(-1))(); 16 | } 17 | 18 | // stores recipe as string 19 | class RecipeConverter extends TypeConverter { 20 | const RecipeConverter(); 21 | 22 | @override 23 | Recipe fromSql(String fromDb) { 24 | return RecipeMapper.fromJson(fromDb); 25 | } 26 | 27 | @override 28 | String toSql(Recipe value) { 29 | return value.toJson(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/extensions/e_date_time.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:intl/intl.dart'; 3 | 4 | extension EDateTime on DateTime { 5 | String toLocalDateString(BuildContext context) { 6 | return DateFormat.yMMMMd( 7 | Localizations.localeOf(context).languageCode, 8 | ).format(this); 9 | } 10 | 11 | String toLocalTimeString(BuildContext context) { 12 | return DateFormat.Hm( 13 | Localizations.localeOf(context).languageCode, 14 | ).format(this); 15 | } 16 | 17 | String toLocalDateTimeString(BuildContext context) { 18 | return '${toLocalDateString(context)} - ${toLocalTimeString(context)}'; 19 | } 20 | 21 | String toLocalDateTimeString2(BuildContext context) { 22 | return '${toLocalDateString(context)}\n${toLocalTimeString(context)}'; 23 | } 24 | 25 | bool isSameDate(DateTime other) { 26 | return year == other.year && month == other.month && day == other.day; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/extensions/e_drift.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | 3 | extension EDrift on Migrator { 4 | Future addColumnIfNotExists( 5 | TableInfo table, 6 | GeneratedColumn column, 7 | ) async { 8 | final List queryRows = 9 | await database 10 | .customSelect( 11 | 'SELECT name FROM pragma_table_info(?);', 12 | variables: >[ 13 | Variable(table.actualTableName), 14 | ], 15 | ) 16 | .get(); 17 | 18 | final List columnNameList = 19 | queryRows 20 | .map((QueryRow e) => e.readNullable('name')) 21 | .whereType() 22 | .toList(); 23 | 24 | if (columnNameList.contains(column.name)) { 25 | return; 26 | } 27 | 28 | await addColumn(table, column); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/extensions/e_list.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | extension EList on List { 4 | T? random() { 5 | return elementAtOrNull(Random().nextInt(length)); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lib/extensions/e_number.dart: -------------------------------------------------------------------------------- 1 | extension EDouble on double { 2 | bool get isInt => this % 1 == 0; 3 | 4 | double add(double? val) { 5 | return this + (val ?? 0); 6 | } 7 | 8 | String get beautify { 9 | return this % 1 == 0 ? '${toInt()}' : toStringAsFixed(2); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/extensions/e_uri.dart: -------------------------------------------------------------------------------- 1 | extension EUri on Uri { 2 | String get socket { 3 | var response = host; 4 | 5 | if (port != 0) { 6 | response += ':$port'; 7 | } 8 | 9 | if (path.isNotEmpty) { 10 | response += path; 11 | } 12 | 13 | return response; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/interfaces/a_base_page_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | abstract class BasePageProvider extends AutoDisposeNotifier { 4 | void setState(int value) { 5 | state = value; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lib/interfaces/a_filter_search_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_pageable_client.dart'; 2 | import 'package:flavormate/models/pageable/pageable.dart'; 3 | import 'package:flavormate/models/recipe/diet.dart'; 4 | 5 | abstract class AFilterSearchClient extends APageableClient { 6 | AFilterSearchClient({ 7 | required super.httpClient, 8 | required super.baseURL, 9 | required super.parser, 10 | }); 11 | 12 | Future> findBySearch({ 13 | required String searchTerm, 14 | int? page, 15 | int? size, 16 | String? sortBy, 17 | String? sortDirection, 18 | Diet? filter, 19 | }) async { 20 | final params = getParams({ 21 | 'searchTerm': searchTerm, 22 | 'size': size, 23 | 'sortBy': sortBy, 24 | 'sortDirection': sortDirection, 25 | 'page': page, 26 | 'filter': filter?.name, 27 | }); 28 | 29 | final response = await httpClient.get('$baseURL/search?$params'); 30 | 31 | return Pageable.fromMap(response.data!, parser); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/interfaces/a_pageable_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_client.dart'; 2 | import 'package:flavormate/models/pageable/pageable.dart'; 3 | 4 | abstract class APageableClient extends ABaseClient { 5 | APageableClient({ 6 | required super.httpClient, 7 | required super.baseURL, 8 | required super.parser, 9 | }); 10 | 11 | Future> findByPage({ 12 | int? page, 13 | int? size, 14 | String? sortBy, 15 | String? sortDirection, 16 | }) async { 17 | final params = getParams({ 18 | 'size': size, 19 | 'sortBy': sortBy, 20 | 'sortDirection': sortDirection, 21 | 'page': page, 22 | }); 23 | 24 | final response = await httpClient.get('$baseURL/list?$params'); 25 | 26 | return Pageable.fromMap(response.data, parser); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/interfaces/a_pageable_l10n_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_client.dart'; 2 | import 'package:flavormate/models/pageable/pageable.dart'; 3 | 4 | abstract class APageableL10nClient extends ABaseClient { 5 | APageableL10nClient({ 6 | required super.httpClient, 7 | required super.baseURL, 8 | required super.parser, 9 | }); 10 | 11 | Future> findByPage({ 12 | required String language, 13 | int? page, 14 | int? size, 15 | String? sortBy, 16 | String? sortDirection, 17 | }) async { 18 | final params = getParams({ 19 | 'language': language, 20 | 'size': size, 21 | 'sortBy': sortBy, 22 | 'sortDirection': sortDirection, 23 | 'page': page, 24 | }); 25 | 26 | final response = await httpClient.get('$baseURL/list?$params'); 27 | 28 | return Pageable.fromMap(response.data, parser); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/interfaces/a_search_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_pageable_client.dart'; 2 | import 'package:flavormate/models/pageable/pageable.dart'; 3 | 4 | abstract class ASearchClient extends APageableClient { 5 | ASearchClient({ 6 | required super.httpClient, 7 | required super.baseURL, 8 | required super.parser, 9 | }); 10 | 11 | Future> findBySearch({ 12 | required String searchTerm, 13 | int? page, 14 | int? size, 15 | String? sortBy, 16 | String? sortDirection, 17 | }) async { 18 | final params = getParams({ 19 | 'size': size, 20 | 'sortBy': sortBy, 21 | 'sortDirection': sortDirection, 22 | 'page': page, 23 | 'searchTerm': searchTerm, 24 | }); 25 | 26 | final response = await httpClient.get('$baseURL/search?$params'); 27 | 28 | return Pageable.fromMap(response.data!, parser); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/interfaces/a_search_l10n_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_pageable_l10n_client.dart'; 2 | import 'package:flavormate/models/pageable/pageable.dart'; 3 | 4 | abstract class ASearchL10nClient extends APageableL10nClient { 5 | ASearchL10nClient({ 6 | required super.httpClient, 7 | required super.baseURL, 8 | required super.parser, 9 | }); 10 | 11 | Future> findBySearch({ 12 | required String language, 13 | required String searchTerm, 14 | int? page, 15 | int? size, 16 | String? sortBy, 17 | String? sortDirection, 18 | }) async { 19 | final params = getParams({ 20 | 'language': language, 21 | 'size': size, 22 | 'sortBy': sortBy, 23 | 'sortDirection': sortDirection, 24 | 'page': page, 25 | 'searchTerm': searchTerm, 26 | }); 27 | 28 | final response = await httpClient.get('$baseURL/search?$params'); 29 | 30 | return Pageable.fromMap(response.data!, parser); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/models/api/login.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | 3 | part 'login.mapper.dart'; 4 | 5 | @MappableClass() 6 | class Login with LoginMappable { 7 | final String username; 8 | final String password; 9 | 10 | Login({required this.username, required this.password}); 11 | } 12 | -------------------------------------------------------------------------------- /lib/models/appLink/app_link.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class AppLink { 4 | final String server; 5 | final String token; 6 | final String page; 7 | final int id; 8 | 9 | AppLink({ 10 | required this.server, 11 | required this.token, 12 | required this.page, 13 | required this.id, 14 | }); 15 | 16 | factory AppLink.decode(Map input) { 17 | final s = utf8.decode(base64Decode(input['server']!)); 18 | 19 | return AppLink( 20 | server: s, 21 | token: input['token']!, 22 | page: input['page']!, 23 | id: int.parse(input['id']!), 24 | ); 25 | } 26 | 27 | String encode() { 28 | final s = base64Encode(utf8.encode(server)); 29 | 30 | return 'server=$s&page=${Uri.encodeComponent(page)}&id=${Uri.encodeComponent(id.toString())}&token=${Uri.encodeComponent(token)}'; 31 | } 32 | } 33 | 34 | enum AppLinkMode { open } 35 | -------------------------------------------------------------------------------- /lib/models/author/author.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/entity.dart'; 2 | import 'package:flavormate/models/user/user.dart'; 3 | import 'package:dart_mappable/dart_mappable.dart'; 4 | 5 | part 'author.mapper.dart'; 6 | 7 | @MappableClass() 8 | class Author extends Entity with AuthorMappable { 9 | final User account; 10 | 11 | final List>? books; 12 | 13 | final List>? recipes; 14 | 15 | Author({ 16 | required this.account, 17 | required this.books, 18 | required this.recipes, 19 | required super.id, 20 | required super.version, 21 | required super.createdOn, 22 | required super.lastModifiedOn, 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /lib/models/categories/category.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/extensions/e_string.dart'; 3 | import 'package:flavormate/models/categories/category_group.dart'; 4 | import 'package:flavormate/models/entity.dart'; 5 | import 'package:flavormate/models/recipe/recipe.dart'; 6 | 7 | part 'category.mapper.dart'; 8 | 9 | @MappableClass() 10 | class Category extends Entity with CategoryMappable { 11 | String label; 12 | CategoryGroup group; 13 | List? recipes; 14 | 15 | Category({ 16 | required this.label, 17 | required this.group, 18 | required this.recipes, 19 | required super.id, 20 | required super.version, 21 | required super.createdOn, 22 | required super.lastModifiedOn, 23 | }); 24 | 25 | String? get coverUrl => 26 | recipes 27 | ?.where((recipe) => EString.isNotEmpty(recipe.coverUrl)) 28 | .firstOrNull 29 | ?.coverUrl; 30 | } 31 | -------------------------------------------------------------------------------- /lib/models/categories/category_group.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/entity.dart'; 2 | import 'package:dart_mappable/dart_mappable.dart'; 3 | 4 | part 'category_group.mapper.dart'; 5 | 6 | @MappableClass() 7 | class CategoryGroup extends Entity with CategoryGroupMappable { 8 | final String label; 9 | 10 | CategoryGroup({ 11 | required this.label, 12 | required super.id, 13 | required super.version, 14 | required super.createdOn, 15 | required super.lastModifiedOn, 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /lib/models/changelog/changelog.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/models/version/version.dart'; 3 | import 'package:flavormate/utils/custom_mappers/version_converter.dart'; 4 | 5 | part 'changelog.mapper.dart'; 6 | 7 | class Changelog { 8 | final Map entries; 9 | 10 | Changelog({required this.entries}); 11 | 12 | List get sorted => entries.values.toList(); 13 | } 14 | 15 | @MappableClass() 16 | class ChangelogVersion with ChangelogVersionMappable { 17 | @MappableField(hook: VersionConverter()) 18 | final Version version; 19 | 20 | final List details; 21 | 22 | ChangelogVersion({required this.version, required this.details}); 23 | } 24 | 25 | @MappableClass() 26 | class ChangelogDetail with ChangelogDetailMappable { 27 | final String icon; 28 | final String change; 29 | 30 | ChangelogDetail({required this.icon, required this.change}); 31 | } 32 | -------------------------------------------------------------------------------- /lib/models/destination.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Destination { 4 | IconData icon; 5 | String label; 6 | 7 | Destination({required this.icon, required this.label}); 8 | } 9 | -------------------------------------------------------------------------------- /lib/models/entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | 3 | part 'entity.mapper.dart'; 4 | 5 | @MappableClass() 6 | class Entity with EntityMappable { 7 | int? id; 8 | int? version; 9 | DateTime? createdOn; 10 | DateTime? lastModifiedOn; 11 | 12 | Entity({ 13 | required this.id, 14 | required this.version, 15 | required this.createdOn, 16 | required this.lastModifiedOn, 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /lib/models/features/features_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | 3 | part 'features_response.mapper.dart'; 4 | 5 | @MappableClass() 6 | class FeaturesResponse with FeaturesResponseMappable { 7 | final String version; 8 | final List features; 9 | 10 | FeaturesResponse({required this.version, required this.features}); 11 | } 12 | -------------------------------------------------------------------------------- /lib/models/file/file_draft.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | 3 | part 'file_draft.mapper.dart'; 4 | 5 | @MappableClass() 6 | class FileDraft with FileDraftMappable { 7 | final int id; 8 | final String type; 9 | final String category; 10 | final int owner; 11 | final String name; 12 | final String? content; 13 | 14 | FileDraft({ 15 | required this.id, 16 | required this.type, 17 | required this.category, 18 | required this.owner, 19 | required this.name, 20 | required this.content, 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /lib/models/highlight.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/entity.dart'; 2 | import 'package:flavormate/models/recipe/diet.dart'; 3 | import 'package:flavormate/models/recipe/recipe.dart'; 4 | import 'package:dart_mappable/dart_mappable.dart'; 5 | 6 | part 'highlight.mapper.dart'; 7 | 8 | @MappableClass() 9 | class Highlight extends Entity with HighlightMappable { 10 | final Recipe recipe; 11 | 12 | final DateTime date; 13 | 14 | final Diet diet; 15 | 16 | Highlight({ 17 | required this.recipe, 18 | required this.date, 19 | required this.diet, 20 | required super.id, 21 | required super.version, 22 | required super.createdOn, 23 | required super.lastModifiedOn, 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /lib/models/library/book.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/author/author.dart'; 2 | import 'package:flavormate/models/entity.dart'; 3 | import 'package:flavormate/models/recipe/recipe.dart'; 4 | import 'package:dart_mappable/dart_mappable.dart'; 5 | 6 | part 'book.mapper.dart'; 7 | 8 | @MappableClass() 9 | class Book extends Entity with BookMappable { 10 | final String label; 11 | final bool visible; 12 | final List? recipes; 13 | final Author owner; 14 | 15 | Book({ 16 | required this.owner, 17 | required this.label, 18 | required this.visible, 19 | required this.recipes, 20 | required super.id, 21 | required super.version, 22 | required super.createdOn, 23 | required super.lastModifiedOn, 24 | }); 25 | 26 | has(Recipe recipe) { 27 | return recipes!.indexWhere((r) => r.id == recipe.id) >= 0; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/models/pageable/pageable.dart: -------------------------------------------------------------------------------- 1 | class Pageable { 2 | final List content; 3 | final Page page; 4 | 5 | Pageable({required this.content, required this.page}); 6 | 7 | factory Pageable.fromMap( 8 | Map data, 9 | T Function(Map) parser, 10 | ) { 11 | List> content = List.from(data['content']); 12 | return Pageable( 13 | content: content.map(parser).toList(), 14 | page: Page.fromMap(data['page']), 15 | ); 16 | } 17 | } 18 | 19 | class Page { 20 | /// current page 21 | final int number; 22 | 23 | /// current page size 24 | final int size; 25 | final int totalElements; 26 | final int totalPages; 27 | 28 | Page({ 29 | required this.number, 30 | required this.size, 31 | required this.totalElements, 32 | required this.totalPages, 33 | }); 34 | 35 | bool get empty => totalElements == 0; 36 | 37 | bool get first => number == 0; 38 | 39 | bool get last => totalPages == number; 40 | 41 | factory Page.fromMap(Map data) { 42 | return Page( 43 | number: data['number'] as int, 44 | size: data['size'] as int, 45 | totalElements: data['totalElements'] as int, 46 | totalPages: data['totalPages'] as int, 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/models/recipe/course.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/l10n/generated/l10n.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; 5 | 6 | part 'course.mapper.dart'; 7 | 8 | @MappableEnum() 9 | enum Course { 10 | Appetizer(MdiIcons.breadSlice), 11 | MainDish(MdiIcons.noodles), 12 | Dessert(MdiIcons.cookie), 13 | SideDish(MdiIcons.bowlMix), 14 | Bakery(MdiIcons.baguette), 15 | Drink(MdiIcons.beer); 16 | 17 | final IconData icon; 18 | 19 | const Course(this.icon); 20 | 21 | String getName(BuildContext context) { 22 | return switch (this) { 23 | Course.Appetizer => L10n.of(context).en_course_appetizer, 24 | Course.MainDish => L10n.of(context).en_course_main_dish, 25 | Course.Dessert => L10n.of(context).en_course_dessert, 26 | Course.SideDish => L10n.of(context).en_course_side_dish, 27 | Course.Bakery => L10n.of(context).en_course_bakery, 28 | Course.Drink => L10n.of(context).en_course_drink, 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/models/recipe/diet.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/l10n/generated/l10n.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; 5 | 6 | part 'diet.mapper.dart'; 7 | 8 | @MappableEnum() 9 | enum Diet { 10 | Meat(MdiIcons.foodDrumstick), 11 | Fish(MdiIcons.fish), 12 | Vegetarian(MdiIcons.eggFried), 13 | Vegan(MdiIcons.foodApple); 14 | 15 | final IconData icon; 16 | 17 | const Diet(this.icon); 18 | 19 | String getName(BuildContext context) { 20 | return switch (this) { 21 | Diet.Meat => L10n.of(context).en_diet_meat, 22 | Diet.Fish => L10n.of(context).en_diet_fish, 23 | Diet.Vegetarian => L10n.of(context).en_diet_vegetarian, 24 | Diet.Vegan => L10n.of(context).en_diet_vegan, 25 | }; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/models/recipe/ingredients/ingredient_group.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/models/recipe/ingredients/ingredient.dart'; 3 | import 'package:flavormate/models/recipe_draft/ingredients/ingredient_group_draft.dart'; 4 | 5 | part 'ingredient_group.mapper.dart'; 6 | 7 | @MappableClass() 8 | class IngredientGroup with IngredientGroupMappable { 9 | String? label; 10 | List ingredients; 11 | 12 | IngredientGroup({this.label, required this.ingredients}); 13 | 14 | IngredientGroupDraft toDraft() { 15 | return IngredientGroupDraft( 16 | label: label, 17 | ingredients: ingredients.map((i) => i.toDraft()).toList(), 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/models/recipe/instructions/instruction.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/models/entity.dart'; 3 | import 'package:flavormate/models/recipe_draft/instructions/instruction_draft.dart'; 4 | 5 | part 'instruction.mapper.dart'; 6 | 7 | @MappableClass() 8 | class Instruction extends Entity with InstructionMappable { 9 | String label; 10 | 11 | Instruction({ 12 | required super.id, 13 | required super.version, 14 | required super.createdOn, 15 | required super.lastModifiedOn, 16 | required this.label, 17 | }); 18 | 19 | InstructionDraft toDraft() { 20 | return InstructionDraft(label: label); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/models/recipe/instructions/instruction_group.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/models/entity.dart'; 3 | import 'package:flavormate/models/recipe/instructions/instruction.dart'; 4 | import 'package:flavormate/models/recipe_draft/instructions/instruction_group_draft.dart'; 5 | 6 | part 'instruction_group.mapper.dart'; 7 | 8 | @MappableClass() 9 | class InstructionGroup extends Entity with InstructionGroupMappable { 10 | String? label; 11 | List instructions; 12 | 13 | InstructionGroup({ 14 | required super.id, 15 | required super.version, 16 | required super.createdOn, 17 | required super.lastModifiedOn, 18 | required this.instructions, 19 | this.label, 20 | }); 21 | 22 | InstructionGroupDraft toDraft() { 23 | return InstructionGroupDraft( 24 | label: label, 25 | instructions: instructions.map((i) => i.toDraft()).toList(), 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/models/recipe/serving/serving.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/models/entity.dart'; 3 | import 'package:flavormate/models/recipe_draft/serving_draft/serving_draft.dart'; 4 | 5 | part 'serving.mapper.dart'; 6 | 7 | @MappableClass() 8 | class Serving extends Entity with ServingMappable { 9 | String label; 10 | double amount; 11 | 12 | Serving({ 13 | required super.id, 14 | required super.version, 15 | required super.createdOn, 16 | required super.lastModifiedOn, 17 | required this.label, 18 | required this.amount, 19 | }); 20 | 21 | ServingDraft toDraft() { 22 | return ServingDraft(amount, label); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/models/recipe/unit_ref/unit_conversion.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/models/recipe/unit_ref/unit_ref.dart'; 3 | 4 | part 'unit_conversion.mapper.dart'; 5 | 6 | @MappableClass() 7 | class UnitConversion with UnitConversionMappable { 8 | final UnitConversionId id; 9 | final double factor; 10 | 11 | UnitConversion({required this.id, required this.factor}); 12 | } 13 | 14 | @MappableClass() 15 | class UnitConversionId with UnitConversionIdMappable { 16 | final UnitRef from; 17 | final UnitRef to; 18 | 19 | UnitConversionId({required this.from, required this.to}); 20 | } 21 | -------------------------------------------------------------------------------- /lib/models/recipe/unit_ref/unit_localized.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/extensions/e_string.dart'; 3 | import 'package:flavormate/models/entity.dart'; 4 | import 'package:flavormate/models/recipe/unit_ref/unit_ref.dart'; 5 | 6 | part 'unit_localized.mapper.dart'; 7 | 8 | @MappableClass() 9 | class UnitLocalized extends Entity with UnitLocalizedMappable { 10 | final UnitRef unitRef; 11 | final String language; 12 | final String labelSg; 13 | final String? labelSgAbrv; 14 | final String? labelPl; 15 | final String? labelPlAbrv; 16 | 17 | UnitLocalized({ 18 | required super.id, 19 | required super.version, 20 | required super.createdOn, 21 | required super.lastModifiedOn, 22 | required this.unitRef, 23 | required this.language, 24 | required this.labelSg, 25 | required this.labelSgAbrv, 26 | required this.labelPl, 27 | required this.labelPlAbrv, 28 | }); 29 | 30 | String getLabel(double? amount) { 31 | if (amount != 1.0) { 32 | if (!EString.isEmpty(labelPlAbrv)) { 33 | return labelPlAbrv!; 34 | } else if (!EString.isEmpty(labelPl)) { 35 | return labelPl!; 36 | } 37 | } 38 | if (!EString.isEmpty(labelSgAbrv)) { 39 | return labelSgAbrv!; 40 | } else { 41 | return labelSg; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/models/recipe/unit_ref/unit_ref.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/models/entity.dart'; 3 | 4 | part 'unit_ref.mapper.dart'; 5 | 6 | @MappableClass() 7 | class UnitRef extends Entity with UnitRefMappable { 8 | static final _convertable = [ 9 | // fluids 10 | 'teaspoon', 11 | 'tablespoon', 12 | 'smooth tablespoon', 13 | 'heaped tablespoon', 14 | 'level teaspoon', 15 | 'heaped teaspoon', 16 | 'cup', 17 | 'pint', 18 | 'quart', 19 | 'gallon', 20 | 'centiliter', 21 | 'deciliter', 22 | 'milliliter', 23 | 'liter', 24 | // weights 25 | 'ounce', 26 | 'pound', 27 | 'gram', 28 | 'kilogram', 29 | 'milligram', 30 | ]; 31 | 32 | final String description; 33 | 34 | UnitRef({ 35 | required super.id, 36 | required super.version, 37 | required super.createdOn, 38 | required super.lastModifiedOn, 39 | required this.description, 40 | }); 41 | 42 | double? convertTo(UnitRef to) { 43 | return null; 44 | } 45 | 46 | bool get isConvertable { 47 | return _convertable.contains(description); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/models/recipe_draft/ingredients/ingredient_draft.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/extensions/e_number.dart'; 3 | import 'package:flavormate/models/recipe/unit_ref/unit_localized.dart'; 4 | import 'package:flavormate/models/recipe_draft/nutrition/nutrition_draft.dart'; 5 | import 'package:flavormate/models/unit.dart'; 6 | 7 | part 'ingredient_draft.mapper.dart'; 8 | 9 | @MappableClass() 10 | class IngredientDraft with IngredientDraftMappable { 11 | double? amount; 12 | Unit? unit; 13 | String label; 14 | UnitLocalized? unitLocalized; 15 | NutritionDraft? nutrition; 16 | 17 | IngredientDraft({ 18 | required this.label, 19 | this.amount, 20 | this.unit, 21 | this.nutrition, 22 | this.unitLocalized, 23 | }); 24 | 25 | bool get isValid => label.isNotEmpty; 26 | 27 | String get beautify { 28 | final parts = []; 29 | 30 | if (amount != null && amount! > 0) parts.add(amount!.beautify); 31 | if (unit != null) parts.add(unit!.beautify); 32 | if (unitLocalized != null) parts.add(unitLocalized!.getLabel(amount)); 33 | parts.add(label); 34 | 35 | return parts.isEmpty ? '-' : parts.join(' '); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/models/recipe_draft/ingredients/ingredient_group_draft.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/models/recipe_draft/ingredients/ingredient_draft.dart'; 3 | 4 | part 'ingredient_group_draft.mapper.dart'; 5 | 6 | @MappableClass() 7 | class IngredientGroupDraft with IngredientGroupDraftMappable { 8 | String? label; 9 | final List ingredients; 10 | 11 | IngredientGroupDraft({this.label, required this.ingredients}); 12 | 13 | bool get isValid => 14 | ingredients.isNotEmpty && ingredients.every((i) => i.isValid); 15 | } 16 | -------------------------------------------------------------------------------- /lib/models/recipe_draft/instructions/instruction_draft.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | 3 | part 'instruction_draft.mapper.dart'; 4 | 5 | @MappableClass() 6 | class InstructionDraft with InstructionDraftMappable { 7 | String label; 8 | 9 | InstructionDraft({required this.label}); 10 | 11 | bool get isValid => label.isNotEmpty; 12 | } 13 | -------------------------------------------------------------------------------- /lib/models/recipe_draft/instructions/instruction_group_draft.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/models/recipe_draft/instructions/instruction_draft.dart'; 3 | 4 | part 'instruction_group_draft.mapper.dart'; 5 | 6 | @MappableClass() 7 | class InstructionGroupDraft with InstructionGroupDraftMappable { 8 | String? label; 9 | List instructions; 10 | 11 | InstructionGroupDraft({this.label, required this.instructions}); 12 | 13 | bool get isValid => 14 | instructions.isNotEmpty && instructions.every((i) => i.isValid); 15 | } 16 | -------------------------------------------------------------------------------- /lib/models/recipe_draft/serving_draft/serving_draft.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | 3 | part 'serving_draft.mapper.dart'; 4 | 5 | @MappableClass() 6 | class ServingDraft with ServingDraftMappable { 7 | final double amount; 8 | final String label; 9 | 10 | ServingDraft(this.amount, this.label); 11 | } 12 | -------------------------------------------------------------------------------- /lib/models/recipe_draft_wrapper/scrape_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/models/recipe_draft/recipe_draft.dart'; 3 | 4 | part 'scrape_response.mapper.dart'; 5 | 6 | @MappableClass() 7 | class ScrapeResponse with ScrapeResponseMappable { 8 | final RecipeDraft recipe; 9 | final List images; 10 | 11 | ScrapeResponse({required this.recipe, required this.images}); 12 | } 13 | -------------------------------------------------------------------------------- /lib/models/search/search_result.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; 3 | 4 | class SearchResult { 5 | final SearchResultType type; 6 | final String label; 7 | final int id; 8 | late IconData icon; 9 | 10 | SearchResult({required this.type, required this.label, required this.id}) { 11 | icon = switch (type) { 12 | SearchResultType.author => MdiIcons.account, 13 | SearchResultType.book => MdiIcons.book, 14 | SearchResultType.category => MdiIcons.archive, 15 | SearchResultType.recipe => MdiIcons.foodForkDrink, 16 | SearchResultType.story => MdiIcons.star, 17 | SearchResultType.tag => MdiIcons.tag, 18 | }; 19 | } 20 | } 21 | 22 | enum SearchResultType { author, book, category, recipe, story, tag } 23 | -------------------------------------------------------------------------------- /lib/models/story.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/models/author/author.dart'; 3 | import 'package:flavormate/models/entity.dart'; 4 | import 'package:flavormate/models/recipe/recipe.dart'; 5 | 6 | part 'story.mapper.dart'; 7 | 8 | @MappableClass() 9 | class Story extends Entity with StoryMappable { 10 | final Author author; 11 | 12 | final Recipe recipe; 13 | 14 | final String content; 15 | 16 | final String label; 17 | 18 | Story({ 19 | required this.author, 20 | required this.recipe, 21 | required this.content, 22 | required this.label, 23 | required super.id, 24 | required super.version, 25 | required super.createdOn, 26 | required super.lastModifiedOn, 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /lib/models/t_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class TTheme { 4 | final ColorScheme? light; 5 | final ColorScheme? dark; 6 | 7 | TTheme({required this.light, required this.dark}); 8 | 9 | factory TTheme.fromColor(Color color) { 10 | return TTheme( 11 | light: ColorScheme.fromSeed( 12 | seedColor: color, 13 | brightness: Brightness.light, 14 | ), 15 | dark: ColorScheme.fromSeed(seedColor: color, brightness: Brightness.dark), 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/models/tag/tag.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/extensions/e_string.dart'; 3 | import 'package:flavormate/models/entity.dart'; 4 | import 'package:flavormate/models/recipe/recipe.dart'; 5 | import 'package:flavormate/models/tag_draft/tag_draft.dart'; 6 | 7 | part 'tag.mapper.dart'; 8 | 9 | @MappableClass() 10 | class Tag extends Entity with TagMappable { 11 | final String label; 12 | 13 | final List? recipes; 14 | 15 | Tag({ 16 | required super.id, 17 | required super.version, 18 | required super.createdOn, 19 | required super.lastModifiedOn, 20 | required this.label, 21 | required this.recipes, 22 | }); 23 | 24 | String? get coverUrl => 25 | recipes 26 | ?.where((recipe) => EString.isNotEmpty(recipe.coverUrl)) 27 | .firstOrNull 28 | ?.coverUrl; 29 | 30 | TagDraft toDraft() { 31 | return TagDraft(label: label); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/models/tag_draft/tag_draft.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | 3 | part 'tag_draft.mapper.dart'; 4 | 5 | @MappableClass() 6 | class TagDraft with TagDraftMappable { 7 | final String label; 8 | 9 | TagDraft({required this.label}); 10 | } 11 | -------------------------------------------------------------------------------- /lib/models/unit.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/models/entity.dart'; 3 | 4 | part 'unit.mapper.dart'; 5 | 6 | @MappableClass() 7 | class Unit extends Entity with UnitMappable { 8 | final String label; 9 | 10 | Unit({ 11 | super.id, 12 | super.createdOn, 13 | super.lastModifiedOn, 14 | super.version, 15 | required this.label, 16 | }); 17 | 18 | String get beautify { 19 | return label; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/models/user/role.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | 3 | part 'role.mapper.dart'; 4 | 5 | @MappableClass() 6 | class Role with RoleMappable { 7 | final String label; 8 | 9 | const Role({required this.label}); 10 | } 11 | -------------------------------------------------------------------------------- /lib/models/user/token.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/models/entity.dart'; 3 | import 'package:flavormate/models/user/user.dart'; 4 | 5 | part 'token.mapper.dart'; 6 | 7 | @MappableClass() 8 | class Tokens with TokensMappable { 9 | final Token? accessToken; 10 | final Token? refreshToken; 11 | 12 | Tokens({required this.accessToken, required this.refreshToken}); 13 | } 14 | 15 | @MappableClass() 16 | class Token with TokenMappable { 17 | final String token; 18 | final int expiresIn; 19 | 20 | Token({required this.token, required this.expiresIn}); 21 | } 22 | 23 | @MappableClass() 24 | class TToken extends Entity with TTokenMappable { 25 | final String token; 26 | final Duration? validFor; 27 | final String type; 28 | final int? content; 29 | final User owner; 30 | final int uses; 31 | 32 | TToken({ 33 | required super.id, 34 | required super.version, 35 | required super.createdOn, 36 | required super.lastModifiedOn, 37 | required this.token, 38 | required this.validFor, 39 | required this.type, 40 | required this.content, 41 | required this.owner, 42 | required this.uses, 43 | }); 44 | 45 | DateTime? get validUntil => 46 | validFor != null ? createdOn!.add(validFor!) : null; 47 | } 48 | -------------------------------------------------------------------------------- /lib/models/version/version.dart: -------------------------------------------------------------------------------- 1 | class Version { 2 | final int major; 3 | final int minor; 4 | final int patch; 5 | 6 | Version({required this.major, required this.minor, required this.patch}); 7 | 8 | factory Version.fromString(String input) { 9 | /// example: input = 1.0.2+13 10 | 11 | final version = input.split('+')[0]; 12 | final parts = version.split('.'); 13 | 14 | final major = int.parse(parts[0]); 15 | final minor = int.parse(parts[1]); 16 | final patch = int.parse(parts[2]); 17 | 18 | return Version(major: major, minor: minor, patch: patch); 19 | } 20 | 21 | VersionComparison compare(Version serverVersion) { 22 | if (major != serverVersion.major) { 23 | return VersionComparison.majorIncompatible; 24 | } 25 | 26 | if (minor != serverVersion.minor) { 27 | return VersionComparison.minorIncompatible; 28 | } 29 | 30 | return VersionComparison.fullyCompatible; 31 | } 32 | 33 | @override 34 | String toString() { 35 | return '$major.$minor.$patch'; 36 | } 37 | } 38 | 39 | enum VersionComparison { 40 | /// Incompatible because of major version difference 41 | majorIncompatible, 42 | 43 | /// Might be incompatible because of minor version difference 44 | minorIncompatible, 45 | 46 | /// Server and client are 100% compatible 47 | fullyCompatible, 48 | } 49 | -------------------------------------------------------------------------------- /lib/pages/home/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/dashboard/highlights_viewer.dart'; 2 | import 'package:flavormate/components/dashboard/latest_recipe_viewer.dart'; 3 | import 'package:flavormate/components/dashboard/quick_actions.dart'; 4 | import 'package:flavormate/components/dashboard/stories_viewer.dart'; 5 | import 'package:flavormate/components/riverpod/r_feature.dart'; 6 | import 'package:flavormate/components/t_column.dart'; 7 | import 'package:flavormate/components/t_responsive.dart'; 8 | import 'package:flavormate/components/t_search.dart'; 9 | import 'package:flavormate/riverpod/features/p_feature_story.dart'; 10 | import 'package:flutter/material.dart'; 11 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 12 | 13 | class HomePage extends ConsumerWidget { 14 | const HomePage({super.key}); 15 | 16 | @override 17 | Widget build(BuildContext context, WidgetRef ref) { 18 | final storyProvider = ref.watch(pFeatureStoryProvider); 19 | return TResponsive( 20 | child: TColumn( 21 | children: [ 22 | TSearch(), 23 | QuickActions(), 24 | RFeature(storyProvider, (_) => StoriesViewer()), 25 | HighlightsViewer(), 26 | LatestRecipeViewer(), 27 | ], 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/pages/login/login_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/components/login/login_form.dart'; 2 | import 'package:flavormate/components/login/server_form.dart'; 3 | import 'package:flavormate/riverpod/root_bundle/p_backend_url.dart'; 4 | import 'package:flavormate/riverpod/shared_preferences/p_server.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 7 | 8 | class LoginPage extends ConsumerWidget { 9 | const LoginPage({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context, WidgetRef ref) { 13 | final isStatic = ref.watch(pBackendUrlProvider).requireValue; 14 | final serverURL = ref.watch(pServerProvider); 15 | 16 | if (serverURL.isEmpty) { 17 | return ServerForm(readOnly: isStatic != null); 18 | } else { 19 | return LoginForm(server: serverURL, isStatic: isStatic != null); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/riverpod/api/p_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/clients/api_client.dart'; 2 | import 'package:flavormate/clients/interceptor_methods.dart'; 3 | import 'package:flavormate/riverpod/auth_state/p_auth_state.dart'; 4 | import 'package:flavormate/riverpod/go_router/p_go_router.dart'; 5 | import 'package:flavormate/riverpod/shared_preferences/p_server.dart'; 6 | import 'package:flavormate/riverpod/shared_preferences/p_tokens.dart'; 7 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 8 | 9 | part 'p_api.g.dart'; 10 | 11 | @riverpod 12 | class PApi extends _$PApi { 13 | @override 14 | ApiClient build() { 15 | final server = ref.watch(pServerProvider); 16 | final token = ref.watch(pTokensProvider); 17 | final auth = ref.read(pAuthStateProvider.notifier); 18 | 19 | ref.keepAlive(); 20 | 21 | final interceptorMethods = InterceptorMethods( 22 | onUnauthenticated: auth.logout, 23 | onNoConnection: 24 | () => ref.read(pGoRouterProvider).goNamed('no-connection'), 25 | ); 26 | 27 | return token != null 28 | ? ApiClient.withToken( 29 | server, 30 | token.accessToken!.token, 31 | interceptorMethods, 32 | ) 33 | : ApiClient(server, interceptorMethods); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/riverpod/authors/p_author.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/pageable/pageable.dart'; 2 | import 'package:flavormate/models/recipe/recipe.dart'; 3 | import 'package:flavormate/riverpod/api/p_api.dart'; 4 | import 'package:flavormate/riverpod/authors/p_author_page.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | part 'p_author.g.dart'; 8 | 9 | @riverpod 10 | class PAuthor extends _$PAuthor { 11 | @override 12 | Future> build(int id) async { 13 | final page = ref.watch(pAuthorPageProvider); 14 | return await ref 15 | .watch(pApiProvider) 16 | .authorsClient 17 | .findRecipesInAuthor(id, page: page); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/riverpod/authors/p_author_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_page_provider.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_author_page.g.dart'; 5 | 6 | @riverpod 7 | class PAuthorPage extends BasePageProvider { 8 | @override 9 | int build() { 10 | return 0; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/riverpod/authors/p_authors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/author/author.dart'; 2 | import 'package:flavormate/models/pageable/pageable.dart'; 3 | import 'package:flavormate/riverpod/api/p_api.dart'; 4 | import 'package:flavormate/riverpod/authors/p_authors_page.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | part 'p_authors.g.dart'; 8 | 9 | @riverpod 10 | class PAuthors extends _$PAuthors { 11 | @override 12 | Future> build() async { 13 | final page = ref.watch(pAuthorsPageProvider); 14 | return await ref.watch(pApiProvider).authorsClient.findByPage(page: page); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/riverpod/authors/p_authors_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_page_provider.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_authors_page.g.dart'; 5 | 6 | @riverpod 7 | class PAuthorsPage extends BasePageProvider { 8 | @override 9 | int build() { 10 | return 0; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/riverpod/categories/p_categories.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/categories/category.dart'; 2 | import 'package:flavormate/models/pageable/pageable.dart'; 3 | import 'package:flavormate/riverpod/api/p_api.dart'; 4 | import 'package:flavormate/riverpod/categories/p_categories_page.dart'; 5 | import 'package:flavormate/utils/u_localizations.dart'; 6 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 7 | 8 | part 'p_categories.g.dart'; 9 | 10 | @riverpod 11 | class PCategories extends _$PCategories { 12 | @override 13 | Future> build() async { 14 | final language = currentLocalization().languageCode; 15 | final page = ref.watch(pCategoriesPageProvider); 16 | return await ref 17 | .watch(pApiProvider) 18 | .categoriesClient 19 | .findNotEmpty( 20 | language: language, 21 | page: page, 22 | sortBy: 'label', 23 | sortDirection: 'ASC', 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/riverpod/categories/p_categories_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_page_provider.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_categories_page.g.dart'; 5 | 6 | @riverpod 7 | class PCategoriesPage extends BasePageProvider { 8 | @override 9 | int build() { 10 | return 0; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/riverpod/categories/p_category.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/pageable/pageable.dart'; 2 | import 'package:flavormate/models/recipe/recipe.dart'; 3 | import 'package:flavormate/riverpod/api/p_api.dart'; 4 | import 'package:flavormate/riverpod/categories/p_category_page.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | part 'p_category.g.dart'; 8 | 9 | @riverpod 10 | class PCategory extends _$PCategory { 11 | @override 12 | Future> build(int id) async { 13 | final page = ref.watch(pCategoryPageProvider); 14 | final categories = await ref 15 | .watch(pApiProvider) 16 | .categoriesClient 17 | .findRecipesInCategory(id, page); 18 | 19 | return categories; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/riverpod/categories/p_category_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_page_provider.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_category_page.g.dart'; 5 | 6 | @riverpod 7 | class PCategoryPage extends BasePageProvider { 8 | @override 9 | int build() { 10 | return 0; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/riverpod/categories/p_raw_categories.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/categories/category.dart'; 2 | import 'package:flavormate/riverpod/api/p_api.dart'; 3 | import 'package:flavormate/utils/u_localizations.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | part 'p_raw_categories.g.dart'; 7 | 8 | @riverpod 9 | class PRawCategories extends _$PRawCategories { 10 | @override 11 | Future> build() async { 12 | final language = currentLocalization().languageCode; 13 | final categories = await ref 14 | .watch(pApiProvider) 15 | .categoriesClient 16 | .findRaw(language); 17 | 18 | return {for (var v in categories) v.id!: v}; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/riverpod/category_group/p_category_groups.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/categories/category_group.dart'; 2 | import 'package:flavormate/riverpod/api/p_api.dart'; 3 | import 'package:flavormate/utils/u_localizations.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | part 'p_category_groups.g.dart'; 7 | 8 | @riverpod 9 | class PCategoryGroups extends _$PCategoryGroups { 10 | @override 11 | Future> build() async { 12 | final language = currentLocalization().languageCode; 13 | return await ref 14 | .watch(pApiProvider) 15 | .categoryGroupClient 16 | .findAll(language: language); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/riverpod/category_group/p_category_groups_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_page_provider.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_category_groups_page.g.dart'; 5 | 6 | @riverpod 7 | class PCategoryGroupsPage extends BasePageProvider { 8 | @override 9 | int build() { 10 | return 0; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/riverpod/changelog/p_changelog.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flavormate/gen/assets.gen.dart'; 4 | import 'package:flavormate/models/changelog/changelog.dart'; 5 | import 'package:flavormate/utils/u_localizations.dart'; 6 | import 'package:flutter/services.dart'; 7 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 8 | 9 | part 'p_changelog.g.dart'; 10 | 11 | @riverpod 12 | class PChangelog extends _$PChangelog { 13 | @override 14 | Future build() async { 15 | final language = currentLocalization().languageCode; 16 | 17 | final path = switch (language) { 18 | 'de' => Assets.documents.changelog.de, 19 | 'en' => Assets.documents.changelog.en, 20 | _ => '', 21 | }; 22 | 23 | final value = await rootBundle.loadString(path); 24 | 25 | final List parsedJson = jsonDecode(value); 26 | 27 | final entries = 28 | parsedJson.map((map) => ChangelogVersionMapper.fromMap(map)).toList(); 29 | 30 | ref.keepAlive(); 31 | 32 | return Changelog(entries: {for (var v in entries) v.version: v}); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/riverpod/drift/p_drift.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/drift/app_database.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_drift.g.dart'; 5 | 6 | @riverpod 7 | class PDrift extends _$PDrift { 8 | @override 9 | AppDatabase build() { 10 | final db = AppDatabase(); 11 | ref.keepAlive(); 12 | return db; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/riverpod/features/p_compatibility.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/version/version.dart'; 2 | import 'package:flavormate/riverpod/features/p_features.dart'; 3 | import 'package:flavormate/riverpod/package_info/p_package_info.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | part 'p_compatibility.g.dart'; 7 | 8 | @riverpod 9 | class PCompatibility extends _$PCompatibility { 10 | @override 11 | Future build() async { 12 | final serverInfo = await ref.watch( 13 | pFeaturesProvider.selectAsync((data) => data), 14 | ); 15 | 16 | final clientInfo = await ref.watch( 17 | pPackageInfoProvider.selectAsync((data) => data), 18 | ); 19 | 20 | final serverVersion = Version.fromString(serverInfo.version); 21 | final clientVersion = Version.fromString(clientInfo.version); 22 | 23 | return clientVersion.compare(serverVersion); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/riverpod/features/p_feature_bring.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/riverpod/features/p_features.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_feature_bring.g.dart'; 5 | 6 | @riverpod 7 | class PFeatureBring extends _$PFeatureBring { 8 | @override 9 | Future build() async { 10 | final features = await ref.watch( 11 | pFeaturesProvider.selectAsync((data) => data), 12 | ); 13 | 14 | return features.features.contains('bring'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/riverpod/features/p_feature_open_food_facts.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/riverpod/features/p_features.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_feature_open_food_facts.g.dart'; 5 | 6 | @riverpod 7 | class PFeatureOpenFoodFacts extends _$PFeatureOpenFoodFacts { 8 | @override 9 | bool build() { 10 | final features = ref.watch(pFeaturesProvider).requireValue; 11 | 12 | return features.features.contains('open-food-facts'); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/riverpod/features/p_feature_recovery.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/riverpod/features/p_features.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_feature_recovery.g.dart'; 5 | 6 | @riverpod 7 | class PFeatureRecovery extends _$PFeatureRecovery { 8 | @override 9 | Future build() async { 10 | final features = await ref.watch( 11 | pFeaturesProvider.selectAsync((data) => data), 12 | ); 13 | return features.features.contains('recovery'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/riverpod/features/p_feature_registration.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/riverpod/features/p_features.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_feature_registration.g.dart'; 5 | 6 | @riverpod 7 | class PFeatureRegistration extends _$PFeatureRegistration { 8 | @override 9 | Future build() async { 10 | final features = await ref.watch( 11 | pFeaturesProvider.selectAsync((data) => data), 12 | ); 13 | return features.features.contains('registration'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/riverpod/features/p_feature_share.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/riverpod/features/p_features.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_feature_share.g.dart'; 5 | 6 | @riverpod 7 | class PFeatureShare extends _$PFeatureShare { 8 | @override 9 | Future build() async { 10 | final features = await ref.watch( 11 | pFeaturesProvider.selectAsync((data) => data), 12 | ); 13 | return features.features.contains('share'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/riverpod/features/p_feature_story.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/riverpod/features/p_features.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_feature_story.g.dart'; 5 | 6 | @riverpod 7 | class PFeatureStory extends _$PFeatureStory { 8 | @override 9 | Future build() async { 10 | final features = await ref.watch( 11 | pFeaturesProvider.selectAsync((data) => data), 12 | ); 13 | return features.features.contains('story'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/riverpod/features/p_features.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/features/features_response.dart'; 2 | import 'package:flavormate/riverpod/api/p_api.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | part 'p_features.g.dart'; 6 | 7 | @riverpod 8 | class PFeatures extends _$PFeatures { 9 | @override 10 | Future build() async { 11 | final response = await ref.watch(pApiProvider).featuresClient.get(); 12 | 13 | /// if response is successful, cache the response 14 | ref.keepAlive(); 15 | 16 | return response; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/riverpod/highlights/p_highlight.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/highlight.dart'; 2 | import 'package:flavormate/models/pageable/pageable.dart'; 3 | import 'package:flavormate/riverpod/api/p_api.dart'; 4 | import 'package:flavormate/riverpod/user/p_user.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | part 'p_highlight.g.dart'; 8 | 9 | @riverpod 10 | class PHighlight extends _$PHighlight { 11 | @override 12 | Future> build() async { 13 | final user = await ref.watch(pUserProvider.selectAsync((data) => data)); 14 | 15 | return await ref 16 | .watch(pApiProvider) 17 | .highlightsClient 18 | .findAllByDiet(filter: user.diet!, size: 14, sortBy: 'createdOn'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/riverpod/library/p_book_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_page_provider.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_book_page.g.dart'; 5 | 6 | @riverpod 7 | class PBookPage extends BasePageProvider { 8 | @override 9 | int build() { 10 | return 0; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/riverpod/library/p_book_recipes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/pageable/pageable.dart'; 2 | import 'package:flavormate/models/recipe/recipe.dart'; 3 | import 'package:flavormate/riverpod/api/p_api.dart'; 4 | import 'package:flavormate/riverpod/library/p_book_page.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | part 'p_book_recipes.g.dart'; 8 | 9 | @riverpod 10 | class PBookRecipes extends _$PBookRecipes { 11 | @override 12 | Future> build(int id) async { 13 | final page = ref.watch(pBookPageProvider); 14 | return await ref 15 | .watch(pApiProvider) 16 | .libraryClient 17 | .findRecipesInBook(id, page: page); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/riverpod/library/p_library_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_page_provider.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_library_page.g.dart'; 5 | 6 | @riverpod 7 | class PLibraryPage extends BasePageProvider { 8 | @override 9 | int build() { 10 | return 0; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/riverpod/library/p_whole_library.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/library/book.dart'; 2 | import 'package:flavormate/riverpod/api/p_api.dart'; 3 | import 'package:flavormate/riverpod/library/p_library.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | part 'p_whole_library.g.dart'; 7 | 8 | @riverpod 9 | class PWholeLibrary extends _$PWholeLibrary { 10 | @override 11 | Future> build() async { 12 | return await ref.watch(pApiProvider).libraryClient.findOwn(); 13 | } 14 | 15 | Future toggleRecipeInBook(int bookId, int recipeId) async { 16 | await ref 17 | .read(pApiProvider) 18 | .libraryClient 19 | .toggleRecipeInBook(bookId, recipeId); 20 | 21 | ref.invalidate(pLibraryProvider); 22 | ref.invalidateSelf(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/riverpod/package_info/p_package_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:package_info_plus/package_info_plus.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_package_info.g.dart'; 5 | 6 | @riverpod 7 | class PPackageInfo extends _$PPackageInfo { 8 | @override 9 | Future build() async { 10 | return await PackageInfo.fromPlatform(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/riverpod/public_recipes/p_public_recipe.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/clients/api_client.dart'; 2 | import 'package:flavormate/models/appLink/app_link.dart'; 3 | import 'package:flavormate/models/recipe/recipe.dart'; 4 | import 'package:flavormate/utils/u_localizations.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | part 'p_public_recipe.g.dart'; 8 | 9 | @riverpod 10 | class PPublicRecipe extends _$PPublicRecipe { 11 | @override 12 | Future build(AppLink appLink) async { 13 | final language = currentLocalization().languageCode; 14 | 15 | final api = ApiClient.public(appLink.server); 16 | 17 | return await api.publicRecipeClient.findByIdL10n( 18 | appLink.token, 19 | appLink.id, 20 | language, 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/riverpod/recipe_draft/p_recipe_draft_categories.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/categories/category.dart'; 2 | import 'package:flavormate/riverpod/api/p_api.dart'; 3 | import 'package:flavormate/riverpod/category_group/p_category_groups.dart'; 4 | import 'package:flavormate/utils/u_localizations.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | part 'p_recipe_draft_categories.g.dart'; 8 | 9 | @riverpod 10 | class PRecipeDraftCategories extends _$PRecipeDraftCategories { 11 | @override 12 | Future>> build() async { 13 | final language = currentLocalization().languageCode; 14 | final Map> map = {}; 15 | 16 | final categoryGroups = await ref.watch( 17 | pCategoryGroupsProvider.selectAsync((data) => data), 18 | ); 19 | final categories = await ref 20 | .watch(pApiProvider) 21 | .categoriesClient 22 | .findRaw(language); 23 | 24 | for (final categoryGroup in categoryGroups) { 25 | map[categoryGroup.label] = 26 | categories 27 | .where((category) => category.group.id == categoryGroup.id) 28 | .toList(); 29 | } 30 | 31 | return map; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/riverpod/recipes/p_action_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/riverpod/recipes/p_recipe.dart'; 2 | import 'package:flavormate/riverpod/user/p_user.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | part 'p_action_button.g.dart'; 6 | 7 | @riverpod 8 | class PActionButton extends _$PActionButton { 9 | @override 10 | Future build(int recipeId) async { 11 | final recipe = await ref.watch( 12 | pRecipeProvider(recipeId).selectAsync((data) => data), 13 | ); 14 | final user = await ref.watch(pUserProvider.selectAsync((data) => data)); 15 | 16 | return ActionButtonPermissions( 17 | isOwner: recipe.author!.id == user.id!, 18 | isAdmin: user.isAdmin, 19 | ); 20 | } 21 | } 22 | 23 | class ActionButtonPermissions { 24 | final bool isOwner; 25 | final bool isAdmin; 26 | 27 | ActionButtonPermissions({required this.isOwner, required this.isAdmin}); 28 | } 29 | -------------------------------------------------------------------------------- /lib/riverpod/recipes/p_latest_recipes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/pageable/pageable.dart'; 2 | import 'package:flavormate/models/recipe/recipe.dart'; 3 | import 'package:flavormate/riverpod/api/p_api.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | part 'p_latest_recipes.g.dart'; 7 | 8 | @riverpod 9 | class PLatestRecipes extends _$PLatestRecipes { 10 | @override 11 | Future> build() async { 12 | return await ref 13 | .watch(pApiProvider) 14 | .recipesClient 15 | .findByPage(page: 0, size: 10, sortBy: 'createdOn'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/riverpod/recipes/p_recipe.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/recipe/recipe.dart'; 2 | import 'package:flavormate/riverpod/api/p_api.dart'; 3 | import 'package:flavormate/riverpod/units/p_unit_conversions.dart'; 4 | import 'package:flavormate/utils/u_localizations.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | part 'p_recipe.g.dart'; 8 | 9 | @riverpod 10 | class PRecipe extends _$PRecipe { 11 | @override 12 | Future build(int id) async { 13 | await ref.watch(pUnitConversionProvider.selectAsync((data) => data)); 14 | final language = currentLocalization().languageCode; 15 | return await ref 16 | .watch(pApiProvider) 17 | .recipesClient 18 | .findByIdL10n(id, language); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/riverpod/recipes/p_recipes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/pageable/pageable.dart'; 2 | import 'package:flavormate/models/recipe/recipe.dart'; 3 | import 'package:flavormate/riverpod/api/p_api.dart'; 4 | import 'package:flavormate/riverpod/recipes/p_recipes_page.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | part 'p_recipes.g.dart'; 8 | 9 | @riverpod 10 | class PRecipes extends _$PRecipes { 11 | @override 12 | Future> build() async { 13 | final page = ref.watch(pRecipesPageProvider); 14 | return await ref 15 | .watch(pApiProvider) 16 | .recipesClient 17 | .findByPage(page: page, sortBy: 'label', sortDirection: 'ASC'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/riverpod/recipes/p_recipes_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_page_provider.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_recipes_page.g.dart'; 5 | 6 | @riverpod 7 | class PRecipesPage extends BasePageProvider { 8 | @override 9 | int build() { 10 | return 0; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/riverpod/root_bundle/p_backend_url.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/extensions/e_string.dart'; 2 | import 'package:flavormate/gen/assets.gen.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | part 'p_backend_url.g.dart'; 7 | 8 | /// Provides the backend url to use. Once this string is set, the user is unable to change it. 9 | /// On iOS, Android, Linux, macOS and Windows this should always be null. 10 | /// On Web it's recommended to provide a url via the file 11 | @riverpod 12 | class PBackendUrl extends _$PBackendUrl { 13 | @override 14 | Future build() async { 15 | final path = Assets.web.backendUrl; 16 | final value = await rootBundle.loadString(path).catchError((_) => ''); 17 | return EString.trimToNull(value); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/riverpod/search/p_recipe_search_term.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | part 'p_recipe_search_term.g.dart'; 4 | 5 | @Riverpod(keepAlive: true) 6 | class PRecipeSearchTerm extends _$PRecipeSearchTerm { 7 | @override 8 | String build() { 9 | return ''; 10 | } 11 | 12 | void set(String val) { 13 | state = val; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/riverpod/search/p_search_term.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | part 'p_search_term.g.dart'; 4 | 5 | @Riverpod(keepAlive: true) 6 | class PSearchTerm extends _$PSearchTerm { 7 | @override 8 | String build() { 9 | return ''; 10 | } 11 | 12 | void set(String val) { 13 | state = val; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/riverpod/shared_preferences/p_server.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/riverpod/root_bundle/p_backend_url.dart'; 2 | import 'package:flavormate/riverpod/shared_preferences/p_shared_preferences.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | part 'p_server.g.dart'; 6 | 7 | @riverpod 8 | class PServer extends _$PServer { 9 | @override 10 | String build() { 11 | final backendUrl = ref.read(pBackendUrlProvider).requireValue; 12 | if (backendUrl != null) { 13 | return backendUrl; 14 | } 15 | 16 | final provider = ref.watch(pSharedPreferencesProvider).requireValue; 17 | final server = provider.get('server') as String?; 18 | return server ?? ''; 19 | } 20 | 21 | void set(String value) async { 22 | state = value; 23 | await ref 24 | .read(pSharedPreferencesProvider) 25 | .requireValue 26 | .setString('server', value); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/riverpod/shared_preferences/p_shared_preferences.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | 4 | part 'p_shared_preferences.g.dart'; 5 | 6 | @riverpod 7 | class PSharedPreferences extends _$PSharedPreferences { 8 | @override 9 | Future build() { 10 | return SharedPreferences.getInstance(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/riverpod/shared_preferences/p_tokens.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/user/token.dart'; 2 | import 'package:flavormate/riverpod/shared_preferences/p_shared_preferences.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | part 'p_tokens.g.dart'; 6 | 7 | @riverpod 8 | class PTokens extends _$PTokens { 9 | @override 10 | Tokens? build() { 11 | final provider = ref.watch(pSharedPreferencesProvider).requireValue; 12 | final response = provider.get('token'); 13 | if (response == null) return null; 14 | 15 | return TokensMapper.fromJson(response.toString()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/riverpod/stories/p_action_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/riverpod/stories/p_story.dart'; 2 | import 'package:flavormate/riverpod/user/p_user.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | part 'p_action_button.g.dart'; 6 | 7 | @riverpod 8 | class PActionButton extends _$PActionButton { 9 | @override 10 | Future build(int storyId) async { 11 | final story = await ref.watch( 12 | pStoryProvider(storyId).selectAsync((data) => data), 13 | ); 14 | final user = await ref.watch(pUserProvider.selectAsync((data) => data)); 15 | 16 | return ActionButtonPermissions( 17 | isOwner: story.author.id == user.id!, 18 | isAdmin: user.isAdmin, 19 | ); 20 | } 21 | } 22 | 23 | class ActionButtonPermissions { 24 | final bool isOwner; 25 | final bool isAdmin; 26 | 27 | ActionButtonPermissions({required this.isOwner, required this.isAdmin}); 28 | } 29 | -------------------------------------------------------------------------------- /lib/riverpod/stories/p_stories.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/pageable/pageable.dart'; 2 | import 'package:flavormate/models/story.dart'; 3 | import 'package:flavormate/riverpod/api/p_api.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | part 'p_stories.g.dart'; 7 | 8 | @riverpod 9 | class PStories extends _$PStories { 10 | @override 11 | Future> build() async { 12 | return await ref.watch(pApiProvider).storiesClient.findByPage(page: 0); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/riverpod/stories/p_story.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/story.dart'; 2 | import 'package:flavormate/riverpod/api/p_api.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | part 'p_story.g.dart'; 6 | 7 | @riverpod 8 | class PStory extends _$PStory { 9 | @override 10 | Future build(int id) async { 11 | return await ref.watch(pApiProvider).storiesClient.findById(id); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/riverpod/tags/p_tag.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/pageable/pageable.dart'; 2 | import 'package:flavormate/models/recipe/recipe.dart'; 3 | import 'package:flavormate/riverpod/api/p_api.dart'; 4 | import 'package:flavormate/riverpod/tags/p_tag_page.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | part 'p_tag.g.dart'; 8 | 9 | @riverpod 10 | class PTag extends _$PTag { 11 | @override 12 | Future> build(int id) async { 13 | final page = ref.watch(pTagPageProvider); 14 | return await ref 15 | .watch(pApiProvider) 16 | .tagsClient 17 | .findAllRecipesInTag(id, page); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/riverpod/tags/p_tag_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_page_provider.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_tag_page.g.dart'; 5 | 6 | @riverpod 7 | class PTagPage extends BasePageProvider { 8 | @override 9 | int build() { 10 | return 0; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/riverpod/tags/p_tags.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/pageable/pageable.dart'; 2 | import 'package:flavormate/models/tag/tag.dart'; 3 | import 'package:flavormate/riverpod/api/p_api.dart'; 4 | import 'package:flavormate/riverpod/tags/p_tags_page.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | part 'p_tags.g.dart'; 8 | 9 | @riverpod 10 | class PTags extends _$PTags { 11 | @override 12 | Future> build() async { 13 | final page = ref.watch(pTagsPageProvider); 14 | return await ref 15 | .watch(pApiProvider) 16 | .tagsClient 17 | .findByPage(page: page, sortBy: 'label', sortDirection: 'ASC'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/riverpod/tags/p_tags_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/interfaces/a_base_page_provider.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'p_tags_page.g.dart'; 5 | 6 | @riverpod 7 | class PTagsPage extends BasePageProvider { 8 | @override 9 | int build() { 10 | return 0; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/riverpod/theme/p_custom_color.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/t_theme.dart'; 2 | import 'package:flavormate/riverpod/shared_preferences/p_shared_preferences.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | part 'p_custom_color.g.dart'; 7 | 8 | @riverpod 9 | class PCustomColor extends _$PCustomColor { 10 | @override 11 | Future build() async { 12 | final sp = ref.watch(pSharedPreferencesProvider).requireValue; 13 | 14 | final color = sp.getInt('theme_custom_color'); 15 | 16 | if (color == null) return null; 17 | 18 | return TTheme.fromColor(Color(color)); 19 | } 20 | 21 | void setColor(Color? color) { 22 | final sp = ref.read(pSharedPreferencesProvider).requireValue; 23 | 24 | if (color == null) { 25 | sp.remove('theme_custom_color'); 26 | } else { 27 | sp.setInt('theme_custom_color', color.toARGB32()); 28 | } 29 | 30 | ref.invalidateSelf(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/riverpod/theme/p_dynamic_color.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/t_theme.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | import 'package:system_theme/system_theme.dart'; 5 | 6 | part 'p_dynamic_color.g.dart'; 7 | 8 | @riverpod 9 | class PDynamicColor extends _$PDynamicColor { 10 | @override 11 | Future build() async { 12 | if (!_supportAccentColor()) return null; 13 | 14 | return TTheme.fromColor(SystemTheme.accentColor.accent); 15 | } 16 | 17 | bool _supportAccentColor() { 18 | return !kIsWeb && 19 | [ 20 | TargetPlatform.windows, 21 | TargetPlatform.macOS, 22 | TargetPlatform.android, 23 | TargetPlatform.linux, 24 | ].contains(defaultTargetPlatform); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/riverpod/theme/p_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/t_theme.dart'; 2 | import 'package:flavormate/riverpod/theme/p_custom_color.dart'; 3 | import 'package:flavormate/riverpod/theme/p_dynamic_color.dart'; 4 | import 'package:flavormate/riverpod/theme/p_theme_mode.dart'; 5 | import 'package:flavormate/utils/constants.dart'; 6 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 7 | 8 | part 'p_theme.g.dart'; 9 | 10 | @riverpod 11 | class PTheme extends _$PTheme { 12 | @override 13 | Future build() async { 14 | final mode = ref.watch(pThemeModeProvider); 15 | 16 | final dynamic = await ref.watch( 17 | pDynamicColorProvider.selectAsync((data) => data), 18 | ); 19 | 20 | final custom = await ref.watch( 21 | pCustomColorProvider.selectAsync((data) => data), 22 | ); 23 | 24 | if (mode == CustomThemeMode.dynamic && dynamic != null) { 25 | return dynamic; 26 | } else if (mode == CustomThemeMode.custom && custom != null) { 27 | return custom; 28 | } 29 | 30 | return TTheme.fromColor(FLAVORMATE_COLOR); 31 | } 32 | } 33 | 34 | enum CustomThemeMode { flavormate, dynamic, custom } 35 | -------------------------------------------------------------------------------- /lib/riverpod/theme/p_theme_mode.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/riverpod/shared_preferences/p_shared_preferences.dart'; 2 | import 'package:flavormate/riverpod/theme/p_theme.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | part 'p_theme_mode.g.dart'; 6 | 7 | @riverpod 8 | class PThemeMode extends _$PThemeMode { 9 | @override 10 | CustomThemeMode build() { 11 | final sharedPreferences = 12 | ref.watch(pSharedPreferencesProvider).requireValue; 13 | 14 | final modeString = 15 | sharedPreferences.getString('theme_mode') ?? 'flavormate'; 16 | 17 | final mode = CustomThemeMode.values.byName(modeString); 18 | 19 | return mode; 20 | } 21 | 22 | void setMode(CustomThemeMode? single) { 23 | final sharedPreferences = 24 | ref.watch(pSharedPreferencesProvider).requireValue; 25 | 26 | if (single == null) { 27 | sharedPreferences.remove('theme_mode'); 28 | } else { 29 | sharedPreferences.setString('theme_mode', single.name); 30 | } 31 | 32 | ref.invalidateSelf(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/riverpod/units/p_unit_conversions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/recipe/unit_ref/unit_conversion.dart'; 2 | import 'package:flavormate/models/recipe/unit_ref/unit_ref.dart'; 3 | import 'package:flavormate/riverpod/api/p_api.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | part 'p_unit_conversions.g.dart'; 7 | 8 | @riverpod 9 | class PUnitConversion extends _$PUnitConversion { 10 | @override 11 | Future> build() async { 12 | final response = 13 | await ref.watch(pApiProvider).unitsClient.getAllConversions(); 14 | 15 | ref.keepAlive(); 16 | 17 | return response; 18 | } 19 | 20 | double? convert(UnitRef from, UnitRef to) { 21 | final results = state.value!.where( 22 | (v) => v.id.from == from && v.id.to == to, 23 | ); 24 | 25 | return results.firstOrNull?.factor; 26 | } 27 | 28 | double? convertFromGram(UnitRef to) { 29 | final results = 30 | state.value! 31 | .where( 32 | (v) => 33 | (v.id.from.description == 'gram' || 34 | v.id.from.description == 'milliliter') && 35 | v.id.to == to, 36 | ) 37 | .toList(); 38 | 39 | return results.firstOrNull?.factor; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/riverpod/units/p_units.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/recipe/unit_ref/unit_localized.dart'; 2 | import 'package:flavormate/riverpod/api/p_api.dart'; 3 | import 'package:flavormate/riverpod/units/p_unit_conversions.dart'; 4 | import 'package:flavormate/utils/u_localizations.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | part 'p_units.g.dart'; 8 | 9 | @riverpod 10 | class PUnits extends _$PUnits { 11 | @override 12 | Future> build() async { 13 | await ref.watch(pUnitConversionProvider.selectAsync((data) => data)); 14 | final units = 15 | (await ref.watch(pApiProvider).unitsClient.findAll()) 16 | .where( 17 | (unit) => unit.language == currentLocalization().languageCode, 18 | ) 19 | .toList(); 20 | 21 | ref.keepAlive(); 22 | 23 | return units; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/riverpod/user/p_user.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/recipe/diet.dart'; 2 | import 'package:flavormate/models/user/user.dart'; 3 | import 'package:flavormate/riverpod/api/p_api.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | part 'p_user.g.dart'; 7 | 8 | @riverpod 9 | class PUser extends _$PUser { 10 | @override 11 | Future build() async { 12 | return await ref.watch(pApiProvider).userClient.getUser(); 13 | } 14 | 15 | Future setDiet(Diet diet) async { 16 | await ref 17 | .read(pApiProvider) 18 | .userClient 19 | .update(state.value!.id!, data: {'diet': diet.name}); 20 | 21 | // ref.invalidate(pHighlightProvider); 22 | ref.invalidateSelf(); 23 | } 24 | 25 | Future setMail(String mail) async { 26 | try { 27 | await ref 28 | .read(pApiProvider) 29 | .userClient 30 | .update(state.value!.id!, data: {'mail': mail}); 31 | 32 | ref.invalidateSelf(); 33 | 34 | return true; 35 | } catch (_) { 36 | return false; 37 | } 38 | } 39 | 40 | Future setPassword(Map password) async { 41 | final response = await ref 42 | .read(pApiProvider) 43 | .userClient 44 | .setPassword(state.value!.id!, password); 45 | 46 | ref.invalidateSelf(); 47 | 48 | return response; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/utils/constants.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: constant_identifier_names 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | const double PADDING = 16.0; 6 | const double BORDER_RADIUS = 12; 7 | const double TABLE_ICON_WIDTH = 48; 8 | const double WIDGET_WIDTH = 450; 9 | 10 | const FLAVORMATE_GETTING_STARTED = 11 | 'https://flavormate.de/getting-started/backend/'; 12 | 13 | const FLAVORMATE_COLOR = Colors.lightGreen; 14 | 15 | abstract class Breakpoints { 16 | static const double sm = 600; 17 | static const double m = 840; 18 | static const double l = 1200; 19 | static const double xl = 1600; 20 | 21 | static bool gt(BuildContext context, double bp) { 22 | return MediaQuery.sizeOf(context).width >= bp; 23 | } 24 | 25 | static bool lt(BuildContext context, double bp) { 26 | return MediaQuery.sizeOf(context).width < bp; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/utils/custom_mappers/custom_mappers.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/extensions/e_duration.dart'; 3 | import 'package:flavormate/utils/u_duration.dart'; 4 | 5 | part 'duration_mapper.dart'; 6 | part 'uri_mapper.dart'; 7 | 8 | final Iterable> customMappers = [ 9 | _DurationMapper(), 10 | _UriMapper(), 11 | ]; 12 | -------------------------------------------------------------------------------- /lib/utils/custom_mappers/duration_mapper.dart: -------------------------------------------------------------------------------- 1 | part of 'custom_mappers.dart'; 2 | 3 | class _DurationMapper extends SimpleMapper { 4 | @override 5 | Duration decode(Object value) { 6 | return UDuration.toDuration('$value'); 7 | } 8 | 9 | @override 10 | String encode(Duration self) { 11 | return self.toISO8601(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/utils/custom_mappers/uri_mapper.dart: -------------------------------------------------------------------------------- 1 | part of 'custom_mappers.dart'; 2 | 3 | class _UriMapper extends SimpleMapper { 4 | @override 5 | Uri decode(Object value) { 6 | return Uri.parse('$value'); 7 | } 8 | 9 | @override 10 | String encode(Uri self) { 11 | return self.toString(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/utils/custom_mappers/version_converter.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mappable/dart_mappable.dart'; 2 | import 'package:flavormate/models/version/version.dart'; 3 | 4 | class VersionConverter extends MappingHook { 5 | const VersionConverter(); 6 | 7 | @override 8 | Object? beforeDecode(Object? value) { 9 | if (value is String) return Version.fromString(value); 10 | return null; 11 | } 12 | 13 | @override 14 | Object? beforeEncode(Object? value) { 15 | if (value is Version) return value.toString(); 16 | return null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/utils/u_app_link.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/models/appLink/app_link.dart'; 2 | 3 | class UAppLink { 4 | static String createURL(AppLink appLink, String language) { 5 | return '${appLink.server}/v2/public/recipes/${appLink.id}?token=${appLink.token}&language=$language'; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lib/utils/u_double.dart: -------------------------------------------------------------------------------- 1 | abstract final class UDouble { 2 | static double? tryParsePositive(String value) { 3 | final parsed = double.tryParse(value); 4 | 5 | if (parsed == null || parsed <= 0) { 6 | return null; 7 | } else { 8 | return parsed; 9 | } 10 | } 11 | 12 | static bool isPositive(double? value) { 13 | return value != null && value > 0; 14 | } 15 | 16 | static double? add(double? a, double? b) { 17 | final sum = (a ?? 0) + (b ?? 0); 18 | return isPositive(sum) ? sum : null; 19 | } 20 | 21 | static double? multiply(double? a, double? b) { 22 | final sum = (a ?? 0) * (b ?? 0); 23 | return isPositive(sum) ? sum : null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/utils/u_duration.dart: -------------------------------------------------------------------------------- 1 | class UDuration { 2 | static Duration toDuration(String isoString) { 3 | if (!RegExp( 4 | r'^([-+])?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$', 5 | ).hasMatch(isoString)) { 6 | throw ArgumentError('String does not follow correct format'); 7 | } 8 | 9 | final weeks = _parseTime(isoString, 'W'); 10 | final days = _parseTime(isoString, 'D'); 11 | final hours = _parseTime(isoString, 'H'); 12 | final minutes = _parseTime(isoString, 'M'); 13 | final seconds = _parseTime(isoString, 'S'); 14 | 15 | return Duration( 16 | days: days + (weeks * 7), 17 | hours: hours, 18 | minutes: minutes, 19 | seconds: seconds, 20 | ); 21 | } 22 | 23 | /// Private helper method for extracting a time value from the ISO8601 string. 24 | static int _parseTime(String duration, String timeUnit) { 25 | final timeMatch = RegExp(r'\d+' + timeUnit).firstMatch(duration); 26 | 27 | if (timeMatch == null) { 28 | return 0; 29 | } 30 | final timeString = timeMatch.group(0)!; 31 | return int.parse(timeString.substring(0, timeString.length - 1)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/utils/u_go_router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/riverpod/go_router/p_go_router.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:go_router/go_router.dart'; 4 | 5 | String currentRoute() { 6 | final BuildContext context = navigationKey.currentContext!; 7 | return GoRouter.of(context).routeInformationProvider.value.uri.toString(); 8 | } 9 | -------------------------------------------------------------------------------- /lib/utils/u_localizations.dart: -------------------------------------------------------------------------------- 1 | import 'package:flavormate/riverpod/go_router/p_go_router.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | Locale currentLocalization() { 5 | final BuildContext context = navigationKey.currentContext!; 6 | return Localizations.localeOf(context); 7 | } 8 | -------------------------------------------------------------------------------- /lib/utils/u_os.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | 5 | abstract class UOS { 6 | static bool get isWeb => kIsWeb; 7 | 8 | static bool get isAndroid => Platform.isAndroid; 9 | 10 | static bool get isIOS => Platform.isIOS; 11 | 12 | static bool get isDesktop => isWeb || (!isAndroid && !isIOS); 13 | } 14 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | file_selector_linux 7 | gtk 8 | sqlite3_flutter_libs 9 | system_theme 10 | url_launcher_linux 11 | ) 12 | 13 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 14 | ) 15 | 16 | set(PLUGIN_BUNDLED_LIBRARIES) 17 | 18 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 19 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 20 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 21 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 23 | endforeach(plugin) 24 | 25 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 26 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 27 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 28 | endforeach(ffi_plugin) 29 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import app_links 9 | import file_selector_macos 10 | import package_info_plus 11 | import path_provider_foundation 12 | import share_plus 13 | import shared_preferences_foundation 14 | import sqlite3_flutter_libs 15 | import system_theme 16 | import url_launcher_macos 17 | 18 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 19 | AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) 20 | FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) 21 | FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) 22 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 23 | SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) 24 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) 25 | Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) 26 | SystemThemePlugin.register(with: registry.registrar(forPlugin: "SystemThemePlugin")) 27 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) 28 | } 29 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @main 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | 10 | override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { 11 | return true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be cookbook_appaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = flavormate 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = de.flavormate 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2024 de.flavormate. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.files.user-selected.read-only 10 | 11 | com.apple.security.network.client 12 | 13 | com.apple.security.network.server 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | com.apple.security.network.client 10 | 11 | com.apple.security.network.server 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flavormate/main.dart'; 9 | import 'package:flutter/material.dart'; 10 | import 'package:flutter_test/flutter_test.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(const MyApp()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flavormate", 3 | "short_name": "flavormate", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#FFFFFF", 7 | "theme_color": "#8BC34A", 8 | "description": "A selfhosted flavormate", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | void RegisterPlugins(flutter::PluginRegistry* registry) { 17 | AppLinksPluginCApiRegisterWithRegistrar( 18 | registry->GetRegistrarForPlugin("AppLinksPluginCApi")); 19 | FileSelectorWindowsRegisterWithRegistrar( 20 | registry->GetRegistrarForPlugin("FileSelectorWindows")); 21 | SharePlusWindowsPluginCApiRegisterWithRegistrar( 22 | registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); 23 | Sqlite3FlutterLibsPluginRegisterWithRegistrar( 24 | registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); 25 | SystemThemePluginRegisterWithRegistrar( 26 | registry->GetRegistrarForPlugin("SystemThemePlugin")); 27 | UrlLauncherWindowsRegisterWithRegistrar( 28 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 29 | } 30 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | app_links 7 | file_selector_windows 8 | share_plus 9 | sqlite3_flutter_libs 10 | system_theme 11 | url_launcher_windows 12 | ) 13 | 14 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 15 | ) 16 | 17 | set(PLUGIN_BUNDLED_LIBRARIES) 18 | 19 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 20 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 21 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 23 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 24 | endforeach(plugin) 25 | 26 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 27 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 28 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 29 | endforeach(ffi_plugin) 30 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlavorMate/flavormate-app/112c9e45370763cd3377b82598ac8c2bc5488f32/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | --------------------------------------------------------------------------------