├── .gitignore ├── .metadata ├── README.md ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── recipes │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.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 │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── settings_aar.gradle ├── build-old.yaml ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── 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@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-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 ├── lib ├── config │ └── constants.dart ├── main.dart ├── models │ ├── categories │ │ ├── categories.dart │ │ ├── categories.g.dart │ │ ├── category.dart │ │ └── category.g.dart │ ├── recipes │ │ ├── recipe.dart │ │ ├── recipe.g.dart │ │ ├── recipes.dart │ │ └── recipes.g.dart │ └── server_error.dart ├── services │ ├── hive.dart │ └── network.dart └── ui │ ├── items │ ├── category.dart │ ├── recipe.dart │ └── recipe_random.dart │ └── screens │ ├── category │ ├── bloc │ │ ├── bloc.dart │ │ ├── bloc.freezed.dart │ │ ├── events.dart │ │ └── states.dart │ └── category.dart │ ├── favorite │ └── favorite.dart │ ├── home │ ├── bloc │ │ ├── bloc.dart │ │ ├── bloc.freezed.dart │ │ ├── events.dart │ │ └── states.dart │ └── home.dart │ ├── recipe │ ├── bloc │ │ ├── bloc.dart │ │ ├── bloc.freezed.dart │ │ ├── events.dart │ │ └── states.dart │ └── recipe.dart │ ├── search │ ├── bloc │ │ ├── bloc.dart │ │ ├── bloc.freezed.dart │ │ ├── events.dart │ │ └── states.dart │ └── search.dart │ └── video.dart ├── pubspec.lock ├── pubspec.yaml ├── screenshot ├── 0.jpg ├── 1.jpg └── 2.jpg ├── test └── widget_test.dart └── web ├── favicon.png ├── icons ├── Icon-192.png └── Icon-512.png ├── index.html └── manifest.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Exceptions to above rules. 44 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 45 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: e606910f28be51c8151f6169072afe3b3a8b3c5e 8 | channel: beta 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Recipes 2 | 3 | 4 | 5 | 6 | 7 | ## 🔌 Plugins 8 | | Name | 9 | |------| 10 | |[**Dio**](https://pub.dev/packages/dio) 11 | |[**Freezed**](https://pub.dev/packages/freezed) 12 | |[**Google_fonts**](https://pub.dev/packages/google_fonts) 13 | |[**Flutter_bloc**](https://pub.dev/packages/flutter_bloc) 14 | |[**Cached_network_image**](https://pub.dev/packages/cached_network_image) 15 | |[**Dartz**](https://pub.dev/packages/dartz) 16 | |[**Json_serializable**](https://pub.dev/packages/json_serializable) 17 | |[**Youtube_player_flutter**](https://pub.dev/packages/youtube_player_flutter) 18 | |[**Hive**](https://pub.dev/packages/hive) 19 | 20 | --- 21 | 22 | #### Developer [Abdelouahed Medjoudja](https://www.facebook.com/AbdelouahedMedjoudja) 23 | -------------------------------------------------------------------------------- /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/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 28 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.example.recipes" 42 | minSdkVersion 17 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | } 47 | 48 | buildTypes { 49 | release { 50 | // TODO: Add your own signing config for the release build. 51 | // Signing with the debug keys for now, so `flutter run --release` works. 52 | signingConfig signingConfigs.debug 53 | } 54 | } 55 | } 56 | 57 | flutter { 58 | source '../..' 59 | } 60 | 61 | dependencies { 62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 63 | } 64 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 13 | 20 | 24 | 28 | 33 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/recipes/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.recipes 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /build-old.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | json_serializable: 5 | options: 6 | # Options configure how source code is generated for every 7 | # `@JsonSerializable`-annotated class in the package. 8 | # 9 | # The default value for each is listed. 10 | any_map: false 11 | checked: false 12 | create_factory: true 13 | create_to_json: true 14 | disallow_unrecognized_keys: false 15 | explicit_to_json: true 16 | field_rename: none 17 | generic_argument_factories: false 18 | ignore_unannotated: false 19 | include_if_null: true 20 | nullable: true -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | ); 26 | name = "Embed Frameworks"; 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 9740EEB11CF90186004384FC /* Flutter */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 65 | ); 66 | name = Flutter; 67 | sourceTree = ""; 68 | }; 69 | 97C146E51CF9000F007C117D = { 70 | isa = PBXGroup; 71 | children = ( 72 | 9740EEB11CF90186004384FC /* Flutter */, 73 | 97C146F01CF9000F007C117D /* Runner */, 74 | 97C146EF1CF9000F007C117D /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 97C146EF1CF9000F007C117D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 97C146EE1CF9000F007C117D /* Runner.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 97C146F01CF9000F007C117D /* Runner */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 92 | 97C147021CF9000F007C117D /* Info.plist */, 93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 97 | ); 98 | path = Runner; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | 97C146ED1CF9000F007C117D /* Runner */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 107 | buildPhases = ( 108 | 9740EEB61CF901F6004384FC /* Run Script */, 109 | 97C146EA1CF9000F007C117D /* Sources */, 110 | 97C146EB1CF9000F007C117D /* Frameworks */, 111 | 97C146EC1CF9000F007C117D /* Resources */, 112 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = Runner; 120 | productName = Runner; 121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | 97C146E61CF9000F007C117D /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 1020; 131 | ORGANIZATIONNAME = ""; 132 | TargetAttributes = { 133 | 97C146ED1CF9000F007C117D = { 134 | CreatedOnToolsVersion = 7.3.1; 135 | LastSwiftMigration = 1100; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 140 | compatibilityVersion = "Xcode 9.3"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | Base, 146 | ); 147 | mainGroup = 97C146E51CF9000F007C117D; 148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 97C146ED1CF9000F007C117D /* Runner */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 97C146EC1CF9000F007C117D /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXShellScriptBuildPhase section */ 172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 173 | isa = PBXShellScriptBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | inputPaths = ( 178 | ); 179 | name = "Thin Binary"; 180 | outputPaths = ( 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | shellPath = /bin/sh; 184 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 185 | }; 186 | 9740EEB61CF901F6004384FC /* Run Script */ = { 187 | isa = PBXShellScriptBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | inputPaths = ( 192 | ); 193 | name = "Run Script"; 194 | outputPaths = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | shellPath = /bin/sh; 198 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 199 | }; 200 | /* End PBXShellScriptBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 97C146EA1CF9000F007C117D /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 208 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXSourcesBuildPhase section */ 213 | 214 | /* Begin PBXVariantGroup section */ 215 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 97C146FB1CF9000F007C117D /* Base */, 219 | ); 220 | name = Main.storyboard; 221 | sourceTree = ""; 222 | }; 223 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 97C147001CF9000F007C117D /* Base */, 227 | ); 228 | name = LaunchScreen.storyboard; 229 | sourceTree = ""; 230 | }; 231 | /* End PBXVariantGroup section */ 232 | 233 | /* Begin XCBuildConfiguration section */ 234 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_ANALYZER_NONNULL = YES; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 240 | CLANG_CXX_LIBRARY = "libc++"; 241 | CLANG_ENABLE_MODULES = YES; 242 | CLANG_ENABLE_OBJC_ARC = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INFINITE_RECURSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 258 | CLANG_WARN_STRICT_PROTOTYPES = YES; 259 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 260 | CLANG_WARN_UNREACHABLE_CODE = YES; 261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 263 | COPY_PHASE_STRIP = NO; 264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 276 | MTL_ENABLE_DEBUG_INFO = NO; 277 | SDKROOT = iphoneos; 278 | SUPPORTED_PLATFORMS = iphoneos; 279 | TARGETED_DEVICE_FAMILY = "1,2"; 280 | VALIDATE_PRODUCT = YES; 281 | }; 282 | name = Profile; 283 | }; 284 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 285 | isa = XCBuildConfiguration; 286 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | CLANG_ENABLE_MODULES = YES; 290 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 291 | ENABLE_BITCODE = NO; 292 | FRAMEWORK_SEARCH_PATHS = ( 293 | "$(inherited)", 294 | "$(PROJECT_DIR)/Flutter", 295 | ); 296 | INFOPLIST_FILE = Runner/Info.plist; 297 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 298 | LIBRARY_SEARCH_PATHS = ( 299 | "$(inherited)", 300 | "$(PROJECT_DIR)/Flutter", 301 | ); 302 | PRODUCT_BUNDLE_IDENTIFIER = com.example.recipes; 303 | PRODUCT_NAME = "$(TARGET_NAME)"; 304 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 305 | SWIFT_VERSION = 5.0; 306 | VERSIONING_SYSTEM = "apple-generic"; 307 | }; 308 | name = Profile; 309 | }; 310 | 97C147031CF9000F007C117D /* Debug */ = { 311 | isa = XCBuildConfiguration; 312 | buildSettings = { 313 | ALWAYS_SEARCH_USER_PATHS = NO; 314 | CLANG_ANALYZER_NONNULL = YES; 315 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 316 | CLANG_CXX_LIBRARY = "libc++"; 317 | CLANG_ENABLE_MODULES = YES; 318 | CLANG_ENABLE_OBJC_ARC = YES; 319 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 320 | CLANG_WARN_BOOL_CONVERSION = YES; 321 | CLANG_WARN_COMMA = YES; 322 | CLANG_WARN_CONSTANT_CONVERSION = YES; 323 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 324 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 325 | CLANG_WARN_EMPTY_BODY = YES; 326 | CLANG_WARN_ENUM_CONVERSION = YES; 327 | CLANG_WARN_INFINITE_RECURSION = YES; 328 | CLANG_WARN_INT_CONVERSION = YES; 329 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 330 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 331 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 332 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 333 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 334 | CLANG_WARN_STRICT_PROTOTYPES = YES; 335 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 336 | CLANG_WARN_UNREACHABLE_CODE = YES; 337 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 338 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 339 | COPY_PHASE_STRIP = NO; 340 | DEBUG_INFORMATION_FORMAT = dwarf; 341 | ENABLE_STRICT_OBJC_MSGSEND = YES; 342 | ENABLE_TESTABILITY = YES; 343 | GCC_C_LANGUAGE_STANDARD = gnu99; 344 | GCC_DYNAMIC_NO_PIC = NO; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_OPTIMIZATION_LEVEL = 0; 347 | GCC_PREPROCESSOR_DEFINITIONS = ( 348 | "DEBUG=1", 349 | "$(inherited)", 350 | ); 351 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 352 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 353 | GCC_WARN_UNDECLARED_SELECTOR = YES; 354 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 355 | GCC_WARN_UNUSED_FUNCTION = YES; 356 | GCC_WARN_UNUSED_VARIABLE = YES; 357 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 358 | MTL_ENABLE_DEBUG_INFO = YES; 359 | ONLY_ACTIVE_ARCH = YES; 360 | SDKROOT = iphoneos; 361 | TARGETED_DEVICE_FAMILY = "1,2"; 362 | }; 363 | name = Debug; 364 | }; 365 | 97C147041CF9000F007C117D /* Release */ = { 366 | isa = XCBuildConfiguration; 367 | buildSettings = { 368 | ALWAYS_SEARCH_USER_PATHS = NO; 369 | CLANG_ANALYZER_NONNULL = YES; 370 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 371 | CLANG_CXX_LIBRARY = "libc++"; 372 | CLANG_ENABLE_MODULES = YES; 373 | CLANG_ENABLE_OBJC_ARC = YES; 374 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 375 | CLANG_WARN_BOOL_CONVERSION = YES; 376 | CLANG_WARN_COMMA = YES; 377 | CLANG_WARN_CONSTANT_CONVERSION = YES; 378 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 379 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 380 | CLANG_WARN_EMPTY_BODY = YES; 381 | CLANG_WARN_ENUM_CONVERSION = YES; 382 | CLANG_WARN_INFINITE_RECURSION = YES; 383 | CLANG_WARN_INT_CONVERSION = YES; 384 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 385 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 386 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 387 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 388 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 389 | CLANG_WARN_STRICT_PROTOTYPES = YES; 390 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 391 | CLANG_WARN_UNREACHABLE_CODE = YES; 392 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 393 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 394 | COPY_PHASE_STRIP = NO; 395 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 396 | ENABLE_NS_ASSERTIONS = NO; 397 | ENABLE_STRICT_OBJC_MSGSEND = YES; 398 | GCC_C_LANGUAGE_STANDARD = gnu99; 399 | GCC_NO_COMMON_BLOCKS = YES; 400 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 401 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 402 | GCC_WARN_UNDECLARED_SELECTOR = YES; 403 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 404 | GCC_WARN_UNUSED_FUNCTION = YES; 405 | GCC_WARN_UNUSED_VARIABLE = YES; 406 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 407 | MTL_ENABLE_DEBUG_INFO = NO; 408 | SDKROOT = iphoneos; 409 | SUPPORTED_PLATFORMS = iphoneos; 410 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 411 | TARGETED_DEVICE_FAMILY = "1,2"; 412 | VALIDATE_PRODUCT = YES; 413 | }; 414 | name = Release; 415 | }; 416 | 97C147061CF9000F007C117D /* Debug */ = { 417 | isa = XCBuildConfiguration; 418 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 419 | buildSettings = { 420 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 421 | CLANG_ENABLE_MODULES = YES; 422 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 423 | ENABLE_BITCODE = NO; 424 | FRAMEWORK_SEARCH_PATHS = ( 425 | "$(inherited)", 426 | "$(PROJECT_DIR)/Flutter", 427 | ); 428 | INFOPLIST_FILE = Runner/Info.plist; 429 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 430 | LIBRARY_SEARCH_PATHS = ( 431 | "$(inherited)", 432 | "$(PROJECT_DIR)/Flutter", 433 | ); 434 | PRODUCT_BUNDLE_IDENTIFIER = com.example.recipes; 435 | PRODUCT_NAME = "$(TARGET_NAME)"; 436 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 437 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 438 | SWIFT_VERSION = 5.0; 439 | VERSIONING_SYSTEM = "apple-generic"; 440 | }; 441 | name = Debug; 442 | }; 443 | 97C147071CF9000F007C117D /* Release */ = { 444 | isa = XCBuildConfiguration; 445 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 446 | buildSettings = { 447 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 448 | CLANG_ENABLE_MODULES = YES; 449 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 450 | ENABLE_BITCODE = NO; 451 | FRAMEWORK_SEARCH_PATHS = ( 452 | "$(inherited)", 453 | "$(PROJECT_DIR)/Flutter", 454 | ); 455 | INFOPLIST_FILE = Runner/Info.plist; 456 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 457 | LIBRARY_SEARCH_PATHS = ( 458 | "$(inherited)", 459 | "$(PROJECT_DIR)/Flutter", 460 | ); 461 | PRODUCT_BUNDLE_IDENTIFIER = com.example.recipes; 462 | PRODUCT_NAME = "$(TARGET_NAME)"; 463 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 464 | SWIFT_VERSION = 5.0; 465 | VERSIONING_SYSTEM = "apple-generic"; 466 | }; 467 | name = Release; 468 | }; 469 | /* End XCBuildConfiguration section */ 470 | 471 | /* Begin XCConfigurationList section */ 472 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 473 | isa = XCConfigurationList; 474 | buildConfigurations = ( 475 | 97C147031CF9000F007C117D /* Debug */, 476 | 97C147041CF9000F007C117D /* Release */, 477 | 249021D3217E4FDB00AE95B9 /* Profile */, 478 | ); 479 | defaultConfigurationIsVisible = 0; 480 | defaultConfigurationName = Release; 481 | }; 482 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 483 | isa = XCConfigurationList; 484 | buildConfigurations = ( 485 | 97C147061CF9000F007C117D /* Debug */, 486 | 97C147071CF9000F007C117D /* Release */, 487 | 249021D4217E4FDB00AE95B9 /* Profile */, 488 | ); 489 | defaultConfigurationIsVisible = 0; 490 | defaultConfigurationName = Release; 491 | }; 492 | /* End XCConfigurationList section */ 493 | }; 494 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 495 | } 496 | -------------------------------------------------------------------------------- /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.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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 UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 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/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/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/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/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/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/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/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/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/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/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/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/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/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-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/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/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 replacing 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`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | io.flutter.embedded_views_preview 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | recipes 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/config/constants.dart: -------------------------------------------------------------------------------- 1 | class AppConstants { 2 | AppConstants._(); 3 | 4 | static const String HOST = 'https://www.themealdb.com/'; 5 | static const String API = '${HOST}api/json/v1/1/'; 6 | } 7 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:google_fonts/google_fonts.dart'; 3 | import 'package:recipes/services/hive.dart'; 4 | import 'package:recipes/ui/screens/home/home.dart'; 5 | 6 | import 'services/network.dart'; 7 | 8 | void main() async { 9 | WidgetsFlutterBinding.ensureInitialized(); 10 | await AppHive.instance.init(); 11 | runApp(MyApp()); 12 | } 13 | 14 | class MyApp extends StatefulWidget { 15 | @override 16 | State createState() => _MyAppState(); 17 | } 18 | 19 | class _MyAppState extends State { 20 | @override 21 | Widget build(BuildContext context) => MaterialApp( 22 | title: 'Flutter Recipes', 23 | debugShowCheckedModeBanner: false, 24 | theme: ThemeData( 25 | primarySwatch: Colors.red, 26 | scaffoldBackgroundColor: Colors.grey[200], 27 | visualDensity: VisualDensity.adaptivePlatformDensity, 28 | appBarTheme: AppBarTheme( 29 | textTheme: GoogleFonts.latoTextTheme( 30 | Theme.of(context).textTheme, 31 | ).apply(bodyColor: Colors.white), 32 | ), 33 | textTheme: GoogleFonts.latoTextTheme( 34 | Theme.of(context).textTheme, 35 | ), 36 | ), 37 | home: HomeScreen(), 38 | ); 39 | 40 | @override 41 | void dispose() { 42 | AppNetwork.instance.close(); 43 | AppHive.instance.close(); 44 | super.dispose(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/models/categories/categories.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | import 'category.dart'; 4 | 5 | part 'categories.g.dart'; 6 | 7 | @JsonSerializable(explicitToJson: true) 8 | class Categories { 9 | @JsonKey(name: 'categories') 10 | final List items; 11 | 12 | Categories({this.items}); 13 | 14 | factory Categories.fromJson(Map json) => 15 | _$CategoriesFromJson(json); 16 | 17 | Map toJson() => _$CategoriesToJson(this); 18 | } 19 | -------------------------------------------------------------------------------- /lib/models/categories/categories.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'categories.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Categories _$CategoriesFromJson(Map json) { 10 | return Categories( 11 | items: (json['categories'] as List) 12 | ?.map((e) => 13 | e == null ? null : Category.fromJson(e as Map)) 14 | ?.toList(), 15 | ); 16 | } 17 | 18 | Map _$CategoriesToJson(Categories instance) => 19 | { 20 | 'categories': instance.items?.map((e) => e?.toJson())?.toList(), 21 | }; 22 | -------------------------------------------------------------------------------- /lib/models/categories/category.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'category.g.dart'; 4 | 5 | @JsonSerializable() 6 | class Category { 7 | @JsonKey(name: 'idCategory') 8 | final id; 9 | @JsonKey(name: 'strCategory') 10 | final name; 11 | @JsonKey(name: 'strCategoryThumb') 12 | final image; 13 | @JsonKey(name: 'strCategoryDescription') 14 | final description; 15 | 16 | Category({this.id, this.name, this.image, this.description}); 17 | 18 | factory Category.fromJson(Map json) => 19 | _$CategoryFromJson(json); 20 | 21 | Map toJson() => _$CategoryToJson(this); 22 | } 23 | -------------------------------------------------------------------------------- /lib/models/categories/category.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'category.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Category _$CategoryFromJson(Map json) { 10 | return Category( 11 | id: json['idCategory'], 12 | name: json['strCategory'], 13 | image: json['strCategoryThumb'], 14 | description: json['strCategoryDescription'], 15 | ); 16 | } 17 | 18 | Map _$CategoryToJson(Category instance) => { 19 | 'idCategory': instance.id, 20 | 'strCategory': instance.name, 21 | 'strCategoryThumb': instance.image, 22 | 'strCategoryDescription': instance.description, 23 | }; 24 | -------------------------------------------------------------------------------- /lib/models/recipes/recipe.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | part 'recipe.g.dart'; 5 | 6 | @HiveType() 7 | @JsonSerializable(nullable: false) 8 | class Recipe { 9 | @HiveField(0) 10 | @JsonKey(name: 'idMeal') 11 | final id; 12 | @HiveField(1) 13 | @JsonKey(name: 'strMeal') 14 | final title; 15 | @JsonKey(name: 'strInstructions') 16 | final instructions; 17 | @HiveField(2) 18 | @JsonKey(name: 'strMealThumb') 19 | final image; 20 | @JsonKey(name: 'strTags') 21 | final tags; 22 | @JsonKey(name: 'strYoutube') 23 | final video; 24 | @JsonKey(name: 'strCategory') 25 | final category; 26 | @JsonKey(name: 'strIngredient1') 27 | final ingredient1; 28 | @JsonKey(name: 'strIngredient2') 29 | final ingredient2; 30 | @JsonKey(name: 'strIngredient3') 31 | final ingredient3; 32 | @JsonKey(name: 'strIngredient4') 33 | final ingredient4; 34 | @JsonKey(name: 'strIngredient5') 35 | final ingredient5; 36 | @JsonKey(name: 'strIngredient6') 37 | final ingredient6; 38 | @JsonKey(name: 'strIngredient7') 39 | final ingredient7; 40 | @JsonKey(name: 'strIngredient8') 41 | final ingredient8; 42 | @JsonKey(name: 'strIngredient9') 43 | final ingredient9; 44 | @JsonKey(name: 'strIngredient10') 45 | final ingredient10; 46 | @JsonKey(name: 'strIngredient11') 47 | final ingredient11; 48 | @JsonKey(name: 'strIngredient12') 49 | final ingredient12; 50 | @JsonKey(name: 'strIngredient13') 51 | final ingredient13; 52 | @JsonKey(name: 'strIngredient14') 53 | final ingredient14; 54 | @JsonKey(name: 'strIngredient15') 55 | final ingredient15; 56 | @JsonKey(name: 'strIngredient16') 57 | final ingredient16; 58 | @JsonKey(name: 'strIngredient17') 59 | final ingredient17; 60 | @JsonKey(name: 'strIngredient18') 61 | final ingredient18; 62 | @JsonKey(name: 'strIngredient19') 63 | final ingredient19; 64 | @JsonKey(name: 'strIngredient20') 65 | final ingredient20; 66 | @JsonKey(name: 'strMeasure1') 67 | final measure1; 68 | @JsonKey(name: 'strMeasure2') 69 | final measure2; 70 | @JsonKey(name: 'strMeasure3') 71 | final measure3; 72 | @JsonKey(name: 'strMeasure4') 73 | final measure4; 74 | @JsonKey(name: 'strMeasure5') 75 | final measure5; 76 | @JsonKey(name: 'strMeasure6') 77 | final measure6; 78 | @JsonKey(name: 'strMeasure7') 79 | final measure7; 80 | @JsonKey(name: 'strMeasure8') 81 | final measure8; 82 | @JsonKey(name: 'strMeasure9') 83 | final measure9; 84 | @JsonKey(name: 'strMeasure10') 85 | final measure10; 86 | @JsonKey(name: 'strMeasure11') 87 | final measure11; 88 | @JsonKey(name: 'strMeasure12') 89 | final measure12; 90 | @JsonKey(name: 'strMeasure13') 91 | final measure13; 92 | @JsonKey(name: 'strMeasure14') 93 | final measure14; 94 | @JsonKey(name: 'strMeasure15') 95 | final measure15; 96 | @JsonKey(name: 'strMeasure16') 97 | final measure16; 98 | @JsonKey(name: 'strMeasure17') 99 | final measure17; 100 | @JsonKey(name: 'strMeasure18') 101 | final measure18; 102 | @JsonKey(name: 'strMeasure19') 103 | final measure19; 104 | @JsonKey(name: 'strMeasure20') 105 | final measure20; 106 | 107 | Recipe({ 108 | this.id, 109 | this.title, 110 | this.instructions, 111 | this.image, 112 | this.tags, 113 | this.video, 114 | this.category, 115 | this.ingredient1, 116 | this.ingredient2, 117 | this.ingredient3, 118 | this.ingredient4, 119 | this.ingredient5, 120 | this.ingredient6, 121 | this.ingredient7, 122 | this.ingredient8, 123 | this.ingredient9, 124 | this.ingredient10, 125 | this.ingredient11, 126 | this.ingredient12, 127 | this.ingredient13, 128 | this.ingredient14, 129 | this.ingredient15, 130 | this.ingredient16, 131 | this.ingredient17, 132 | this.ingredient18, 133 | this.ingredient19, 134 | this.ingredient20, 135 | this.measure1, 136 | this.measure2, 137 | this.measure3, 138 | this.measure4, 139 | this.measure5, 140 | this.measure6, 141 | this.measure7, 142 | this.measure8, 143 | this.measure9, 144 | this.measure10, 145 | this.measure11, 146 | this.measure12, 147 | this.measure13, 148 | this.measure14, 149 | this.measure15, 150 | this.measure16, 151 | this.measure17, 152 | this.measure18, 153 | this.measure19, 154 | this.measure20, 155 | }); 156 | 157 | factory Recipe.fromJson(Map json) => _$RecipeFromJson(json); 158 | 159 | Map toJson() => _$RecipeToJson(this); 160 | } 161 | -------------------------------------------------------------------------------- /lib/models/recipes/recipe.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'recipe.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class RecipeAdapter extends TypeAdapter { 10 | @override 11 | Recipe read(BinaryReader reader) { 12 | var numOfFields = reader.readByte(); 13 | var fields = { 14 | for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 15 | }; 16 | return Recipe( 17 | id: fields[0] as dynamic, 18 | title: fields[1] as dynamic, 19 | image: fields[2] as dynamic, 20 | ); 21 | } 22 | 23 | @override 24 | void write(BinaryWriter writer, Recipe obj) { 25 | writer 26 | ..writeByte(3) 27 | ..writeByte(0) 28 | ..write(obj.id) 29 | ..writeByte(1) 30 | ..write(obj.title) 31 | ..writeByte(2) 32 | ..write(obj.image); 33 | } 34 | 35 | @override 36 | int get typeId => 0; 37 | } 38 | 39 | // ************************************************************************** 40 | // JsonSerializableGenerator 41 | // ************************************************************************** 42 | 43 | Recipe _$RecipeFromJson(Map json) { 44 | return Recipe( 45 | id: json['idMeal'], 46 | title: json['strMeal'], 47 | instructions: json['strInstructions'], 48 | image: json['strMealThumb'], 49 | tags: json['strTags'], 50 | video: json['strYoutube'], 51 | category: json['strCategory'], 52 | ingredient1: json['strIngredient1'], 53 | ingredient2: json['strIngredient2'], 54 | ingredient3: json['strIngredient3'], 55 | ingredient4: json['strIngredient4'], 56 | ingredient5: json['strIngredient5'], 57 | ingredient6: json['strIngredient6'], 58 | ingredient7: json['strIngredient7'], 59 | ingredient8: json['strIngredient8'], 60 | ingredient9: json['strIngredient9'], 61 | ingredient10: json['strIngredient10'], 62 | ingredient11: json['strIngredient11'], 63 | ingredient12: json['strIngredient12'], 64 | ingredient13: json['strIngredient13'], 65 | ingredient14: json['strIngredient14'], 66 | ingredient15: json['strIngredient15'], 67 | ingredient16: json['strIngredient16'], 68 | ingredient17: json['strIngredient17'], 69 | ingredient18: json['strIngredient18'], 70 | ingredient19: json['strIngredient19'], 71 | ingredient20: json['strIngredient20'], 72 | measure1: json['strMeasure1'], 73 | measure2: json['strMeasure2'], 74 | measure3: json['strMeasure3'], 75 | measure4: json['strMeasure4'], 76 | measure5: json['strMeasure5'], 77 | measure6: json['strMeasure6'], 78 | measure7: json['strMeasure7'], 79 | measure8: json['strMeasure8'], 80 | measure9: json['strMeasure9'], 81 | measure10: json['strMeasure10'], 82 | measure11: json['strMeasure11'], 83 | measure12: json['strMeasure12'], 84 | measure13: json['strMeasure13'], 85 | measure14: json['strMeasure14'], 86 | measure15: json['strMeasure15'], 87 | measure16: json['strMeasure16'], 88 | measure17: json['strMeasure17'], 89 | measure18: json['strMeasure18'], 90 | measure19: json['strMeasure19'], 91 | measure20: json['strMeasure20'], 92 | ); 93 | } 94 | 95 | Map _$RecipeToJson(Recipe instance) => { 96 | 'idMeal': instance.id, 97 | 'strMeal': instance.title, 98 | 'strInstructions': instance.instructions, 99 | 'strMealThumb': instance.image, 100 | 'strTags': instance.tags, 101 | 'strYoutube': instance.video, 102 | 'strCategory': instance.category, 103 | 'strIngredient1': instance.ingredient1, 104 | 'strIngredient2': instance.ingredient2, 105 | 'strIngredient3': instance.ingredient3, 106 | 'strIngredient4': instance.ingredient4, 107 | 'strIngredient5': instance.ingredient5, 108 | 'strIngredient6': instance.ingredient6, 109 | 'strIngredient7': instance.ingredient7, 110 | 'strIngredient8': instance.ingredient8, 111 | 'strIngredient9': instance.ingredient9, 112 | 'strIngredient10': instance.ingredient10, 113 | 'strIngredient11': instance.ingredient11, 114 | 'strIngredient12': instance.ingredient12, 115 | 'strIngredient13': instance.ingredient13, 116 | 'strIngredient14': instance.ingredient14, 117 | 'strIngredient15': instance.ingredient15, 118 | 'strIngredient16': instance.ingredient16, 119 | 'strIngredient17': instance.ingredient17, 120 | 'strIngredient18': instance.ingredient18, 121 | 'strIngredient19': instance.ingredient19, 122 | 'strIngredient20': instance.ingredient20, 123 | 'strMeasure1': instance.measure1, 124 | 'strMeasure2': instance.measure2, 125 | 'strMeasure3': instance.measure3, 126 | 'strMeasure4': instance.measure4, 127 | 'strMeasure5': instance.measure5, 128 | 'strMeasure6': instance.measure6, 129 | 'strMeasure7': instance.measure7, 130 | 'strMeasure8': instance.measure8, 131 | 'strMeasure9': instance.measure9, 132 | 'strMeasure10': instance.measure10, 133 | 'strMeasure11': instance.measure11, 134 | 'strMeasure12': instance.measure12, 135 | 'strMeasure13': instance.measure13, 136 | 'strMeasure14': instance.measure14, 137 | 'strMeasure15': instance.measure15, 138 | 'strMeasure16': instance.measure16, 139 | 'strMeasure17': instance.measure17, 140 | 'strMeasure18': instance.measure18, 141 | 'strMeasure19': instance.measure19, 142 | 'strMeasure20': instance.measure20, 143 | }; 144 | -------------------------------------------------------------------------------- /lib/models/recipes/recipes.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | import 'recipe.dart'; 4 | 5 | part 'recipes.g.dart'; 6 | 7 | @JsonSerializable(explicitToJson: true) 8 | class Recipes { 9 | @JsonKey(name: 'meals') 10 | final List items; 11 | 12 | Recipes({this.items}); 13 | 14 | factory Recipes.fromJson(Map json) => 15 | _$RecipesFromJson(json); 16 | 17 | Map toJson() => _$RecipesToJson(this); 18 | } 19 | -------------------------------------------------------------------------------- /lib/models/recipes/recipes.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'recipes.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Recipes _$RecipesFromJson(Map json) { 10 | return Recipes( 11 | items: (json['meals'] as List) 12 | ?.map((e) => 13 | e == null ? null : Recipe.fromJson(e as Map)) 14 | ?.toList(), 15 | ); 16 | } 17 | 18 | Map _$RecipesToJson(Recipes instance) => { 19 | 'meals': instance.items?.map((e) => e?.toJson())?.toList(), 20 | }; 21 | -------------------------------------------------------------------------------- /lib/models/server_error.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | 3 | class ServerError { 4 | final String message; 5 | 6 | ServerError({@required this.message}); 7 | } 8 | -------------------------------------------------------------------------------- /lib/services/hive.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | import 'package:path_provider/path_provider.dart'; 3 | 4 | import '../models/recipes/recipe.dart'; 5 | 6 | class AppHive { 7 | static const KEY_NAME = 'recipes'; 8 | 9 | AppHive._(); 10 | 11 | static AppHive _instance; 12 | 13 | static AppHive get instance { 14 | if (_instance == null) _instance = AppHive._(); 15 | return _instance; 16 | } 17 | 18 | Box _box; 19 | 20 | Future init() async { 21 | final appDocumentDir = await getApplicationDocumentsDirectory(); 22 | Hive 23 | ..init(appDocumentDir.path) 24 | ..registerAdapter(RecipeAdapter()); 25 | _box = await Hive.openBox(KEY_NAME); 26 | return Future.value(); 27 | } 28 | 29 | Future insert(Recipe recipe) => _box.put(recipe.id, recipe); 30 | 31 | Future delete(Recipe recipe) => _box.delete(recipe.id); 32 | 33 | Box selectAll() => _box; 34 | 35 | bool isExist(id) => _box.containsKey(id); 36 | 37 | Future close() async { 38 | await _box.compact(); 39 | return Hive.close(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/services/network.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:dio/dio.dart'; 3 | 4 | import '../models/server_error.dart'; 5 | import '../config/constants.dart'; 6 | 7 | class AppNetwork { 8 | final _dio = Dio(BaseOptions(baseUrl: AppConstants.API)); 9 | 10 | AppNetwork._(); 11 | 12 | static AppNetwork _instance; 13 | 14 | static AppNetwork get instance { 15 | if (_instance == null) _instance = AppNetwork._(); 16 | return _instance; 17 | } 18 | 19 | Future> get( 20 | String path, { 21 | Map queries, 22 | }) async { 23 | try { 24 | Response response = 25 | await _dio.get(path, queryParameters: queries).catchError((error) { 26 | return left(ServerError(message: '$error')); 27 | }); 28 | 29 | return right(response.data); 30 | } catch (e) { 31 | return left(ServerError(message: '$e')); 32 | } 33 | } 34 | 35 | void close() { 36 | _dio.close(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/ui/items/category.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:recipes/models/categories/category.dart'; 5 | import 'package:recipes/ui/screens/category/category.dart'; 6 | 7 | class CategoryItem extends StatelessWidget { 8 | final Category category; 9 | 10 | void _onTap(BuildContext context) { 11 | Navigator.push( 12 | context, 13 | CupertinoPageRoute( 14 | builder: (_) => CategoryScreen(categoryName: category.name), 15 | ), 16 | ); 17 | } 18 | 19 | const CategoryItem({Key key, @required this.category}) : super(key: key); 20 | 21 | @override 22 | Widget build(BuildContext context) => Card( 23 | clipBehavior: Clip.antiAlias, 24 | margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), 25 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), 26 | child: ListTile( 27 | title: Text( 28 | category.name, 29 | style: TextStyle( 30 | color: Colors.black87, 31 | fontWeight: FontWeight.bold, 32 | ), 33 | ), 34 | trailing: CachedNetworkImage(imageUrl: category.image), 35 | contentPadding: const EdgeInsets.symmetric( 36 | horizontal: 25, 37 | vertical: 15, 38 | ), 39 | onTap: () { 40 | _onTap(context); 41 | }, 42 | ), 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /lib/ui/items/recipe.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:recipes/models/recipes/recipe.dart'; 4 | import 'package:recipes/services/hive.dart'; 5 | import 'package:recipes/ui/screens/recipe/recipe.dart'; 6 | 7 | class RecipeItem extends StatefulWidget { 8 | final Recipe recipe; 9 | 10 | const RecipeItem({Key key, @required this.recipe}) : super(key: key); 11 | 12 | @override 13 | _RecipeItemState createState() => _RecipeItemState(); 14 | } 15 | 16 | class _RecipeItemState extends State { 17 | bool _isFavorite = false; 18 | 19 | void _tapFavorite() { 20 | setState(() { 21 | _isFavorite = !_isFavorite; 22 | }); 23 | 24 | if (_isFavorite) 25 | AppHive.instance.insert(widget.recipe); 26 | else 27 | AppHive.instance.delete(widget.recipe); 28 | } 29 | 30 | void _onTap() { 31 | Navigator.push( 32 | context, 33 | CupertinoPageRoute( 34 | builder: (_) => RecipeScreen( 35 | recipeId: widget.recipe.id, 36 | ), 37 | ), 38 | ); 39 | } 40 | 41 | @override 42 | void initState() { 43 | super.initState(); 44 | 45 | // check is favorite 46 | _isFavorite = AppHive.instance.isExist(widget.recipe.id); 47 | } 48 | 49 | @override 50 | Widget build(BuildContext context) => InkWell( 51 | onTap: _onTap, 52 | child: AspectRatio( 53 | aspectRatio: 1.5, 54 | child: Container( 55 | margin: const EdgeInsets.only(bottom: 5), 56 | decoration: BoxDecoration( 57 | image: DecorationImage( 58 | fit: BoxFit.cover, 59 | image: NetworkImage( 60 | widget.recipe.image, 61 | ), 62 | ), 63 | ), 64 | child: Container( 65 | color: Colors.black.withOpacity(.5), 66 | alignment: Alignment.bottomLeft, 67 | padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), 68 | child: Row( 69 | children: [ 70 | Expanded( 71 | child: Text( 72 | widget.recipe.title, 73 | maxLines: 1, 74 | overflow: TextOverflow.ellipsis, 75 | style: TextStyle( 76 | color: Colors.white, 77 | fontSize: 20, 78 | fontWeight: FontWeight.bold, 79 | ), 80 | ), 81 | ), 82 | IconButton( 83 | onPressed: _tapFavorite, 84 | color: Colors.white, 85 | icon: Icon( 86 | _isFavorite ? Icons.favorite : Icons.favorite_border, 87 | ), 88 | ), 89 | ], 90 | ), 91 | ), 92 | ), 93 | ), 94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /lib/ui/items/recipe_random.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:recipes/models/recipes/recipe.dart'; 5 | import 'package:recipes/services/hive.dart'; 6 | import 'package:recipes/ui/screens/recipe/recipe.dart'; 7 | 8 | class RecipeRandomItem extends StatefulWidget { 9 | final Recipe recipe; 10 | 11 | const RecipeRandomItem({Key key, @required this.recipe}) : super(key: key); 12 | 13 | @override 14 | _RecipeRandomItemState createState() => _RecipeRandomItemState(); 15 | } 16 | 17 | class _RecipeRandomItemState extends State { 18 | bool _isFavorite = false; 19 | 20 | void _tapFavorite() { 21 | setState(() { 22 | _isFavorite = !_isFavorite; 23 | }); 24 | 25 | if (_isFavorite) 26 | AppHive.instance.insert(widget.recipe); 27 | else 28 | AppHive.instance.delete(widget.recipe); 29 | } 30 | 31 | void _onTap() { 32 | Navigator.push( 33 | context, 34 | CupertinoPageRoute( 35 | builder: (_) => RecipeScreen( 36 | recipeId: widget.recipe.id, 37 | ), 38 | ), 39 | ); 40 | } 41 | 42 | @override 43 | void initState() { 44 | super.initState(); 45 | // check is favorite 46 | _isFavorite = AppHive.instance.isExist(widget.recipe.id); 47 | } 48 | 49 | @override 50 | Widget build(BuildContext context) => InkWell( 51 | onTap: _onTap, 52 | child: Stack( 53 | children: [ 54 | Positioned( 55 | top: 0, 56 | left: 0, 57 | right: 0, 58 | bottom: 15, 59 | child: ClipRRect( 60 | borderRadius: BorderRadius.circular(20), 61 | child: Stack( 62 | children: [ 63 | SizedBox.expand( 64 | child: CachedNetworkImage( 65 | imageUrl: widget.recipe.image, 66 | fit: BoxFit.cover, 67 | ), 68 | ), 69 | Container( 70 | color: Colors.black.withOpacity(.25), 71 | ), 72 | Positioned( 73 | top: 0, 74 | left: 0, 75 | child: Container( 76 | padding: const EdgeInsets.symmetric( 77 | horizontal: 25, 78 | vertical: 10, 79 | ), 80 | decoration: BoxDecoration( 81 | color: Theme.of(context).accentColor, 82 | borderRadius: BorderRadius.only( 83 | bottomRight: Radius.circular(20), 84 | ), 85 | ), 86 | child: Text( 87 | widget.recipe.category ?? '', 88 | style: TextStyle( 89 | color: Colors.white, 90 | ), 91 | ), 92 | ), 93 | ), 94 | Positioned( 95 | top: 0, 96 | right: 0, 97 | child: IconButton( 98 | onPressed: _tapFavorite, 99 | color: Colors.white, 100 | icon: Icon( 101 | _isFavorite ? Icons.favorite : Icons.favorite_border, 102 | ), 103 | ), 104 | ), 105 | ], 106 | ), 107 | ), 108 | ), 109 | Positioned( 110 | bottom: 0, 111 | right: 20, 112 | left: 20, 113 | child: Container( 114 | alignment: Alignment.center, 115 | padding: const EdgeInsets.symmetric( 116 | horizontal: 20, 117 | vertical: 10, 118 | ), 119 | decoration: BoxDecoration( 120 | color: Colors.white, 121 | borderRadius: BorderRadius.circular(20), 122 | ), 123 | child: Text( 124 | widget.recipe.title, 125 | maxLines: 1, 126 | overflow: TextOverflow.ellipsis, 127 | style: TextStyle( 128 | color: Colors.black, 129 | fontSize: 18, 130 | fontWeight: FontWeight.bold, 131 | ), 132 | ), 133 | ), 134 | ) 135 | ], 136 | ), 137 | ); 138 | } 139 | -------------------------------------------------------------------------------- /lib/ui/screens/category/bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | import '../../../../models/recipes/recipes.dart'; 6 | import '../../../../models/server_error.dart'; 7 | import '../../../../services/network.dart'; 8 | 9 | part 'bloc.freezed.dart'; 10 | 11 | part 'events.dart'; 12 | 13 | part 'states.dart'; 14 | 15 | class CategoryBloc extends Bloc { 16 | final _appNetwork = AppNetwork.instance; 17 | 18 | @override 19 | CategoryState get initialState => CategoryState.initial(); 20 | 21 | @override 22 | Stream mapEventToState(CategoryEvent event) async* { 23 | yield* event.when(getRecipes: _getRecipes); 24 | } 25 | 26 | Stream _getRecipes(String categoryName) async* { 27 | yield CategoryState.recipesLoading(); 28 | final response = await _appNetwork.get('filter.php?c=$categoryName'); 29 | yield* response.fold( 30 | (error) async* { 31 | yield CategoryState.recipesError(serverError: error); 32 | }, 33 | (json) async* { 34 | yield CategoryState.recipesLoaded(recipes: Recipes.fromJson(json)); 35 | }, 36 | ); 37 | } 38 | 39 | @override 40 | Future close() { 41 | return super.close(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/ui/screens/category/bloc/bloc.freezed.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // ignore_for_file: deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies 3 | 4 | part of 'bloc.dart'; 5 | 6 | // ************************************************************************** 7 | // FreezedGenerator 8 | // ************************************************************************** 9 | 10 | T _$identity(T value) => value; 11 | 12 | class _$CategoryEventTearOff { 13 | const _$CategoryEventTearOff(); 14 | 15 | // ignore: unused_element 16 | GetRecipes getRecipes({@required String categoryName}) { 17 | return GetRecipes( 18 | categoryName: categoryName, 19 | ); 20 | } 21 | } 22 | 23 | // ignore: unused_element 24 | const $CategoryEvent = _$CategoryEventTearOff(); 25 | 26 | mixin _$CategoryEvent { 27 | String get categoryName; 28 | 29 | @optionalTypeArgs 30 | Result when({ 31 | @required Result getRecipes(String categoryName), 32 | }); 33 | @optionalTypeArgs 34 | Result maybeWhen({ 35 | Result getRecipes(String categoryName), 36 | @required Result orElse(), 37 | }); 38 | @optionalTypeArgs 39 | Result map({ 40 | @required Result getRecipes(GetRecipes value), 41 | }); 42 | @optionalTypeArgs 43 | Result maybeMap({ 44 | Result getRecipes(GetRecipes value), 45 | @required Result orElse(), 46 | }); 47 | 48 | $CategoryEventCopyWith get copyWith; 49 | } 50 | 51 | abstract class $CategoryEventCopyWith<$Res> { 52 | factory $CategoryEventCopyWith( 53 | CategoryEvent value, $Res Function(CategoryEvent) then) = 54 | _$CategoryEventCopyWithImpl<$Res>; 55 | $Res call({String categoryName}); 56 | } 57 | 58 | class _$CategoryEventCopyWithImpl<$Res> 59 | implements $CategoryEventCopyWith<$Res> { 60 | _$CategoryEventCopyWithImpl(this._value, this._then); 61 | 62 | final CategoryEvent _value; 63 | // ignore: unused_field 64 | final $Res Function(CategoryEvent) _then; 65 | 66 | @override 67 | $Res call({ 68 | Object categoryName = freezed, 69 | }) { 70 | return _then(_value.copyWith( 71 | categoryName: categoryName == freezed 72 | ? _value.categoryName 73 | : categoryName as String, 74 | )); 75 | } 76 | } 77 | 78 | abstract class $GetRecipesCopyWith<$Res> 79 | implements $CategoryEventCopyWith<$Res> { 80 | factory $GetRecipesCopyWith( 81 | GetRecipes value, $Res Function(GetRecipes) then) = 82 | _$GetRecipesCopyWithImpl<$Res>; 83 | @override 84 | $Res call({String categoryName}); 85 | } 86 | 87 | class _$GetRecipesCopyWithImpl<$Res> extends _$CategoryEventCopyWithImpl<$Res> 88 | implements $GetRecipesCopyWith<$Res> { 89 | _$GetRecipesCopyWithImpl(GetRecipes _value, $Res Function(GetRecipes) _then) 90 | : super(_value, (v) => _then(v as GetRecipes)); 91 | 92 | @override 93 | GetRecipes get _value => super._value as GetRecipes; 94 | 95 | @override 96 | $Res call({ 97 | Object categoryName = freezed, 98 | }) { 99 | return _then(GetRecipes( 100 | categoryName: categoryName == freezed 101 | ? _value.categoryName 102 | : categoryName as String, 103 | )); 104 | } 105 | } 106 | 107 | class _$GetRecipes with DiagnosticableTreeMixin implements GetRecipes { 108 | const _$GetRecipes({@required this.categoryName}) 109 | : assert(categoryName != null); 110 | 111 | @override 112 | final String categoryName; 113 | 114 | @override 115 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 116 | return 'CategoryEvent.getRecipes(categoryName: $categoryName)'; 117 | } 118 | 119 | @override 120 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 121 | super.debugFillProperties(properties); 122 | properties 123 | ..add(DiagnosticsProperty('type', 'CategoryEvent.getRecipes')) 124 | ..add(DiagnosticsProperty('categoryName', categoryName)); 125 | } 126 | 127 | @override 128 | bool operator ==(dynamic other) { 129 | return identical(this, other) || 130 | (other is GetRecipes && 131 | (identical(other.categoryName, categoryName) || 132 | const DeepCollectionEquality() 133 | .equals(other.categoryName, categoryName))); 134 | } 135 | 136 | @override 137 | int get hashCode => 138 | runtimeType.hashCode ^ const DeepCollectionEquality().hash(categoryName); 139 | 140 | @override 141 | $GetRecipesCopyWith get copyWith => 142 | _$GetRecipesCopyWithImpl(this, _$identity); 143 | 144 | @override 145 | @optionalTypeArgs 146 | Result when({ 147 | @required Result getRecipes(String categoryName), 148 | }) { 149 | assert(getRecipes != null); 150 | return getRecipes(categoryName); 151 | } 152 | 153 | @override 154 | @optionalTypeArgs 155 | Result maybeWhen({ 156 | Result getRecipes(String categoryName), 157 | @required Result orElse(), 158 | }) { 159 | assert(orElse != null); 160 | if (getRecipes != null) { 161 | return getRecipes(categoryName); 162 | } 163 | return orElse(); 164 | } 165 | 166 | @override 167 | @optionalTypeArgs 168 | Result map({ 169 | @required Result getRecipes(GetRecipes value), 170 | }) { 171 | assert(getRecipes != null); 172 | return getRecipes(this); 173 | } 174 | 175 | @override 176 | @optionalTypeArgs 177 | Result maybeMap({ 178 | Result getRecipes(GetRecipes value), 179 | @required Result orElse(), 180 | }) { 181 | assert(orElse != null); 182 | if (getRecipes != null) { 183 | return getRecipes(this); 184 | } 185 | return orElse(); 186 | } 187 | } 188 | 189 | abstract class GetRecipes implements CategoryEvent { 190 | const factory GetRecipes({@required String categoryName}) = _$GetRecipes; 191 | 192 | @override 193 | String get categoryName; 194 | @override 195 | $GetRecipesCopyWith get copyWith; 196 | } 197 | 198 | class _$CategoryStateTearOff { 199 | const _$CategoryStateTearOff(); 200 | 201 | // ignore: unused_element 202 | Initial initial() { 203 | return const Initial(); 204 | } 205 | 206 | // ignore: unused_element 207 | RecipesLoading recipesLoading() { 208 | return const RecipesLoading(); 209 | } 210 | 211 | // ignore: unused_element 212 | RecipesLoaded recipesLoaded({@required Recipes recipes}) { 213 | return RecipesLoaded( 214 | recipes: recipes, 215 | ); 216 | } 217 | 218 | // ignore: unused_element 219 | RecipesError recipesError({@required ServerError serverError}) { 220 | return RecipesError( 221 | serverError: serverError, 222 | ); 223 | } 224 | } 225 | 226 | // ignore: unused_element 227 | const $CategoryState = _$CategoryStateTearOff(); 228 | 229 | mixin _$CategoryState { 230 | @optionalTypeArgs 231 | Result when({ 232 | @required Result initial(), 233 | @required Result recipesLoading(), 234 | @required Result recipesLoaded(Recipes recipes), 235 | @required Result recipesError(ServerError serverError), 236 | }); 237 | @optionalTypeArgs 238 | Result maybeWhen({ 239 | Result initial(), 240 | Result recipesLoading(), 241 | Result recipesLoaded(Recipes recipes), 242 | Result recipesError(ServerError serverError), 243 | @required Result orElse(), 244 | }); 245 | @optionalTypeArgs 246 | Result map({ 247 | @required Result initial(Initial value), 248 | @required Result recipesLoading(RecipesLoading value), 249 | @required Result recipesLoaded(RecipesLoaded value), 250 | @required Result recipesError(RecipesError value), 251 | }); 252 | @optionalTypeArgs 253 | Result maybeMap({ 254 | Result initial(Initial value), 255 | Result recipesLoading(RecipesLoading value), 256 | Result recipesLoaded(RecipesLoaded value), 257 | Result recipesError(RecipesError value), 258 | @required Result orElse(), 259 | }); 260 | } 261 | 262 | abstract class $CategoryStateCopyWith<$Res> { 263 | factory $CategoryStateCopyWith( 264 | CategoryState value, $Res Function(CategoryState) then) = 265 | _$CategoryStateCopyWithImpl<$Res>; 266 | } 267 | 268 | class _$CategoryStateCopyWithImpl<$Res> 269 | implements $CategoryStateCopyWith<$Res> { 270 | _$CategoryStateCopyWithImpl(this._value, this._then); 271 | 272 | final CategoryState _value; 273 | // ignore: unused_field 274 | final $Res Function(CategoryState) _then; 275 | } 276 | 277 | abstract class $InitialCopyWith<$Res> { 278 | factory $InitialCopyWith(Initial value, $Res Function(Initial) then) = 279 | _$InitialCopyWithImpl<$Res>; 280 | } 281 | 282 | class _$InitialCopyWithImpl<$Res> extends _$CategoryStateCopyWithImpl<$Res> 283 | implements $InitialCopyWith<$Res> { 284 | _$InitialCopyWithImpl(Initial _value, $Res Function(Initial) _then) 285 | : super(_value, (v) => _then(v as Initial)); 286 | 287 | @override 288 | Initial get _value => super._value as Initial; 289 | } 290 | 291 | class _$Initial with DiagnosticableTreeMixin implements Initial { 292 | const _$Initial(); 293 | 294 | @override 295 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 296 | return 'CategoryState.initial()'; 297 | } 298 | 299 | @override 300 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 301 | super.debugFillProperties(properties); 302 | properties..add(DiagnosticsProperty('type', 'CategoryState.initial')); 303 | } 304 | 305 | @override 306 | bool operator ==(dynamic other) { 307 | return identical(this, other) || (other is Initial); 308 | } 309 | 310 | @override 311 | int get hashCode => runtimeType.hashCode; 312 | 313 | @override 314 | @optionalTypeArgs 315 | Result when({ 316 | @required Result initial(), 317 | @required Result recipesLoading(), 318 | @required Result recipesLoaded(Recipes recipes), 319 | @required Result recipesError(ServerError serverError), 320 | }) { 321 | assert(initial != null); 322 | assert(recipesLoading != null); 323 | assert(recipesLoaded != null); 324 | assert(recipesError != null); 325 | return initial(); 326 | } 327 | 328 | @override 329 | @optionalTypeArgs 330 | Result maybeWhen({ 331 | Result initial(), 332 | Result recipesLoading(), 333 | Result recipesLoaded(Recipes recipes), 334 | Result recipesError(ServerError serverError), 335 | @required Result orElse(), 336 | }) { 337 | assert(orElse != null); 338 | if (initial != null) { 339 | return initial(); 340 | } 341 | return orElse(); 342 | } 343 | 344 | @override 345 | @optionalTypeArgs 346 | Result map({ 347 | @required Result initial(Initial value), 348 | @required Result recipesLoading(RecipesLoading value), 349 | @required Result recipesLoaded(RecipesLoaded value), 350 | @required Result recipesError(RecipesError value), 351 | }) { 352 | assert(initial != null); 353 | assert(recipesLoading != null); 354 | assert(recipesLoaded != null); 355 | assert(recipesError != null); 356 | return initial(this); 357 | } 358 | 359 | @override 360 | @optionalTypeArgs 361 | Result maybeMap({ 362 | Result initial(Initial value), 363 | Result recipesLoading(RecipesLoading value), 364 | Result recipesLoaded(RecipesLoaded value), 365 | Result recipesError(RecipesError value), 366 | @required Result orElse(), 367 | }) { 368 | assert(orElse != null); 369 | if (initial != null) { 370 | return initial(this); 371 | } 372 | return orElse(); 373 | } 374 | } 375 | 376 | abstract class Initial implements CategoryState { 377 | const factory Initial() = _$Initial; 378 | } 379 | 380 | abstract class $RecipesLoadingCopyWith<$Res> { 381 | factory $RecipesLoadingCopyWith( 382 | RecipesLoading value, $Res Function(RecipesLoading) then) = 383 | _$RecipesLoadingCopyWithImpl<$Res>; 384 | } 385 | 386 | class _$RecipesLoadingCopyWithImpl<$Res> 387 | extends _$CategoryStateCopyWithImpl<$Res> 388 | implements $RecipesLoadingCopyWith<$Res> { 389 | _$RecipesLoadingCopyWithImpl( 390 | RecipesLoading _value, $Res Function(RecipesLoading) _then) 391 | : super(_value, (v) => _then(v as RecipesLoading)); 392 | 393 | @override 394 | RecipesLoading get _value => super._value as RecipesLoading; 395 | } 396 | 397 | class _$RecipesLoading with DiagnosticableTreeMixin implements RecipesLoading { 398 | const _$RecipesLoading(); 399 | 400 | @override 401 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 402 | return 'CategoryState.recipesLoading()'; 403 | } 404 | 405 | @override 406 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 407 | super.debugFillProperties(properties); 408 | properties 409 | ..add(DiagnosticsProperty('type', 'CategoryState.recipesLoading')); 410 | } 411 | 412 | @override 413 | bool operator ==(dynamic other) { 414 | return identical(this, other) || (other is RecipesLoading); 415 | } 416 | 417 | @override 418 | int get hashCode => runtimeType.hashCode; 419 | 420 | @override 421 | @optionalTypeArgs 422 | Result when({ 423 | @required Result initial(), 424 | @required Result recipesLoading(), 425 | @required Result recipesLoaded(Recipes recipes), 426 | @required Result recipesError(ServerError serverError), 427 | }) { 428 | assert(initial != null); 429 | assert(recipesLoading != null); 430 | assert(recipesLoaded != null); 431 | assert(recipesError != null); 432 | return recipesLoading(); 433 | } 434 | 435 | @override 436 | @optionalTypeArgs 437 | Result maybeWhen({ 438 | Result initial(), 439 | Result recipesLoading(), 440 | Result recipesLoaded(Recipes recipes), 441 | Result recipesError(ServerError serverError), 442 | @required Result orElse(), 443 | }) { 444 | assert(orElse != null); 445 | if (recipesLoading != null) { 446 | return recipesLoading(); 447 | } 448 | return orElse(); 449 | } 450 | 451 | @override 452 | @optionalTypeArgs 453 | Result map({ 454 | @required Result initial(Initial value), 455 | @required Result recipesLoading(RecipesLoading value), 456 | @required Result recipesLoaded(RecipesLoaded value), 457 | @required Result recipesError(RecipesError value), 458 | }) { 459 | assert(initial != null); 460 | assert(recipesLoading != null); 461 | assert(recipesLoaded != null); 462 | assert(recipesError != null); 463 | return recipesLoading(this); 464 | } 465 | 466 | @override 467 | @optionalTypeArgs 468 | Result maybeMap({ 469 | Result initial(Initial value), 470 | Result recipesLoading(RecipesLoading value), 471 | Result recipesLoaded(RecipesLoaded value), 472 | Result recipesError(RecipesError value), 473 | @required Result orElse(), 474 | }) { 475 | assert(orElse != null); 476 | if (recipesLoading != null) { 477 | return recipesLoading(this); 478 | } 479 | return orElse(); 480 | } 481 | } 482 | 483 | abstract class RecipesLoading implements CategoryState { 484 | const factory RecipesLoading() = _$RecipesLoading; 485 | } 486 | 487 | abstract class $RecipesLoadedCopyWith<$Res> { 488 | factory $RecipesLoadedCopyWith( 489 | RecipesLoaded value, $Res Function(RecipesLoaded) then) = 490 | _$RecipesLoadedCopyWithImpl<$Res>; 491 | $Res call({Recipes recipes}); 492 | } 493 | 494 | class _$RecipesLoadedCopyWithImpl<$Res> 495 | extends _$CategoryStateCopyWithImpl<$Res> 496 | implements $RecipesLoadedCopyWith<$Res> { 497 | _$RecipesLoadedCopyWithImpl( 498 | RecipesLoaded _value, $Res Function(RecipesLoaded) _then) 499 | : super(_value, (v) => _then(v as RecipesLoaded)); 500 | 501 | @override 502 | RecipesLoaded get _value => super._value as RecipesLoaded; 503 | 504 | @override 505 | $Res call({ 506 | Object recipes = freezed, 507 | }) { 508 | return _then(RecipesLoaded( 509 | recipes: recipes == freezed ? _value.recipes : recipes as Recipes, 510 | )); 511 | } 512 | } 513 | 514 | class _$RecipesLoaded with DiagnosticableTreeMixin implements RecipesLoaded { 515 | const _$RecipesLoaded({@required this.recipes}) : assert(recipes != null); 516 | 517 | @override 518 | final Recipes recipes; 519 | 520 | @override 521 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 522 | return 'CategoryState.recipesLoaded(recipes: $recipes)'; 523 | } 524 | 525 | @override 526 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 527 | super.debugFillProperties(properties); 528 | properties 529 | ..add(DiagnosticsProperty('type', 'CategoryState.recipesLoaded')) 530 | ..add(DiagnosticsProperty('recipes', recipes)); 531 | } 532 | 533 | @override 534 | bool operator ==(dynamic other) { 535 | return identical(this, other) || 536 | (other is RecipesLoaded && 537 | (identical(other.recipes, recipes) || 538 | const DeepCollectionEquality().equals(other.recipes, recipes))); 539 | } 540 | 541 | @override 542 | int get hashCode => 543 | runtimeType.hashCode ^ const DeepCollectionEquality().hash(recipes); 544 | 545 | @override 546 | $RecipesLoadedCopyWith get copyWith => 547 | _$RecipesLoadedCopyWithImpl(this, _$identity); 548 | 549 | @override 550 | @optionalTypeArgs 551 | Result when({ 552 | @required Result initial(), 553 | @required Result recipesLoading(), 554 | @required Result recipesLoaded(Recipes recipes), 555 | @required Result recipesError(ServerError serverError), 556 | }) { 557 | assert(initial != null); 558 | assert(recipesLoading != null); 559 | assert(recipesLoaded != null); 560 | assert(recipesError != null); 561 | return recipesLoaded(recipes); 562 | } 563 | 564 | @override 565 | @optionalTypeArgs 566 | Result maybeWhen({ 567 | Result initial(), 568 | Result recipesLoading(), 569 | Result recipesLoaded(Recipes recipes), 570 | Result recipesError(ServerError serverError), 571 | @required Result orElse(), 572 | }) { 573 | assert(orElse != null); 574 | if (recipesLoaded != null) { 575 | return recipesLoaded(recipes); 576 | } 577 | return orElse(); 578 | } 579 | 580 | @override 581 | @optionalTypeArgs 582 | Result map({ 583 | @required Result initial(Initial value), 584 | @required Result recipesLoading(RecipesLoading value), 585 | @required Result recipesLoaded(RecipesLoaded value), 586 | @required Result recipesError(RecipesError value), 587 | }) { 588 | assert(initial != null); 589 | assert(recipesLoading != null); 590 | assert(recipesLoaded != null); 591 | assert(recipesError != null); 592 | return recipesLoaded(this); 593 | } 594 | 595 | @override 596 | @optionalTypeArgs 597 | Result maybeMap({ 598 | Result initial(Initial value), 599 | Result recipesLoading(RecipesLoading value), 600 | Result recipesLoaded(RecipesLoaded value), 601 | Result recipesError(RecipesError value), 602 | @required Result orElse(), 603 | }) { 604 | assert(orElse != null); 605 | if (recipesLoaded != null) { 606 | return recipesLoaded(this); 607 | } 608 | return orElse(); 609 | } 610 | } 611 | 612 | abstract class RecipesLoaded implements CategoryState { 613 | const factory RecipesLoaded({@required Recipes recipes}) = _$RecipesLoaded; 614 | 615 | Recipes get recipes; 616 | $RecipesLoadedCopyWith get copyWith; 617 | } 618 | 619 | abstract class $RecipesErrorCopyWith<$Res> { 620 | factory $RecipesErrorCopyWith( 621 | RecipesError value, $Res Function(RecipesError) then) = 622 | _$RecipesErrorCopyWithImpl<$Res>; 623 | $Res call({ServerError serverError}); 624 | } 625 | 626 | class _$RecipesErrorCopyWithImpl<$Res> extends _$CategoryStateCopyWithImpl<$Res> 627 | implements $RecipesErrorCopyWith<$Res> { 628 | _$RecipesErrorCopyWithImpl( 629 | RecipesError _value, $Res Function(RecipesError) _then) 630 | : super(_value, (v) => _then(v as RecipesError)); 631 | 632 | @override 633 | RecipesError get _value => super._value as RecipesError; 634 | 635 | @override 636 | $Res call({ 637 | Object serverError = freezed, 638 | }) { 639 | return _then(RecipesError( 640 | serverError: serverError == freezed 641 | ? _value.serverError 642 | : serverError as ServerError, 643 | )); 644 | } 645 | } 646 | 647 | class _$RecipesError with DiagnosticableTreeMixin implements RecipesError { 648 | const _$RecipesError({@required this.serverError}) 649 | : assert(serverError != null); 650 | 651 | @override 652 | final ServerError serverError; 653 | 654 | @override 655 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 656 | return 'CategoryState.recipesError(serverError: $serverError)'; 657 | } 658 | 659 | @override 660 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 661 | super.debugFillProperties(properties); 662 | properties 663 | ..add(DiagnosticsProperty('type', 'CategoryState.recipesError')) 664 | ..add(DiagnosticsProperty('serverError', serverError)); 665 | } 666 | 667 | @override 668 | bool operator ==(dynamic other) { 669 | return identical(this, other) || 670 | (other is RecipesError && 671 | (identical(other.serverError, serverError) || 672 | const DeepCollectionEquality() 673 | .equals(other.serverError, serverError))); 674 | } 675 | 676 | @override 677 | int get hashCode => 678 | runtimeType.hashCode ^ const DeepCollectionEquality().hash(serverError); 679 | 680 | @override 681 | $RecipesErrorCopyWith get copyWith => 682 | _$RecipesErrorCopyWithImpl(this, _$identity); 683 | 684 | @override 685 | @optionalTypeArgs 686 | Result when({ 687 | @required Result initial(), 688 | @required Result recipesLoading(), 689 | @required Result recipesLoaded(Recipes recipes), 690 | @required Result recipesError(ServerError serverError), 691 | }) { 692 | assert(initial != null); 693 | assert(recipesLoading != null); 694 | assert(recipesLoaded != null); 695 | assert(recipesError != null); 696 | return recipesError(serverError); 697 | } 698 | 699 | @override 700 | @optionalTypeArgs 701 | Result maybeWhen({ 702 | Result initial(), 703 | Result recipesLoading(), 704 | Result recipesLoaded(Recipes recipes), 705 | Result recipesError(ServerError serverError), 706 | @required Result orElse(), 707 | }) { 708 | assert(orElse != null); 709 | if (recipesError != null) { 710 | return recipesError(serverError); 711 | } 712 | return orElse(); 713 | } 714 | 715 | @override 716 | @optionalTypeArgs 717 | Result map({ 718 | @required Result initial(Initial value), 719 | @required Result recipesLoading(RecipesLoading value), 720 | @required Result recipesLoaded(RecipesLoaded value), 721 | @required Result recipesError(RecipesError value), 722 | }) { 723 | assert(initial != null); 724 | assert(recipesLoading != null); 725 | assert(recipesLoaded != null); 726 | assert(recipesError != null); 727 | return recipesError(this); 728 | } 729 | 730 | @override 731 | @optionalTypeArgs 732 | Result maybeMap({ 733 | Result initial(Initial value), 734 | Result recipesLoading(RecipesLoading value), 735 | Result recipesLoaded(RecipesLoaded value), 736 | Result recipesError(RecipesError value), 737 | @required Result orElse(), 738 | }) { 739 | assert(orElse != null); 740 | if (recipesError != null) { 741 | return recipesError(this); 742 | } 743 | return orElse(); 744 | } 745 | } 746 | 747 | abstract class RecipesError implements CategoryState { 748 | const factory RecipesError({@required ServerError serverError}) = 749 | _$RecipesError; 750 | 751 | ServerError get serverError; 752 | $RecipesErrorCopyWith get copyWith; 753 | } 754 | -------------------------------------------------------------------------------- /lib/ui/screens/category/bloc/events.dart: -------------------------------------------------------------------------------- 1 | part of 'bloc.dart'; 2 | 3 | @freezed 4 | abstract class CategoryEvent with _$CategoryEvent { 5 | const factory CategoryEvent.getRecipes({@required String categoryName}) = 6 | GetRecipes; 7 | } 8 | -------------------------------------------------------------------------------- /lib/ui/screens/category/bloc/states.dart: -------------------------------------------------------------------------------- 1 | part of 'bloc.dart'; 2 | 3 | @freezed 4 | abstract class CategoryState with _$CategoryState { 5 | const factory CategoryState.initial() = Initial; 6 | 7 | const factory CategoryState.recipesLoading() = RecipesLoading; 8 | 9 | const factory CategoryState.recipesLoaded({@required Recipes recipes}) = 10 | RecipesLoaded; 11 | 12 | const factory CategoryState.recipesError( 13 | {@required ServerError serverError}) = RecipesError; 14 | } 15 | -------------------------------------------------------------------------------- /lib/ui/screens/category/category.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | import '../../../ui/items/recipe.dart'; 5 | import 'bloc/bloc.dart'; 6 | 7 | class CategoryScreen extends StatefulWidget { 8 | final String categoryName; 9 | 10 | const CategoryScreen({Key key, @required this.categoryName}) 11 | : super(key: key); 12 | 13 | @override 14 | _CategoryScreenState createState() => _CategoryScreenState(); 15 | } 16 | 17 | class _CategoryScreenState extends State { 18 | final _bloc = CategoryBloc(); 19 | 20 | void _loadData() { 21 | _bloc..add(CategoryEvent.getRecipes(categoryName: widget.categoryName)); 22 | } 23 | 24 | Future _onRefresh() { 25 | _loadData(); 26 | return Future.value(); 27 | } 28 | 29 | @override 30 | void initState() { 31 | super.initState(); 32 | _loadData(); 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) => Scaffold( 37 | body: RefreshIndicator( 38 | onRefresh: _onRefresh, 39 | child: CustomScrollView( 40 | slivers: [ 41 | SliverAppBar( 42 | floating: true, 43 | centerTitle: true, 44 | title: Text(widget.categoryName), 45 | ), 46 | BlocBuilder( 47 | bloc: _bloc, 48 | builder: (_, state) => state.maybeWhen( 49 | recipesLoading: () => SliverFillRemaining( 50 | child: Center(child: CircularProgressIndicator()), 51 | ), 52 | recipesLoaded: (recipes) => SliverList( 53 | delegate: SliverChildBuilderDelegate( 54 | (_, index) => RecipeItem( 55 | recipe: recipes.items[index], 56 | ), 57 | childCount: recipes.items.length, 58 | ), 59 | ), 60 | orElse: () => SliverPadding(padding: const EdgeInsets.all(0)), 61 | ), 62 | ), 63 | ], 64 | ), 65 | ), 66 | ); 67 | 68 | @override 69 | void dispose() { 70 | _bloc.close(); 71 | super.dispose(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/ui/screens/favorite/favorite.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hive/hive.dart'; 3 | import 'package:hive_flutter/hive_flutter.dart'; 4 | import 'package:recipes/models/recipes/recipe.dart'; 5 | import 'package:recipes/services/hive.dart'; 6 | import 'package:recipes/ui/items/recipe.dart'; 7 | 8 | class FavoriteScreen extends StatefulWidget { 9 | @override 10 | _FavoriteScreenState createState() => _FavoriteScreenState(); 11 | } 12 | 13 | class _FavoriteScreenState extends State { 14 | @override 15 | Widget build(BuildContext context) => Scaffold( 16 | body: ValueListenableBuilder>( 17 | valueListenable: AppHive.instance.selectAll().listenable(), 18 | builder: (_, box, __) => CustomScrollView( 19 | slivers: [ 20 | SliverAppBar( 21 | floating: true, 22 | centerTitle: true, 23 | title: Text('Favorite'), 24 | ), 25 | SliverList( 26 | delegate: SliverChildBuilderDelegate( 27 | (_, index) => RecipeItem( 28 | key: ValueKey(box.getAt(index).id), 29 | recipe: box.getAt(index), 30 | ), 31 | childCount: box.length, 32 | ), 33 | ), 34 | ], 35 | ), 36 | ), 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /lib/ui/screens/home/bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | import '../../../../models/categories/categories.dart'; 6 | import '../../../../models/recipes/recipe.dart'; 7 | import '../../../../models/recipes/recipes.dart'; 8 | import '../../../../models/server_error.dart'; 9 | import '../../../../services/network.dart'; 10 | 11 | part 'bloc.freezed.dart'; 12 | 13 | part 'events.dart'; 14 | 15 | part 'states.dart'; 16 | 17 | class HomeBloc extends Bloc { 18 | final _appNetwork = AppNetwork.instance; 19 | 20 | @override 21 | HomeState get initialState => HomeState.initial(); 22 | 23 | @override 24 | Stream mapEventToState(HomeEvent event) async* { 25 | yield* event.when( 26 | getRandomRecipe: _getRandomRecipe, 27 | getCategories: _getCategories, 28 | ); 29 | } 30 | 31 | Stream _getRandomRecipe() async* { 32 | yield HomeState.randomRecipeLoading(); 33 | final response = await _appNetwork.get('random.php'); 34 | yield* response.fold( 35 | (error) async* { 36 | yield HomeState.randomRecipeError(serverError: error); 37 | }, 38 | (json) async* { 39 | yield HomeState.randomRecipeLoaded( 40 | recipe: Recipes.fromJson(json).items[0]); 41 | }, 42 | ); 43 | } 44 | 45 | Stream _getCategories() async* { 46 | yield HomeState.categoriesLoading(); 47 | final response = await _appNetwork.get('categories.php'); 48 | yield* response.fold( 49 | (error) async* { 50 | yield HomeState.categoriesError(serverError: error); 51 | }, 52 | (json) async* { 53 | yield HomeState.categoriesLoaded(categories: Categories.fromJson(json)); 54 | }, 55 | ); 56 | } 57 | 58 | @override 59 | Future close() { 60 | return super.close(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/ui/screens/home/bloc/events.dart: -------------------------------------------------------------------------------- 1 | part of 'bloc.dart'; 2 | 3 | @freezed 4 | abstract class HomeEvent with _$HomeEvent { 5 | const factory HomeEvent.getRandomRecipe() = GetRandomRecipe; 6 | 7 | const factory HomeEvent.getCategories() = GetCategories; 8 | } 9 | -------------------------------------------------------------------------------- /lib/ui/screens/home/bloc/states.dart: -------------------------------------------------------------------------------- 1 | part of 'bloc.dart'; 2 | 3 | @freezed 4 | abstract class HomeState with _$HomeState { 5 | const factory HomeState.initial() = Initial; 6 | 7 | const factory HomeState.randomRecipeLoading() = RandomRecipeLoading; 8 | 9 | const factory HomeState.randomRecipeLoaded({@required Recipe recipe}) = 10 | RandomRecipeLoaded; 11 | 12 | const factory HomeState.randomRecipeError( 13 | {@required ServerError serverError}) = RandomRecipeError; 14 | 15 | const factory HomeState.categoriesLoading() = CategoriesLoading; 16 | 17 | const factory HomeState.categoriesLoaded({@required Categories categories}) = 18 | CategoriesLoaded; 19 | 20 | const factory HomeState.categoriesError({@required ServerError serverError}) = 21 | CategoriesError; 22 | } 23 | -------------------------------------------------------------------------------- /lib/ui/screens/home/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:recipes/ui/screens/favorite/favorite.dart'; 5 | import 'package:recipes/ui/screens/search/search.dart'; 6 | 7 | import '../../../ui/items/category.dart'; 8 | import '../../../ui/items/recipe_random.dart'; 9 | import 'bloc/bloc.dart'; 10 | 11 | class HomeScreen extends StatefulWidget { 12 | @override 13 | _HomeScreenState createState() => _HomeScreenState(); 14 | } 15 | 16 | class _HomeScreenState extends State { 17 | final _bloc = HomeBloc(); 18 | 19 | void _loadData() { 20 | _bloc..add(HomeEvent.getRandomRecipe())..add(HomeEvent.getCategories()); 21 | } 22 | 23 | Future _onRefresh() { 24 | _loadData(); 25 | return Future.value(); 26 | } 27 | 28 | @override 29 | void initState() { 30 | super.initState(); 31 | _loadData(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) => Scaffold( 36 | body: RefreshIndicator( 37 | onRefresh: _onRefresh, 38 | child: CustomScrollView( 39 | slivers: [ 40 | SliverAppBar( 41 | floating: true, 42 | centerTitle: true, 43 | title: Text('Recipes'), 44 | actions: [ 45 | IconButton( 46 | onPressed: () { 47 | Navigator.push( 48 | context, 49 | CupertinoPageRoute(builder: (_) => FavoriteScreen()), 50 | ); 51 | }, 52 | icon: Icon(Icons.favorite_border), 53 | ), 54 | IconButton( 55 | onPressed: () { 56 | showSearch( 57 | context: context, 58 | delegate: SearchScreen(), 59 | ); 60 | }, 61 | icon: Icon(Icons.search), 62 | ), 63 | ], 64 | ), 65 | SliverToBoxAdapter( 66 | child: Padding( 67 | padding: const EdgeInsetsDirectional.only( 68 | start: 20, 69 | top: 20, 70 | bottom: 5, 71 | ), 72 | child: Text( 73 | 'RANDOM RECIPE', 74 | style: TextStyle( 75 | color: Colors.black, 76 | fontSize: 20, 77 | fontWeight: FontWeight.w500, 78 | ), 79 | ), 80 | ), 81 | ), 82 | SliverToBoxAdapter( 83 | child: BlocBuilder( 84 | bloc: _bloc, 85 | condition: (_, state) => state.maybeWhen( 86 | randomRecipeLoading: () => true, 87 | randomRecipeLoaded: (_) => true, 88 | randomRecipeError: (_) => true, 89 | orElse: () => false, 90 | ), 91 | builder: (_, state) => state.maybeWhen( 92 | randomRecipeLoading: () => Center( 93 | child: CircularProgressIndicator(), 94 | ), 95 | randomRecipeLoaded: (recipe) => Padding( 96 | padding: const EdgeInsets.symmetric(horizontal: 20), 97 | child: AspectRatio( 98 | aspectRatio: 1.3, 99 | child: RecipeRandomItem(recipe: recipe), 100 | ), 101 | ), 102 | orElse: () => const SizedBox(), 103 | ), 104 | ), 105 | ), 106 | SliverToBoxAdapter( 107 | child: Padding( 108 | padding: const EdgeInsetsDirectional.only( 109 | start: 20, 110 | top: 20, 111 | bottom: 5, 112 | ), 113 | child: Text( 114 | 'CATEGORIES', 115 | style: TextStyle( 116 | color: Colors.black, 117 | fontSize: 20, 118 | fontWeight: FontWeight.w500, 119 | ), 120 | ), 121 | ), 122 | ), 123 | BlocBuilder( 124 | bloc: _bloc, 125 | condition: (_, state) => state.maybeWhen( 126 | categoriesLoading: () => true, 127 | categoriesLoaded: (_) => true, 128 | categoriesError: (_) => true, 129 | orElse: () => false, 130 | ), 131 | builder: (_, state) => state.maybeWhen( 132 | categoriesLoading: () => SliverFillRemaining( 133 | child: Center(child: CircularProgressIndicator()), 134 | ), 135 | categoriesLoaded: (categories) => SliverList( 136 | delegate: SliverChildBuilderDelegate( 137 | (_, index) => CategoryItem( 138 | category: categories.items[index], 139 | ), 140 | childCount: categories.items.length, 141 | ), 142 | ), 143 | orElse: () => SliverPadding(padding: const EdgeInsets.all(0)), 144 | ), 145 | ), 146 | ], 147 | ), 148 | ), 149 | ); 150 | 151 | @override 152 | void dispose() { 153 | _bloc.close(); 154 | super.dispose(); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /lib/ui/screens/recipe/bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | import '../../../../models/recipes/recipe.dart'; 6 | import '../../../../models/recipes/recipes.dart'; 7 | import '../../../../models/server_error.dart'; 8 | import '../../../../services/network.dart'; 9 | 10 | part 'bloc.freezed.dart'; 11 | 12 | part 'events.dart'; 13 | 14 | part 'states.dart'; 15 | 16 | class RecipeBloc extends Bloc { 17 | final _appNetwork = AppNetwork.instance; 18 | 19 | @override 20 | RecipeState get initialState => RecipeState.initial(); 21 | 22 | @override 23 | Stream mapEventToState(RecipeEvent event) async* { 24 | yield* event.when(getDetails: _getDetails); 25 | } 26 | 27 | Stream _getDetails(dynamic recipeId) async* { 28 | yield RecipeState.detailsLoading(); 29 | final response = await _appNetwork.get('lookup.php?i=$recipeId'); 30 | yield* response.fold( 31 | (error) async* { 32 | yield RecipeState.detailsError(serverError: error); 33 | }, 34 | (json) async* { 35 | yield RecipeState.detailsLoaded( 36 | recipe: Recipes.fromJson(json).items[0], 37 | ); 38 | }, 39 | ); 40 | } 41 | 42 | @override 43 | Future close() { 44 | return super.close(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/ui/screens/recipe/bloc/bloc.freezed.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // ignore_for_file: deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies 3 | 4 | part of 'bloc.dart'; 5 | 6 | // ************************************************************************** 7 | // FreezedGenerator 8 | // ************************************************************************** 9 | 10 | T _$identity(T value) => value; 11 | 12 | class _$RecipeEventTearOff { 13 | const _$RecipeEventTearOff(); 14 | 15 | // ignore: unused_element 16 | GetDetails getDetails({@required dynamic recipeId}) { 17 | return GetDetails( 18 | recipeId: recipeId, 19 | ); 20 | } 21 | } 22 | 23 | // ignore: unused_element 24 | const $RecipeEvent = _$RecipeEventTearOff(); 25 | 26 | mixin _$RecipeEvent { 27 | dynamic get recipeId; 28 | 29 | @optionalTypeArgs 30 | Result when({ 31 | @required Result getDetails(dynamic recipeId), 32 | }); 33 | @optionalTypeArgs 34 | Result maybeWhen({ 35 | Result getDetails(dynamic recipeId), 36 | @required Result orElse(), 37 | }); 38 | @optionalTypeArgs 39 | Result map({ 40 | @required Result getDetails(GetDetails value), 41 | }); 42 | @optionalTypeArgs 43 | Result maybeMap({ 44 | Result getDetails(GetDetails value), 45 | @required Result orElse(), 46 | }); 47 | 48 | $RecipeEventCopyWith get copyWith; 49 | } 50 | 51 | abstract class $RecipeEventCopyWith<$Res> { 52 | factory $RecipeEventCopyWith( 53 | RecipeEvent value, $Res Function(RecipeEvent) then) = 54 | _$RecipeEventCopyWithImpl<$Res>; 55 | $Res call({dynamic recipeId}); 56 | } 57 | 58 | class _$RecipeEventCopyWithImpl<$Res> implements $RecipeEventCopyWith<$Res> { 59 | _$RecipeEventCopyWithImpl(this._value, this._then); 60 | 61 | final RecipeEvent _value; 62 | // ignore: unused_field 63 | final $Res Function(RecipeEvent) _then; 64 | 65 | @override 66 | $Res call({ 67 | Object recipeId = freezed, 68 | }) { 69 | return _then(_value.copyWith( 70 | recipeId: recipeId == freezed ? _value.recipeId : recipeId as dynamic, 71 | )); 72 | } 73 | } 74 | 75 | abstract class $GetDetailsCopyWith<$Res> implements $RecipeEventCopyWith<$Res> { 76 | factory $GetDetailsCopyWith( 77 | GetDetails value, $Res Function(GetDetails) then) = 78 | _$GetDetailsCopyWithImpl<$Res>; 79 | @override 80 | $Res call({dynamic recipeId}); 81 | } 82 | 83 | class _$GetDetailsCopyWithImpl<$Res> extends _$RecipeEventCopyWithImpl<$Res> 84 | implements $GetDetailsCopyWith<$Res> { 85 | _$GetDetailsCopyWithImpl(GetDetails _value, $Res Function(GetDetails) _then) 86 | : super(_value, (v) => _then(v as GetDetails)); 87 | 88 | @override 89 | GetDetails get _value => super._value as GetDetails; 90 | 91 | @override 92 | $Res call({ 93 | Object recipeId = freezed, 94 | }) { 95 | return _then(GetDetails( 96 | recipeId: recipeId == freezed ? _value.recipeId : recipeId as dynamic, 97 | )); 98 | } 99 | } 100 | 101 | class _$GetDetails with DiagnosticableTreeMixin implements GetDetails { 102 | const _$GetDetails({@required this.recipeId}) : assert(recipeId != null); 103 | 104 | @override 105 | final dynamic recipeId; 106 | 107 | @override 108 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 109 | return 'RecipeEvent.getDetails(recipeId: $recipeId)'; 110 | } 111 | 112 | @override 113 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 114 | super.debugFillProperties(properties); 115 | properties 116 | ..add(DiagnosticsProperty('type', 'RecipeEvent.getDetails')) 117 | ..add(DiagnosticsProperty('recipeId', recipeId)); 118 | } 119 | 120 | @override 121 | bool operator ==(dynamic other) { 122 | return identical(this, other) || 123 | (other is GetDetails && 124 | (identical(other.recipeId, recipeId) || 125 | const DeepCollectionEquality() 126 | .equals(other.recipeId, recipeId))); 127 | } 128 | 129 | @override 130 | int get hashCode => 131 | runtimeType.hashCode ^ const DeepCollectionEquality().hash(recipeId); 132 | 133 | @override 134 | $GetDetailsCopyWith get copyWith => 135 | _$GetDetailsCopyWithImpl(this, _$identity); 136 | 137 | @override 138 | @optionalTypeArgs 139 | Result when({ 140 | @required Result getDetails(dynamic recipeId), 141 | }) { 142 | assert(getDetails != null); 143 | return getDetails(recipeId); 144 | } 145 | 146 | @override 147 | @optionalTypeArgs 148 | Result maybeWhen({ 149 | Result getDetails(dynamic recipeId), 150 | @required Result orElse(), 151 | }) { 152 | assert(orElse != null); 153 | if (getDetails != null) { 154 | return getDetails(recipeId); 155 | } 156 | return orElse(); 157 | } 158 | 159 | @override 160 | @optionalTypeArgs 161 | Result map({ 162 | @required Result getDetails(GetDetails value), 163 | }) { 164 | assert(getDetails != null); 165 | return getDetails(this); 166 | } 167 | 168 | @override 169 | @optionalTypeArgs 170 | Result maybeMap({ 171 | Result getDetails(GetDetails value), 172 | @required Result orElse(), 173 | }) { 174 | assert(orElse != null); 175 | if (getDetails != null) { 176 | return getDetails(this); 177 | } 178 | return orElse(); 179 | } 180 | } 181 | 182 | abstract class GetDetails implements RecipeEvent { 183 | const factory GetDetails({@required dynamic recipeId}) = _$GetDetails; 184 | 185 | @override 186 | dynamic get recipeId; 187 | @override 188 | $GetDetailsCopyWith get copyWith; 189 | } 190 | 191 | class _$RecipeStateTearOff { 192 | const _$RecipeStateTearOff(); 193 | 194 | // ignore: unused_element 195 | Initial initial() { 196 | return const Initial(); 197 | } 198 | 199 | // ignore: unused_element 200 | RecipesLoading detailsLoading() { 201 | return const RecipesLoading(); 202 | } 203 | 204 | // ignore: unused_element 205 | RecipesLoaded detailsLoaded({@required Recipe recipe}) { 206 | return RecipesLoaded( 207 | recipe: recipe, 208 | ); 209 | } 210 | 211 | // ignore: unused_element 212 | RecipesError detailsError({@required ServerError serverError}) { 213 | return RecipesError( 214 | serverError: serverError, 215 | ); 216 | } 217 | } 218 | 219 | // ignore: unused_element 220 | const $RecipeState = _$RecipeStateTearOff(); 221 | 222 | mixin _$RecipeState { 223 | @optionalTypeArgs 224 | Result when({ 225 | @required Result initial(), 226 | @required Result detailsLoading(), 227 | @required Result detailsLoaded(Recipe recipe), 228 | @required Result detailsError(ServerError serverError), 229 | }); 230 | @optionalTypeArgs 231 | Result maybeWhen({ 232 | Result initial(), 233 | Result detailsLoading(), 234 | Result detailsLoaded(Recipe recipe), 235 | Result detailsError(ServerError serverError), 236 | @required Result orElse(), 237 | }); 238 | @optionalTypeArgs 239 | Result map({ 240 | @required Result initial(Initial value), 241 | @required Result detailsLoading(RecipesLoading value), 242 | @required Result detailsLoaded(RecipesLoaded value), 243 | @required Result detailsError(RecipesError value), 244 | }); 245 | @optionalTypeArgs 246 | Result maybeMap({ 247 | Result initial(Initial value), 248 | Result detailsLoading(RecipesLoading value), 249 | Result detailsLoaded(RecipesLoaded value), 250 | Result detailsError(RecipesError value), 251 | @required Result orElse(), 252 | }); 253 | } 254 | 255 | abstract class $RecipeStateCopyWith<$Res> { 256 | factory $RecipeStateCopyWith( 257 | RecipeState value, $Res Function(RecipeState) then) = 258 | _$RecipeStateCopyWithImpl<$Res>; 259 | } 260 | 261 | class _$RecipeStateCopyWithImpl<$Res> implements $RecipeStateCopyWith<$Res> { 262 | _$RecipeStateCopyWithImpl(this._value, this._then); 263 | 264 | final RecipeState _value; 265 | // ignore: unused_field 266 | final $Res Function(RecipeState) _then; 267 | } 268 | 269 | abstract class $InitialCopyWith<$Res> { 270 | factory $InitialCopyWith(Initial value, $Res Function(Initial) then) = 271 | _$InitialCopyWithImpl<$Res>; 272 | } 273 | 274 | class _$InitialCopyWithImpl<$Res> extends _$RecipeStateCopyWithImpl<$Res> 275 | implements $InitialCopyWith<$Res> { 276 | _$InitialCopyWithImpl(Initial _value, $Res Function(Initial) _then) 277 | : super(_value, (v) => _then(v as Initial)); 278 | 279 | @override 280 | Initial get _value => super._value as Initial; 281 | } 282 | 283 | class _$Initial with DiagnosticableTreeMixin implements Initial { 284 | const _$Initial(); 285 | 286 | @override 287 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 288 | return 'RecipeState.initial()'; 289 | } 290 | 291 | @override 292 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 293 | super.debugFillProperties(properties); 294 | properties..add(DiagnosticsProperty('type', 'RecipeState.initial')); 295 | } 296 | 297 | @override 298 | bool operator ==(dynamic other) { 299 | return identical(this, other) || (other is Initial); 300 | } 301 | 302 | @override 303 | int get hashCode => runtimeType.hashCode; 304 | 305 | @override 306 | @optionalTypeArgs 307 | Result when({ 308 | @required Result initial(), 309 | @required Result detailsLoading(), 310 | @required Result detailsLoaded(Recipe recipe), 311 | @required Result detailsError(ServerError serverError), 312 | }) { 313 | assert(initial != null); 314 | assert(detailsLoading != null); 315 | assert(detailsLoaded != null); 316 | assert(detailsError != null); 317 | return initial(); 318 | } 319 | 320 | @override 321 | @optionalTypeArgs 322 | Result maybeWhen({ 323 | Result initial(), 324 | Result detailsLoading(), 325 | Result detailsLoaded(Recipe recipe), 326 | Result detailsError(ServerError serverError), 327 | @required Result orElse(), 328 | }) { 329 | assert(orElse != null); 330 | if (initial != null) { 331 | return initial(); 332 | } 333 | return orElse(); 334 | } 335 | 336 | @override 337 | @optionalTypeArgs 338 | Result map({ 339 | @required Result initial(Initial value), 340 | @required Result detailsLoading(RecipesLoading value), 341 | @required Result detailsLoaded(RecipesLoaded value), 342 | @required Result detailsError(RecipesError value), 343 | }) { 344 | assert(initial != null); 345 | assert(detailsLoading != null); 346 | assert(detailsLoaded != null); 347 | assert(detailsError != null); 348 | return initial(this); 349 | } 350 | 351 | @override 352 | @optionalTypeArgs 353 | Result maybeMap({ 354 | Result initial(Initial value), 355 | Result detailsLoading(RecipesLoading value), 356 | Result detailsLoaded(RecipesLoaded value), 357 | Result detailsError(RecipesError value), 358 | @required Result orElse(), 359 | }) { 360 | assert(orElse != null); 361 | if (initial != null) { 362 | return initial(this); 363 | } 364 | return orElse(); 365 | } 366 | } 367 | 368 | abstract class Initial implements RecipeState { 369 | const factory Initial() = _$Initial; 370 | } 371 | 372 | abstract class $RecipesLoadingCopyWith<$Res> { 373 | factory $RecipesLoadingCopyWith( 374 | RecipesLoading value, $Res Function(RecipesLoading) then) = 375 | _$RecipesLoadingCopyWithImpl<$Res>; 376 | } 377 | 378 | class _$RecipesLoadingCopyWithImpl<$Res> extends _$RecipeStateCopyWithImpl<$Res> 379 | implements $RecipesLoadingCopyWith<$Res> { 380 | _$RecipesLoadingCopyWithImpl( 381 | RecipesLoading _value, $Res Function(RecipesLoading) _then) 382 | : super(_value, (v) => _then(v as RecipesLoading)); 383 | 384 | @override 385 | RecipesLoading get _value => super._value as RecipesLoading; 386 | } 387 | 388 | class _$RecipesLoading with DiagnosticableTreeMixin implements RecipesLoading { 389 | const _$RecipesLoading(); 390 | 391 | @override 392 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 393 | return 'RecipeState.detailsLoading()'; 394 | } 395 | 396 | @override 397 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 398 | super.debugFillProperties(properties); 399 | properties..add(DiagnosticsProperty('type', 'RecipeState.detailsLoading')); 400 | } 401 | 402 | @override 403 | bool operator ==(dynamic other) { 404 | return identical(this, other) || (other is RecipesLoading); 405 | } 406 | 407 | @override 408 | int get hashCode => runtimeType.hashCode; 409 | 410 | @override 411 | @optionalTypeArgs 412 | Result when({ 413 | @required Result initial(), 414 | @required Result detailsLoading(), 415 | @required Result detailsLoaded(Recipe recipe), 416 | @required Result detailsError(ServerError serverError), 417 | }) { 418 | assert(initial != null); 419 | assert(detailsLoading != null); 420 | assert(detailsLoaded != null); 421 | assert(detailsError != null); 422 | return detailsLoading(); 423 | } 424 | 425 | @override 426 | @optionalTypeArgs 427 | Result maybeWhen({ 428 | Result initial(), 429 | Result detailsLoading(), 430 | Result detailsLoaded(Recipe recipe), 431 | Result detailsError(ServerError serverError), 432 | @required Result orElse(), 433 | }) { 434 | assert(orElse != null); 435 | if (detailsLoading != null) { 436 | return detailsLoading(); 437 | } 438 | return orElse(); 439 | } 440 | 441 | @override 442 | @optionalTypeArgs 443 | Result map({ 444 | @required Result initial(Initial value), 445 | @required Result detailsLoading(RecipesLoading value), 446 | @required Result detailsLoaded(RecipesLoaded value), 447 | @required Result detailsError(RecipesError value), 448 | }) { 449 | assert(initial != null); 450 | assert(detailsLoading != null); 451 | assert(detailsLoaded != null); 452 | assert(detailsError != null); 453 | return detailsLoading(this); 454 | } 455 | 456 | @override 457 | @optionalTypeArgs 458 | Result maybeMap({ 459 | Result initial(Initial value), 460 | Result detailsLoading(RecipesLoading value), 461 | Result detailsLoaded(RecipesLoaded value), 462 | Result detailsError(RecipesError value), 463 | @required Result orElse(), 464 | }) { 465 | assert(orElse != null); 466 | if (detailsLoading != null) { 467 | return detailsLoading(this); 468 | } 469 | return orElse(); 470 | } 471 | } 472 | 473 | abstract class RecipesLoading implements RecipeState { 474 | const factory RecipesLoading() = _$RecipesLoading; 475 | } 476 | 477 | abstract class $RecipesLoadedCopyWith<$Res> { 478 | factory $RecipesLoadedCopyWith( 479 | RecipesLoaded value, $Res Function(RecipesLoaded) then) = 480 | _$RecipesLoadedCopyWithImpl<$Res>; 481 | $Res call({Recipe recipe}); 482 | } 483 | 484 | class _$RecipesLoadedCopyWithImpl<$Res> extends _$RecipeStateCopyWithImpl<$Res> 485 | implements $RecipesLoadedCopyWith<$Res> { 486 | _$RecipesLoadedCopyWithImpl( 487 | RecipesLoaded _value, $Res Function(RecipesLoaded) _then) 488 | : super(_value, (v) => _then(v as RecipesLoaded)); 489 | 490 | @override 491 | RecipesLoaded get _value => super._value as RecipesLoaded; 492 | 493 | @override 494 | $Res call({ 495 | Object recipe = freezed, 496 | }) { 497 | return _then(RecipesLoaded( 498 | recipe: recipe == freezed ? _value.recipe : recipe as Recipe, 499 | )); 500 | } 501 | } 502 | 503 | class _$RecipesLoaded with DiagnosticableTreeMixin implements RecipesLoaded { 504 | const _$RecipesLoaded({@required this.recipe}) : assert(recipe != null); 505 | 506 | @override 507 | final Recipe recipe; 508 | 509 | @override 510 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 511 | return 'RecipeState.detailsLoaded(recipe: $recipe)'; 512 | } 513 | 514 | @override 515 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 516 | super.debugFillProperties(properties); 517 | properties 518 | ..add(DiagnosticsProperty('type', 'RecipeState.detailsLoaded')) 519 | ..add(DiagnosticsProperty('recipe', recipe)); 520 | } 521 | 522 | @override 523 | bool operator ==(dynamic other) { 524 | return identical(this, other) || 525 | (other is RecipesLoaded && 526 | (identical(other.recipe, recipe) || 527 | const DeepCollectionEquality().equals(other.recipe, recipe))); 528 | } 529 | 530 | @override 531 | int get hashCode => 532 | runtimeType.hashCode ^ const DeepCollectionEquality().hash(recipe); 533 | 534 | @override 535 | $RecipesLoadedCopyWith get copyWith => 536 | _$RecipesLoadedCopyWithImpl(this, _$identity); 537 | 538 | @override 539 | @optionalTypeArgs 540 | Result when({ 541 | @required Result initial(), 542 | @required Result detailsLoading(), 543 | @required Result detailsLoaded(Recipe recipe), 544 | @required Result detailsError(ServerError serverError), 545 | }) { 546 | assert(initial != null); 547 | assert(detailsLoading != null); 548 | assert(detailsLoaded != null); 549 | assert(detailsError != null); 550 | return detailsLoaded(recipe); 551 | } 552 | 553 | @override 554 | @optionalTypeArgs 555 | Result maybeWhen({ 556 | Result initial(), 557 | Result detailsLoading(), 558 | Result detailsLoaded(Recipe recipe), 559 | Result detailsError(ServerError serverError), 560 | @required Result orElse(), 561 | }) { 562 | assert(orElse != null); 563 | if (detailsLoaded != null) { 564 | return detailsLoaded(recipe); 565 | } 566 | return orElse(); 567 | } 568 | 569 | @override 570 | @optionalTypeArgs 571 | Result map({ 572 | @required Result initial(Initial value), 573 | @required Result detailsLoading(RecipesLoading value), 574 | @required Result detailsLoaded(RecipesLoaded value), 575 | @required Result detailsError(RecipesError value), 576 | }) { 577 | assert(initial != null); 578 | assert(detailsLoading != null); 579 | assert(detailsLoaded != null); 580 | assert(detailsError != null); 581 | return detailsLoaded(this); 582 | } 583 | 584 | @override 585 | @optionalTypeArgs 586 | Result maybeMap({ 587 | Result initial(Initial value), 588 | Result detailsLoading(RecipesLoading value), 589 | Result detailsLoaded(RecipesLoaded value), 590 | Result detailsError(RecipesError value), 591 | @required Result orElse(), 592 | }) { 593 | assert(orElse != null); 594 | if (detailsLoaded != null) { 595 | return detailsLoaded(this); 596 | } 597 | return orElse(); 598 | } 599 | } 600 | 601 | abstract class RecipesLoaded implements RecipeState { 602 | const factory RecipesLoaded({@required Recipe recipe}) = _$RecipesLoaded; 603 | 604 | Recipe get recipe; 605 | $RecipesLoadedCopyWith get copyWith; 606 | } 607 | 608 | abstract class $RecipesErrorCopyWith<$Res> { 609 | factory $RecipesErrorCopyWith( 610 | RecipesError value, $Res Function(RecipesError) then) = 611 | _$RecipesErrorCopyWithImpl<$Res>; 612 | $Res call({ServerError serverError}); 613 | } 614 | 615 | class _$RecipesErrorCopyWithImpl<$Res> extends _$RecipeStateCopyWithImpl<$Res> 616 | implements $RecipesErrorCopyWith<$Res> { 617 | _$RecipesErrorCopyWithImpl( 618 | RecipesError _value, $Res Function(RecipesError) _then) 619 | : super(_value, (v) => _then(v as RecipesError)); 620 | 621 | @override 622 | RecipesError get _value => super._value as RecipesError; 623 | 624 | @override 625 | $Res call({ 626 | Object serverError = freezed, 627 | }) { 628 | return _then(RecipesError( 629 | serverError: serverError == freezed 630 | ? _value.serverError 631 | : serverError as ServerError, 632 | )); 633 | } 634 | } 635 | 636 | class _$RecipesError with DiagnosticableTreeMixin implements RecipesError { 637 | const _$RecipesError({@required this.serverError}) 638 | : assert(serverError != null); 639 | 640 | @override 641 | final ServerError serverError; 642 | 643 | @override 644 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 645 | return 'RecipeState.detailsError(serverError: $serverError)'; 646 | } 647 | 648 | @override 649 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 650 | super.debugFillProperties(properties); 651 | properties 652 | ..add(DiagnosticsProperty('type', 'RecipeState.detailsError')) 653 | ..add(DiagnosticsProperty('serverError', serverError)); 654 | } 655 | 656 | @override 657 | bool operator ==(dynamic other) { 658 | return identical(this, other) || 659 | (other is RecipesError && 660 | (identical(other.serverError, serverError) || 661 | const DeepCollectionEquality() 662 | .equals(other.serverError, serverError))); 663 | } 664 | 665 | @override 666 | int get hashCode => 667 | runtimeType.hashCode ^ const DeepCollectionEquality().hash(serverError); 668 | 669 | @override 670 | $RecipesErrorCopyWith get copyWith => 671 | _$RecipesErrorCopyWithImpl(this, _$identity); 672 | 673 | @override 674 | @optionalTypeArgs 675 | Result when({ 676 | @required Result initial(), 677 | @required Result detailsLoading(), 678 | @required Result detailsLoaded(Recipe recipe), 679 | @required Result detailsError(ServerError serverError), 680 | }) { 681 | assert(initial != null); 682 | assert(detailsLoading != null); 683 | assert(detailsLoaded != null); 684 | assert(detailsError != null); 685 | return detailsError(serverError); 686 | } 687 | 688 | @override 689 | @optionalTypeArgs 690 | Result maybeWhen({ 691 | Result initial(), 692 | Result detailsLoading(), 693 | Result detailsLoaded(Recipe recipe), 694 | Result detailsError(ServerError serverError), 695 | @required Result orElse(), 696 | }) { 697 | assert(orElse != null); 698 | if (detailsError != null) { 699 | return detailsError(serverError); 700 | } 701 | return orElse(); 702 | } 703 | 704 | @override 705 | @optionalTypeArgs 706 | Result map({ 707 | @required Result initial(Initial value), 708 | @required Result detailsLoading(RecipesLoading value), 709 | @required Result detailsLoaded(RecipesLoaded value), 710 | @required Result detailsError(RecipesError value), 711 | }) { 712 | assert(initial != null); 713 | assert(detailsLoading != null); 714 | assert(detailsLoaded != null); 715 | assert(detailsError != null); 716 | return detailsError(this); 717 | } 718 | 719 | @override 720 | @optionalTypeArgs 721 | Result maybeMap({ 722 | Result initial(Initial value), 723 | Result detailsLoading(RecipesLoading value), 724 | Result detailsLoaded(RecipesLoaded value), 725 | Result detailsError(RecipesError value), 726 | @required Result orElse(), 727 | }) { 728 | assert(orElse != null); 729 | if (detailsError != null) { 730 | return detailsError(this); 731 | } 732 | return orElse(); 733 | } 734 | } 735 | 736 | abstract class RecipesError implements RecipeState { 737 | const factory RecipesError({@required ServerError serverError}) = 738 | _$RecipesError; 739 | 740 | ServerError get serverError; 741 | $RecipesErrorCopyWith get copyWith; 742 | } 743 | -------------------------------------------------------------------------------- /lib/ui/screens/recipe/bloc/events.dart: -------------------------------------------------------------------------------- 1 | part of 'bloc.dart'; 2 | 3 | @freezed 4 | abstract class RecipeEvent with _$RecipeEvent { 5 | const factory RecipeEvent.getDetails({@required dynamic recipeId}) = GetDetails; 6 | } 7 | -------------------------------------------------------------------------------- /lib/ui/screens/recipe/bloc/states.dart: -------------------------------------------------------------------------------- 1 | part of 'bloc.dart'; 2 | 3 | @freezed 4 | abstract class RecipeState with _$RecipeState { 5 | const factory RecipeState.initial() = Initial; 6 | 7 | const factory RecipeState.detailsLoading() = RecipesLoading; 8 | 9 | const factory RecipeState.detailsLoaded({@required Recipe recipe}) = 10 | RecipesLoaded; 11 | 12 | const factory RecipeState.detailsError({@required ServerError serverError}) = 13 | RecipesError; 14 | } 15 | -------------------------------------------------------------------------------- /lib/ui/screens/recipe/recipe.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:recipes/services/hive.dart'; 6 | import 'package:recipes/ui/screens/category/category.dart'; 7 | 8 | import '../../../config/constants.dart'; 9 | import '../../../models/recipes/recipe.dart'; 10 | import '../../../ui/screens/video.dart'; 11 | import 'bloc/bloc.dart'; 12 | 13 | class RecipeScreen extends StatefulWidget { 14 | final recipeId; 15 | 16 | const RecipeScreen({Key key, @required this.recipeId}) : super(key: key); 17 | 18 | @override 19 | _RecipeScreenState createState() => _RecipeScreenState(); 20 | } 21 | 22 | class _RecipeScreenState extends State { 23 | final _bloc = RecipeBloc(); 24 | 25 | Recipe _recipe; 26 | 27 | bool _isFavorite = false; 28 | 29 | void _tapCategory(String categoryName) { 30 | Navigator.push( 31 | context, 32 | CupertinoPageRoute( 33 | builder: (_) => CategoryScreen( 34 | categoryName: categoryName, 35 | ), 36 | ), 37 | ); 38 | } 39 | 40 | void _tapFavorite() { 41 | setState(() { 42 | _isFavorite = !_isFavorite; 43 | }); 44 | if (_isFavorite) 45 | AppHive.instance.insert(_recipe); 46 | else 47 | AppHive.instance.delete(_recipe); 48 | } 49 | 50 | void _watchVideo(videoUrl) { 51 | Navigator.push( 52 | context, 53 | CupertinoPageRoute(builder: (_) => VideoScreen(videoUrl: videoUrl)), 54 | ); 55 | } 56 | 57 | @override 58 | void initState() { 59 | super.initState(); 60 | _isFavorite = AppHive.instance.isExist(widget.recipeId); 61 | 62 | _bloc 63 | ..add(RecipeEvent.getDetails(recipeId: widget.recipeId)) 64 | ..listen((state) { 65 | state.maybeWhen( 66 | detailsLoaded: (recipe) { 67 | _recipe = recipe; 68 | }, 69 | orElse: () {}, 70 | ); 71 | }); 72 | } 73 | 74 | @override 75 | Widget build(BuildContext context) => Scaffold( 76 | body: BlocBuilder( 77 | bloc: _bloc, 78 | builder: (_, state) => state.maybeWhen( 79 | detailsLoading: () => Center( 80 | child: CircularProgressIndicator(), 81 | ), 82 | detailsLoaded: (recipe) => Stack( 83 | children: [ 84 | CachedNetworkImage( 85 | imageUrl: recipe.image, 86 | fit: BoxFit.cover, 87 | ), 88 | CustomScrollView( 89 | slivers: [ 90 | SliverAppBar( 91 | elevation: 0, 92 | expandedHeight: 250, 93 | backgroundColor: Colors.transparent, 94 | leading: Align( 95 | child: InkWell( 96 | onTap: () { 97 | Navigator.pop(context); 98 | }, 99 | child: Container( 100 | padding: const EdgeInsets.all(10), 101 | decoration: BoxDecoration( 102 | shape: BoxShape.circle, 103 | color: Colors.white, 104 | ), 105 | child: Icon( 106 | Icons.arrow_back_ios, 107 | color: Theme.of(context).accentColor, 108 | ), 109 | ), 110 | ), 111 | ), 112 | actions: [ 113 | InkWell( 114 | onTap: _tapFavorite, 115 | child: Container( 116 | padding: const EdgeInsets.all(10), 117 | decoration: BoxDecoration( 118 | shape: BoxShape.circle, 119 | color: Colors.white, 120 | ), 121 | child: Icon( 122 | _isFavorite 123 | ? Icons.favorite 124 | : Icons.favorite_border, 125 | color: Theme.of(context).accentColor, 126 | ), 127 | ), 128 | ), 129 | const SizedBox(width: 10), 130 | ], 131 | ), 132 | SliverToBoxAdapter( 133 | child: Container( 134 | padding: const EdgeInsets.symmetric(horizontal: 20), 135 | decoration: BoxDecoration( 136 | color: Colors.white, 137 | borderRadius: BorderRadius.vertical( 138 | top: Radius.circular(20), 139 | ), 140 | ), 141 | child: Column( 142 | crossAxisAlignment: CrossAxisAlignment.start, 143 | children: [ 144 | const SizedBox(height: 20), 145 | Center( 146 | child: Container( 147 | width: 100, 148 | height: 5, 149 | decoration: BoxDecoration( 150 | color: Colors.grey[300], 151 | borderRadius: BorderRadius.circular(20), 152 | ), 153 | ), 154 | ), 155 | const SizedBox(height: 25), 156 | Row( 157 | children: [ 158 | Expanded( 159 | child: Text( 160 | recipe.title, 161 | style: TextStyle( 162 | color: Colors.black, 163 | fontSize: 25, 164 | fontWeight: FontWeight.bold, 165 | ), 166 | ), 167 | ), 168 | const SizedBox(width: 10), 169 | recipe.video?.isNotEmpty ?? false 170 | ? Container( 171 | decoration: BoxDecoration( 172 | shape: BoxShape.circle, 173 | color: Colors.grey[100], 174 | ), 175 | child: IconButton( 176 | onPressed: () { 177 | _watchVideo(recipe.video); 178 | }, 179 | icon: Icon(Icons.video_library), 180 | tooltip: 'watch video', 181 | ), 182 | ) 183 | : const SizedBox(), 184 | ], 185 | ), 186 | InkWell( 187 | onTap: () { 188 | _tapCategory(recipe.category); 189 | }, 190 | child: Text( 191 | recipe.category.toLowerCase(), 192 | style: TextStyle( 193 | color: Theme.of(context).accentColor, 194 | fontSize: 12.5, 195 | fontWeight: FontWeight.w900, 196 | ), 197 | ), 198 | ), 199 | const SizedBox(height: 25), 200 | Text( 201 | 'Ingredients', 202 | style: TextStyle( 203 | color: Colors.black, 204 | fontSize: 18, 205 | fontWeight: FontWeight.w600, 206 | ), 207 | ), 208 | ..._getIngredients(recipe), 209 | const SizedBox(height: 25), 210 | Text( 211 | 'Instructions', 212 | style: TextStyle( 213 | color: Colors.black, 214 | fontSize: 18, 215 | fontWeight: FontWeight.w600, 216 | ), 217 | ), 218 | const SizedBox(height: 5), 219 | Text( 220 | recipe.instructions, 221 | style: TextStyle( 222 | color: Colors.grey[800], 223 | wordSpacing: 2.5, 224 | height: 1.5, 225 | ), 226 | ), 227 | const SizedBox(height: 25), 228 | ], 229 | ), 230 | ), 231 | ), 232 | ], 233 | ), 234 | ], 235 | ), 236 | orElse: () => const SizedBox(), 237 | ), 238 | ), 239 | ); 240 | 241 | List _getIngredients(Recipe recipe) => List.generate( 242 | 20, 243 | (index) { 244 | String ingredient = recipe.toJson()['strIngredient$index']; 245 | String measure = recipe.toJson()['strMeasure$index']; 246 | return (ingredient != null && ingredient.isNotEmpty) 247 | ? ListTile( 248 | title: Text('$measure $ingredient'), 249 | leading: Container( 250 | height: 40, 251 | width: 40, 252 | padding: const EdgeInsets.all(5), 253 | decoration: BoxDecoration( 254 | color: Colors.grey[100], 255 | shape: BoxShape.circle, 256 | ), 257 | child: CachedNetworkImage( 258 | imageUrl: 259 | '${AppConstants.HOST}images/ingredients/$ingredient-Small.png', 260 | fit: BoxFit.cover, 261 | ), 262 | ), 263 | ) 264 | : const SizedBox(); 265 | }, 266 | ).toList(); 267 | 268 | @override 269 | void dispose() { 270 | _bloc.close(); 271 | super.dispose(); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /lib/ui/screens/search/bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | import '../../../../models/recipes/recipes.dart'; 6 | import '../../../../models/server_error.dart'; 7 | import '../../../../services/network.dart'; 8 | 9 | part 'bloc.freezed.dart'; 10 | 11 | part 'events.dart'; 12 | 13 | part 'states.dart'; 14 | 15 | class SearchBloc extends Bloc { 16 | final _appNetwork = AppNetwork.instance; 17 | 18 | @override 19 | SearchState get initialState => SearchState.initial(); 20 | 21 | @override 22 | Stream mapEventToState(SearchEvent event) async* { 23 | yield* event.when(searchRecipes: _searchRecipes); 24 | } 25 | 26 | Stream _searchRecipes(String query) async* { 27 | yield SearchState.loading(); 28 | final response = await _appNetwork.get('search.php?s=$query'); 29 | yield* response.fold( 30 | (error) async* { 31 | yield SearchState.error(serverError: error); 32 | }, 33 | (json) async* { 34 | final recipes = Recipes.fromJson(json).items ?? []; 35 | if (recipes.isEmpty) 36 | yield SearchState.error( 37 | serverError: ServerError(message: "Empty recipes!")); 38 | else 39 | yield SearchState.loaded(recipes: Recipes.fromJson(json)); 40 | }, 41 | ); 42 | } 43 | 44 | @override 45 | Future close() { 46 | return super.close(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/ui/screens/search/bloc/bloc.freezed.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // ignore_for_file: deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies 3 | 4 | part of 'bloc.dart'; 5 | 6 | // ************************************************************************** 7 | // FreezedGenerator 8 | // ************************************************************************** 9 | 10 | T _$identity(T value) => value; 11 | 12 | class _$SearchEventTearOff { 13 | const _$SearchEventTearOff(); 14 | 15 | // ignore: unused_element 16 | SearchRecipes searchRecipes({@required String query}) { 17 | return SearchRecipes( 18 | query: query, 19 | ); 20 | } 21 | } 22 | 23 | // ignore: unused_element 24 | const $SearchEvent = _$SearchEventTearOff(); 25 | 26 | mixin _$SearchEvent { 27 | String get query; 28 | 29 | @optionalTypeArgs 30 | Result when({ 31 | @required Result searchRecipes(String query), 32 | }); 33 | @optionalTypeArgs 34 | Result maybeWhen({ 35 | Result searchRecipes(String query), 36 | @required Result orElse(), 37 | }); 38 | @optionalTypeArgs 39 | Result map({ 40 | @required Result searchRecipes(SearchRecipes value), 41 | }); 42 | @optionalTypeArgs 43 | Result maybeMap({ 44 | Result searchRecipes(SearchRecipes value), 45 | @required Result orElse(), 46 | }); 47 | 48 | $SearchEventCopyWith get copyWith; 49 | } 50 | 51 | abstract class $SearchEventCopyWith<$Res> { 52 | factory $SearchEventCopyWith( 53 | SearchEvent value, $Res Function(SearchEvent) then) = 54 | _$SearchEventCopyWithImpl<$Res>; 55 | $Res call({String query}); 56 | } 57 | 58 | class _$SearchEventCopyWithImpl<$Res> implements $SearchEventCopyWith<$Res> { 59 | _$SearchEventCopyWithImpl(this._value, this._then); 60 | 61 | final SearchEvent _value; 62 | // ignore: unused_field 63 | final $Res Function(SearchEvent) _then; 64 | 65 | @override 66 | $Res call({ 67 | Object query = freezed, 68 | }) { 69 | return _then(_value.copyWith( 70 | query: query == freezed ? _value.query : query as String, 71 | )); 72 | } 73 | } 74 | 75 | abstract class $SearchRecipesCopyWith<$Res> 76 | implements $SearchEventCopyWith<$Res> { 77 | factory $SearchRecipesCopyWith( 78 | SearchRecipes value, $Res Function(SearchRecipes) then) = 79 | _$SearchRecipesCopyWithImpl<$Res>; 80 | @override 81 | $Res call({String query}); 82 | } 83 | 84 | class _$SearchRecipesCopyWithImpl<$Res> extends _$SearchEventCopyWithImpl<$Res> 85 | implements $SearchRecipesCopyWith<$Res> { 86 | _$SearchRecipesCopyWithImpl( 87 | SearchRecipes _value, $Res Function(SearchRecipes) _then) 88 | : super(_value, (v) => _then(v as SearchRecipes)); 89 | 90 | @override 91 | SearchRecipes get _value => super._value as SearchRecipes; 92 | 93 | @override 94 | $Res call({ 95 | Object query = freezed, 96 | }) { 97 | return _then(SearchRecipes( 98 | query: query == freezed ? _value.query : query as String, 99 | )); 100 | } 101 | } 102 | 103 | class _$SearchRecipes with DiagnosticableTreeMixin implements SearchRecipes { 104 | const _$SearchRecipes({@required this.query}) : assert(query != null); 105 | 106 | @override 107 | final String query; 108 | 109 | @override 110 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 111 | return 'SearchEvent.searchRecipes(query: $query)'; 112 | } 113 | 114 | @override 115 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 116 | super.debugFillProperties(properties); 117 | properties 118 | ..add(DiagnosticsProperty('type', 'SearchEvent.searchRecipes')) 119 | ..add(DiagnosticsProperty('query', query)); 120 | } 121 | 122 | @override 123 | bool operator ==(dynamic other) { 124 | return identical(this, other) || 125 | (other is SearchRecipes && 126 | (identical(other.query, query) || 127 | const DeepCollectionEquality().equals(other.query, query))); 128 | } 129 | 130 | @override 131 | int get hashCode => 132 | runtimeType.hashCode ^ const DeepCollectionEquality().hash(query); 133 | 134 | @override 135 | $SearchRecipesCopyWith get copyWith => 136 | _$SearchRecipesCopyWithImpl(this, _$identity); 137 | 138 | @override 139 | @optionalTypeArgs 140 | Result when({ 141 | @required Result searchRecipes(String query), 142 | }) { 143 | assert(searchRecipes != null); 144 | return searchRecipes(query); 145 | } 146 | 147 | @override 148 | @optionalTypeArgs 149 | Result maybeWhen({ 150 | Result searchRecipes(String query), 151 | @required Result orElse(), 152 | }) { 153 | assert(orElse != null); 154 | if (searchRecipes != null) { 155 | return searchRecipes(query); 156 | } 157 | return orElse(); 158 | } 159 | 160 | @override 161 | @optionalTypeArgs 162 | Result map({ 163 | @required Result searchRecipes(SearchRecipes value), 164 | }) { 165 | assert(searchRecipes != null); 166 | return searchRecipes(this); 167 | } 168 | 169 | @override 170 | @optionalTypeArgs 171 | Result maybeMap({ 172 | Result searchRecipes(SearchRecipes value), 173 | @required Result orElse(), 174 | }) { 175 | assert(orElse != null); 176 | if (searchRecipes != null) { 177 | return searchRecipes(this); 178 | } 179 | return orElse(); 180 | } 181 | } 182 | 183 | abstract class SearchRecipes implements SearchEvent { 184 | const factory SearchRecipes({@required String query}) = _$SearchRecipes; 185 | 186 | @override 187 | String get query; 188 | @override 189 | $SearchRecipesCopyWith get copyWith; 190 | } 191 | 192 | class _$SearchStateTearOff { 193 | const _$SearchStateTearOff(); 194 | 195 | // ignore: unused_element 196 | Initial initial() { 197 | return const Initial(); 198 | } 199 | 200 | // ignore: unused_element 201 | Loading loading() { 202 | return const Loading(); 203 | } 204 | 205 | // ignore: unused_element 206 | Loaded loaded({@required Recipes recipes}) { 207 | return Loaded( 208 | recipes: recipes, 209 | ); 210 | } 211 | 212 | // ignore: unused_element 213 | Error error({@required ServerError serverError}) { 214 | return Error( 215 | serverError: serverError, 216 | ); 217 | } 218 | } 219 | 220 | // ignore: unused_element 221 | const $SearchState = _$SearchStateTearOff(); 222 | 223 | mixin _$SearchState { 224 | @optionalTypeArgs 225 | Result when({ 226 | @required Result initial(), 227 | @required Result loading(), 228 | @required Result loaded(Recipes recipes), 229 | @required Result error(ServerError serverError), 230 | }); 231 | @optionalTypeArgs 232 | Result maybeWhen({ 233 | Result initial(), 234 | Result loading(), 235 | Result loaded(Recipes recipes), 236 | Result error(ServerError serverError), 237 | @required Result orElse(), 238 | }); 239 | @optionalTypeArgs 240 | Result map({ 241 | @required Result initial(Initial value), 242 | @required Result loading(Loading value), 243 | @required Result loaded(Loaded value), 244 | @required Result error(Error value), 245 | }); 246 | @optionalTypeArgs 247 | Result maybeMap({ 248 | Result initial(Initial value), 249 | Result loading(Loading value), 250 | Result loaded(Loaded value), 251 | Result error(Error value), 252 | @required Result orElse(), 253 | }); 254 | } 255 | 256 | abstract class $SearchStateCopyWith<$Res> { 257 | factory $SearchStateCopyWith( 258 | SearchState value, $Res Function(SearchState) then) = 259 | _$SearchStateCopyWithImpl<$Res>; 260 | } 261 | 262 | class _$SearchStateCopyWithImpl<$Res> implements $SearchStateCopyWith<$Res> { 263 | _$SearchStateCopyWithImpl(this._value, this._then); 264 | 265 | final SearchState _value; 266 | // ignore: unused_field 267 | final $Res Function(SearchState) _then; 268 | } 269 | 270 | abstract class $InitialCopyWith<$Res> { 271 | factory $InitialCopyWith(Initial value, $Res Function(Initial) then) = 272 | _$InitialCopyWithImpl<$Res>; 273 | } 274 | 275 | class _$InitialCopyWithImpl<$Res> extends _$SearchStateCopyWithImpl<$Res> 276 | implements $InitialCopyWith<$Res> { 277 | _$InitialCopyWithImpl(Initial _value, $Res Function(Initial) _then) 278 | : super(_value, (v) => _then(v as Initial)); 279 | 280 | @override 281 | Initial get _value => super._value as Initial; 282 | } 283 | 284 | class _$Initial with DiagnosticableTreeMixin implements Initial { 285 | const _$Initial(); 286 | 287 | @override 288 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 289 | return 'SearchState.initial()'; 290 | } 291 | 292 | @override 293 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 294 | super.debugFillProperties(properties); 295 | properties..add(DiagnosticsProperty('type', 'SearchState.initial')); 296 | } 297 | 298 | @override 299 | bool operator ==(dynamic other) { 300 | return identical(this, other) || (other is Initial); 301 | } 302 | 303 | @override 304 | int get hashCode => runtimeType.hashCode; 305 | 306 | @override 307 | @optionalTypeArgs 308 | Result when({ 309 | @required Result initial(), 310 | @required Result loading(), 311 | @required Result loaded(Recipes recipes), 312 | @required Result error(ServerError serverError), 313 | }) { 314 | assert(initial != null); 315 | assert(loading != null); 316 | assert(loaded != null); 317 | assert(error != null); 318 | return initial(); 319 | } 320 | 321 | @override 322 | @optionalTypeArgs 323 | Result maybeWhen({ 324 | Result initial(), 325 | Result loading(), 326 | Result loaded(Recipes recipes), 327 | Result error(ServerError serverError), 328 | @required Result orElse(), 329 | }) { 330 | assert(orElse != null); 331 | if (initial != null) { 332 | return initial(); 333 | } 334 | return orElse(); 335 | } 336 | 337 | @override 338 | @optionalTypeArgs 339 | Result map({ 340 | @required Result initial(Initial value), 341 | @required Result loading(Loading value), 342 | @required Result loaded(Loaded value), 343 | @required Result error(Error value), 344 | }) { 345 | assert(initial != null); 346 | assert(loading != null); 347 | assert(loaded != null); 348 | assert(error != null); 349 | return initial(this); 350 | } 351 | 352 | @override 353 | @optionalTypeArgs 354 | Result maybeMap({ 355 | Result initial(Initial value), 356 | Result loading(Loading value), 357 | Result loaded(Loaded value), 358 | Result error(Error value), 359 | @required Result orElse(), 360 | }) { 361 | assert(orElse != null); 362 | if (initial != null) { 363 | return initial(this); 364 | } 365 | return orElse(); 366 | } 367 | } 368 | 369 | abstract class Initial implements SearchState { 370 | const factory Initial() = _$Initial; 371 | } 372 | 373 | abstract class $LoadingCopyWith<$Res> { 374 | factory $LoadingCopyWith(Loading value, $Res Function(Loading) then) = 375 | _$LoadingCopyWithImpl<$Res>; 376 | } 377 | 378 | class _$LoadingCopyWithImpl<$Res> extends _$SearchStateCopyWithImpl<$Res> 379 | implements $LoadingCopyWith<$Res> { 380 | _$LoadingCopyWithImpl(Loading _value, $Res Function(Loading) _then) 381 | : super(_value, (v) => _then(v as Loading)); 382 | 383 | @override 384 | Loading get _value => super._value as Loading; 385 | } 386 | 387 | class _$Loading with DiagnosticableTreeMixin implements Loading { 388 | const _$Loading(); 389 | 390 | @override 391 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 392 | return 'SearchState.loading()'; 393 | } 394 | 395 | @override 396 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 397 | super.debugFillProperties(properties); 398 | properties..add(DiagnosticsProperty('type', 'SearchState.loading')); 399 | } 400 | 401 | @override 402 | bool operator ==(dynamic other) { 403 | return identical(this, other) || (other is Loading); 404 | } 405 | 406 | @override 407 | int get hashCode => runtimeType.hashCode; 408 | 409 | @override 410 | @optionalTypeArgs 411 | Result when({ 412 | @required Result initial(), 413 | @required Result loading(), 414 | @required Result loaded(Recipes recipes), 415 | @required Result error(ServerError serverError), 416 | }) { 417 | assert(initial != null); 418 | assert(loading != null); 419 | assert(loaded != null); 420 | assert(error != null); 421 | return loading(); 422 | } 423 | 424 | @override 425 | @optionalTypeArgs 426 | Result maybeWhen({ 427 | Result initial(), 428 | Result loading(), 429 | Result loaded(Recipes recipes), 430 | Result error(ServerError serverError), 431 | @required Result orElse(), 432 | }) { 433 | assert(orElse != null); 434 | if (loading != null) { 435 | return loading(); 436 | } 437 | return orElse(); 438 | } 439 | 440 | @override 441 | @optionalTypeArgs 442 | Result map({ 443 | @required Result initial(Initial value), 444 | @required Result loading(Loading value), 445 | @required Result loaded(Loaded value), 446 | @required Result error(Error value), 447 | }) { 448 | assert(initial != null); 449 | assert(loading != null); 450 | assert(loaded != null); 451 | assert(error != null); 452 | return loading(this); 453 | } 454 | 455 | @override 456 | @optionalTypeArgs 457 | Result maybeMap({ 458 | Result initial(Initial value), 459 | Result loading(Loading value), 460 | Result loaded(Loaded value), 461 | Result error(Error value), 462 | @required Result orElse(), 463 | }) { 464 | assert(orElse != null); 465 | if (loading != null) { 466 | return loading(this); 467 | } 468 | return orElse(); 469 | } 470 | } 471 | 472 | abstract class Loading implements SearchState { 473 | const factory Loading() = _$Loading; 474 | } 475 | 476 | abstract class $LoadedCopyWith<$Res> { 477 | factory $LoadedCopyWith(Loaded value, $Res Function(Loaded) then) = 478 | _$LoadedCopyWithImpl<$Res>; 479 | $Res call({Recipes recipes}); 480 | } 481 | 482 | class _$LoadedCopyWithImpl<$Res> extends _$SearchStateCopyWithImpl<$Res> 483 | implements $LoadedCopyWith<$Res> { 484 | _$LoadedCopyWithImpl(Loaded _value, $Res Function(Loaded) _then) 485 | : super(_value, (v) => _then(v as Loaded)); 486 | 487 | @override 488 | Loaded get _value => super._value as Loaded; 489 | 490 | @override 491 | $Res call({ 492 | Object recipes = freezed, 493 | }) { 494 | return _then(Loaded( 495 | recipes: recipes == freezed ? _value.recipes : recipes as Recipes, 496 | )); 497 | } 498 | } 499 | 500 | class _$Loaded with DiagnosticableTreeMixin implements Loaded { 501 | const _$Loaded({@required this.recipes}) : assert(recipes != null); 502 | 503 | @override 504 | final Recipes recipes; 505 | 506 | @override 507 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 508 | return 'SearchState.loaded(recipes: $recipes)'; 509 | } 510 | 511 | @override 512 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 513 | super.debugFillProperties(properties); 514 | properties 515 | ..add(DiagnosticsProperty('type', 'SearchState.loaded')) 516 | ..add(DiagnosticsProperty('recipes', recipes)); 517 | } 518 | 519 | @override 520 | bool operator ==(dynamic other) { 521 | return identical(this, other) || 522 | (other is Loaded && 523 | (identical(other.recipes, recipes) || 524 | const DeepCollectionEquality().equals(other.recipes, recipes))); 525 | } 526 | 527 | @override 528 | int get hashCode => 529 | runtimeType.hashCode ^ const DeepCollectionEquality().hash(recipes); 530 | 531 | @override 532 | $LoadedCopyWith get copyWith => 533 | _$LoadedCopyWithImpl(this, _$identity); 534 | 535 | @override 536 | @optionalTypeArgs 537 | Result when({ 538 | @required Result initial(), 539 | @required Result loading(), 540 | @required Result loaded(Recipes recipes), 541 | @required Result error(ServerError serverError), 542 | }) { 543 | assert(initial != null); 544 | assert(loading != null); 545 | assert(loaded != null); 546 | assert(error != null); 547 | return loaded(recipes); 548 | } 549 | 550 | @override 551 | @optionalTypeArgs 552 | Result maybeWhen({ 553 | Result initial(), 554 | Result loading(), 555 | Result loaded(Recipes recipes), 556 | Result error(ServerError serverError), 557 | @required Result orElse(), 558 | }) { 559 | assert(orElse != null); 560 | if (loaded != null) { 561 | return loaded(recipes); 562 | } 563 | return orElse(); 564 | } 565 | 566 | @override 567 | @optionalTypeArgs 568 | Result map({ 569 | @required Result initial(Initial value), 570 | @required Result loading(Loading value), 571 | @required Result loaded(Loaded value), 572 | @required Result error(Error value), 573 | }) { 574 | assert(initial != null); 575 | assert(loading != null); 576 | assert(loaded != null); 577 | assert(error != null); 578 | return loaded(this); 579 | } 580 | 581 | @override 582 | @optionalTypeArgs 583 | Result maybeMap({ 584 | Result initial(Initial value), 585 | Result loading(Loading value), 586 | Result loaded(Loaded value), 587 | Result error(Error value), 588 | @required Result orElse(), 589 | }) { 590 | assert(orElse != null); 591 | if (loaded != null) { 592 | return loaded(this); 593 | } 594 | return orElse(); 595 | } 596 | } 597 | 598 | abstract class Loaded implements SearchState { 599 | const factory Loaded({@required Recipes recipes}) = _$Loaded; 600 | 601 | Recipes get recipes; 602 | $LoadedCopyWith get copyWith; 603 | } 604 | 605 | abstract class $ErrorCopyWith<$Res> { 606 | factory $ErrorCopyWith(Error value, $Res Function(Error) then) = 607 | _$ErrorCopyWithImpl<$Res>; 608 | $Res call({ServerError serverError}); 609 | } 610 | 611 | class _$ErrorCopyWithImpl<$Res> extends _$SearchStateCopyWithImpl<$Res> 612 | implements $ErrorCopyWith<$Res> { 613 | _$ErrorCopyWithImpl(Error _value, $Res Function(Error) _then) 614 | : super(_value, (v) => _then(v as Error)); 615 | 616 | @override 617 | Error get _value => super._value as Error; 618 | 619 | @override 620 | $Res call({ 621 | Object serverError = freezed, 622 | }) { 623 | return _then(Error( 624 | serverError: serverError == freezed 625 | ? _value.serverError 626 | : serverError as ServerError, 627 | )); 628 | } 629 | } 630 | 631 | class _$Error with DiagnosticableTreeMixin implements Error { 632 | const _$Error({@required this.serverError}) : assert(serverError != null); 633 | 634 | @override 635 | final ServerError serverError; 636 | 637 | @override 638 | String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { 639 | return 'SearchState.error(serverError: $serverError)'; 640 | } 641 | 642 | @override 643 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 644 | super.debugFillProperties(properties); 645 | properties 646 | ..add(DiagnosticsProperty('type', 'SearchState.error')) 647 | ..add(DiagnosticsProperty('serverError', serverError)); 648 | } 649 | 650 | @override 651 | bool operator ==(dynamic other) { 652 | return identical(this, other) || 653 | (other is Error && 654 | (identical(other.serverError, serverError) || 655 | const DeepCollectionEquality() 656 | .equals(other.serverError, serverError))); 657 | } 658 | 659 | @override 660 | int get hashCode => 661 | runtimeType.hashCode ^ const DeepCollectionEquality().hash(serverError); 662 | 663 | @override 664 | $ErrorCopyWith get copyWith => 665 | _$ErrorCopyWithImpl(this, _$identity); 666 | 667 | @override 668 | @optionalTypeArgs 669 | Result when({ 670 | @required Result initial(), 671 | @required Result loading(), 672 | @required Result loaded(Recipes recipes), 673 | @required Result error(ServerError serverError), 674 | }) { 675 | assert(initial != null); 676 | assert(loading != null); 677 | assert(loaded != null); 678 | assert(error != null); 679 | return error(serverError); 680 | } 681 | 682 | @override 683 | @optionalTypeArgs 684 | Result maybeWhen({ 685 | Result initial(), 686 | Result loading(), 687 | Result loaded(Recipes recipes), 688 | Result error(ServerError serverError), 689 | @required Result orElse(), 690 | }) { 691 | assert(orElse != null); 692 | if (error != null) { 693 | return error(serverError); 694 | } 695 | return orElse(); 696 | } 697 | 698 | @override 699 | @optionalTypeArgs 700 | Result map({ 701 | @required Result initial(Initial value), 702 | @required Result loading(Loading value), 703 | @required Result loaded(Loaded value), 704 | @required Result error(Error value), 705 | }) { 706 | assert(initial != null); 707 | assert(loading != null); 708 | assert(loaded != null); 709 | assert(error != null); 710 | return error(this); 711 | } 712 | 713 | @override 714 | @optionalTypeArgs 715 | Result maybeMap({ 716 | Result initial(Initial value), 717 | Result loading(Loading value), 718 | Result loaded(Loaded value), 719 | Result error(Error value), 720 | @required Result orElse(), 721 | }) { 722 | assert(orElse != null); 723 | if (error != null) { 724 | return error(this); 725 | } 726 | return orElse(); 727 | } 728 | } 729 | 730 | abstract class Error implements SearchState { 731 | const factory Error({@required ServerError serverError}) = _$Error; 732 | 733 | ServerError get serverError; 734 | $ErrorCopyWith get copyWith; 735 | } 736 | -------------------------------------------------------------------------------- /lib/ui/screens/search/bloc/events.dart: -------------------------------------------------------------------------------- 1 | part of 'bloc.dart'; 2 | 3 | @freezed 4 | abstract class SearchEvent with _$SearchEvent { 5 | const factory SearchEvent.searchRecipes({@required String query}) = 6 | SearchRecipes; 7 | } 8 | -------------------------------------------------------------------------------- /lib/ui/screens/search/bloc/states.dart: -------------------------------------------------------------------------------- 1 | part of 'bloc.dart'; 2 | 3 | @freezed 4 | abstract class SearchState with _$SearchState { 5 | const factory SearchState.initial() = Initial; 6 | 7 | const factory SearchState.loading() = Loading; 8 | 9 | const factory SearchState.loaded({@required Recipes recipes}) = Loaded; 10 | 11 | const factory SearchState.error({@required ServerError serverError}) = Error; 12 | } 13 | -------------------------------------------------------------------------------- /lib/ui/screens/search/search.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:recipes/ui/items/recipe.dart'; 4 | 5 | import 'bloc/bloc.dart'; 6 | 7 | class SearchScreen extends SearchDelegate { 8 | final _bloc = SearchBloc(); 9 | 10 | @override 11 | List buildActions(BuildContext context) => [ 12 | IconButton( 13 | icon: Icon(Icons.clear), 14 | onPressed: () { 15 | query = ''; 16 | }, 17 | ), 18 | ]; 19 | 20 | @override 21 | Widget buildLeading(BuildContext context) => IconButton( 22 | onPressed: () { 23 | close(context, null); 24 | }, 25 | icon: Icon(Icons.arrow_back_ios), 26 | ); 27 | 28 | @override 29 | Widget buildResults(BuildContext context) => 30 | BlocBuilder( 31 | bloc: _bloc..add(SearchEvent.searchRecipes(query: query)), 32 | builder: (_, state) => state.when( 33 | initial: () => const SizedBox(), 34 | loading: () => Center( 35 | child: CircularProgressIndicator(), 36 | ), 37 | loaded: (recipes) => ListView.builder( 38 | itemBuilder: (_, index) => RecipeItem( 39 | recipe: recipes.items[index], 40 | ), 41 | itemCount: recipes.items.length, 42 | ), 43 | error: (error) => Center( 44 | child: Column( 45 | mainAxisSize: MainAxisSize.min, 46 | children: [ 47 | Icon( 48 | Icons.check_box_outline_blank, 49 | size: 75, 50 | color: Colors.grey[500], 51 | ), 52 | const SizedBox(height: 20), 53 | Text( 54 | 'RECIPES NOT FOUND', 55 | style: TextStyle( 56 | fontSize: 20, 57 | color: Colors.grey[500], 58 | ), 59 | ), 60 | ], 61 | ), 62 | ), 63 | ), 64 | ); 65 | 66 | @override 67 | Widget buildSuggestions(BuildContext context) => const SizedBox(); 68 | 69 | @override 70 | void close(BuildContext context, result) { 71 | _bloc.close(); 72 | super.close(context, result); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/ui/screens/video.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:youtube_player_flutter/youtube_player_flutter.dart'; 3 | 4 | class VideoScreen extends StatefulWidget { 5 | final String videoUrl; 6 | 7 | const VideoScreen({Key key, @required this.videoUrl}) : super(key: key); 8 | 9 | @override 10 | _VideoScreenState createState() => _VideoScreenState(); 11 | } 12 | 13 | class _VideoScreenState extends State { 14 | @override 15 | Widget build(BuildContext context) => Scaffold( 16 | backgroundColor: Colors.black, 17 | appBar: AppBar(backgroundColor: Colors.black), 18 | body: Center( 19 | child: YoutubePlayer( 20 | controller: YoutubePlayerController( 21 | initialVideoId: YoutubePlayer.convertUrlToId(widget.videoUrl), 22 | ), 23 | ), 24 | ), 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "6.0.0" 11 | analyzer: 12 | dependency: transitive 13 | description: 14 | name: analyzer 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "0.39.14" 18 | args: 19 | dependency: transitive 20 | description: 21 | name: args 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.6.0" 25 | async: 26 | dependency: transitive 27 | description: 28 | name: async 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "2.4.2" 32 | bloc: 33 | dependency: transitive 34 | description: 35 | name: bloc 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "4.0.0" 39 | boolean_selector: 40 | dependency: transitive 41 | description: 42 | name: boolean_selector 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "2.0.0" 46 | build: 47 | dependency: transitive 48 | description: 49 | name: build 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.3.0" 53 | build_config: 54 | dependency: transitive 55 | description: 56 | name: build_config 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "0.4.2" 60 | build_daemon: 61 | dependency: transitive 62 | description: 63 | name: build_daemon 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "2.1.4" 67 | build_resolvers: 68 | dependency: transitive 69 | description: 70 | name: build_resolvers 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.3.11" 74 | build_runner: 75 | dependency: "direct dev" 76 | description: 77 | name: build_runner 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.10.0" 81 | build_runner_core: 82 | dependency: transitive 83 | description: 84 | name: build_runner_core 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "5.2.0" 88 | built_collection: 89 | dependency: transitive 90 | description: 91 | name: built_collection 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "4.3.2" 95 | built_value: 96 | dependency: transitive 97 | description: 98 | name: built_value 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "7.1.0" 102 | cached_network_image: 103 | dependency: "direct main" 104 | description: 105 | name: cached_network_image 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "2.2.0+1" 109 | characters: 110 | dependency: transitive 111 | description: 112 | name: characters 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "1.0.0" 116 | charcode: 117 | dependency: transitive 118 | description: 119 | name: charcode 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "1.1.3" 123 | checked_yaml: 124 | dependency: transitive 125 | description: 126 | name: checked_yaml 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "1.0.2" 130 | cli_util: 131 | dependency: transitive 132 | description: 133 | name: cli_util 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "0.1.4" 137 | clock: 138 | dependency: transitive 139 | description: 140 | name: clock 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "1.0.1" 144 | code_builder: 145 | dependency: transitive 146 | description: 147 | name: code_builder 148 | url: "https://pub.dartlang.org" 149 | source: hosted 150 | version: "3.4.1" 151 | collection: 152 | dependency: transitive 153 | description: 154 | name: collection 155 | url: "https://pub.dartlang.org" 156 | source: hosted 157 | version: "1.14.13" 158 | convert: 159 | dependency: transitive 160 | description: 161 | name: convert 162 | url: "https://pub.dartlang.org" 163 | source: hosted 164 | version: "2.1.1" 165 | crypto: 166 | dependency: transitive 167 | description: 168 | name: crypto 169 | url: "https://pub.dartlang.org" 170 | source: hosted 171 | version: "2.1.5" 172 | csslib: 173 | dependency: transitive 174 | description: 175 | name: csslib 176 | url: "https://pub.dartlang.org" 177 | source: hosted 178 | version: "0.16.2" 179 | cupertino_icons: 180 | dependency: "direct main" 181 | description: 182 | name: cupertino_icons 183 | url: "https://pub.dartlang.org" 184 | source: hosted 185 | version: "0.1.3" 186 | dart_style: 187 | dependency: transitive 188 | description: 189 | name: dart_style 190 | url: "https://pub.dartlang.org" 191 | source: hosted 192 | version: "1.3.6" 193 | dartz: 194 | dependency: "direct main" 195 | description: 196 | name: dartz 197 | url: "https://pub.dartlang.org" 198 | source: hosted 199 | version: "0.9.1" 200 | dio: 201 | dependency: "direct main" 202 | description: 203 | name: dio 204 | url: "https://pub.dartlang.org" 205 | source: hosted 206 | version: "3.0.10" 207 | fake_async: 208 | dependency: transitive 209 | description: 210 | name: fake_async 211 | url: "https://pub.dartlang.org" 212 | source: hosted 213 | version: "1.1.0" 214 | file: 215 | dependency: transitive 216 | description: 217 | name: file 218 | url: "https://pub.dartlang.org" 219 | source: hosted 220 | version: "5.2.1" 221 | fixnum: 222 | dependency: transitive 223 | description: 224 | name: fixnum 225 | url: "https://pub.dartlang.org" 226 | source: hosted 227 | version: "0.10.11" 228 | flutter: 229 | dependency: "direct main" 230 | description: flutter 231 | source: sdk 232 | version: "0.0.0" 233 | flutter_bloc: 234 | dependency: "direct main" 235 | description: 236 | name: flutter_bloc 237 | url: "https://pub.dartlang.org" 238 | source: hosted 239 | version: "4.0.1" 240 | flutter_cache_manager: 241 | dependency: transitive 242 | description: 243 | name: flutter_cache_manager 244 | url: "https://pub.dartlang.org" 245 | source: hosted 246 | version: "1.4.2" 247 | flutter_inappwebview: 248 | dependency: transitive 249 | description: 250 | name: flutter_inappwebview 251 | url: "https://pub.dartlang.org" 252 | source: hosted 253 | version: "4.0.0+4" 254 | flutter_test: 255 | dependency: "direct dev" 256 | description: flutter 257 | source: sdk 258 | version: "0.0.0" 259 | freezed: 260 | dependency: "direct dev" 261 | description: 262 | name: freezed 263 | url: "https://pub.dartlang.org" 264 | source: hosted 265 | version: "0.11.6" 266 | freezed_annotation: 267 | dependency: "direct main" 268 | description: 269 | name: freezed_annotation 270 | url: "https://pub.dartlang.org" 271 | source: hosted 272 | version: "0.11.0+1" 273 | glob: 274 | dependency: transitive 275 | description: 276 | name: glob 277 | url: "https://pub.dartlang.org" 278 | source: hosted 279 | version: "1.2.0" 280 | google_fonts: 281 | dependency: "direct main" 282 | description: 283 | name: google_fonts 284 | url: "https://pub.dartlang.org" 285 | source: hosted 286 | version: "1.1.0" 287 | graphs: 288 | dependency: transitive 289 | description: 290 | name: graphs 291 | url: "https://pub.dartlang.org" 292 | source: hosted 293 | version: "0.2.0" 294 | hive: 295 | dependency: "direct main" 296 | description: 297 | name: hive 298 | url: "https://pub.dartlang.org" 299 | source: hosted 300 | version: "1.4.4" 301 | hive_flutter: 302 | dependency: "direct main" 303 | description: 304 | name: hive_flutter 305 | url: "https://pub.dartlang.org" 306 | source: hosted 307 | version: "0.3.1" 308 | hive_generator: 309 | dependency: "direct dev" 310 | description: 311 | name: hive_generator 312 | url: "https://pub.dartlang.org" 313 | source: hosted 314 | version: "0.5.2" 315 | html: 316 | dependency: transitive 317 | description: 318 | name: html 319 | url: "https://pub.dartlang.org" 320 | source: hosted 321 | version: "0.14.0+3" 322 | http: 323 | dependency: transitive 324 | description: 325 | name: http 326 | url: "https://pub.dartlang.org" 327 | source: hosted 328 | version: "0.12.2" 329 | http_multi_server: 330 | dependency: transitive 331 | description: 332 | name: http_multi_server 333 | url: "https://pub.dartlang.org" 334 | source: hosted 335 | version: "2.2.0" 336 | http_parser: 337 | dependency: transitive 338 | description: 339 | name: http_parser 340 | url: "https://pub.dartlang.org" 341 | source: hosted 342 | version: "3.1.4" 343 | intl: 344 | dependency: transitive 345 | description: 346 | name: intl 347 | url: "https://pub.dartlang.org" 348 | source: hosted 349 | version: "0.16.1" 350 | io: 351 | dependency: transitive 352 | description: 353 | name: io 354 | url: "https://pub.dartlang.org" 355 | source: hosted 356 | version: "0.3.4" 357 | js: 358 | dependency: transitive 359 | description: 360 | name: js 361 | url: "https://pub.dartlang.org" 362 | source: hosted 363 | version: "0.6.2" 364 | json_annotation: 365 | dependency: transitive 366 | description: 367 | name: json_annotation 368 | url: "https://pub.dartlang.org" 369 | source: hosted 370 | version: "3.0.1" 371 | json_serializable: 372 | dependency: "direct main" 373 | description: 374 | name: json_serializable 375 | url: "https://pub.dartlang.org" 376 | source: hosted 377 | version: "3.4.1" 378 | logging: 379 | dependency: transitive 380 | description: 381 | name: logging 382 | url: "https://pub.dartlang.org" 383 | source: hosted 384 | version: "0.11.4" 385 | matcher: 386 | dependency: transitive 387 | description: 388 | name: matcher 389 | url: "https://pub.dartlang.org" 390 | source: hosted 391 | version: "0.12.8" 392 | meta: 393 | dependency: transitive 394 | description: 395 | name: meta 396 | url: "https://pub.dartlang.org" 397 | source: hosted 398 | version: "1.1.8" 399 | mime: 400 | dependency: transitive 401 | description: 402 | name: mime 403 | url: "https://pub.dartlang.org" 404 | source: hosted 405 | version: "0.9.7" 406 | nested: 407 | dependency: transitive 408 | description: 409 | name: nested 410 | url: "https://pub.dartlang.org" 411 | source: hosted 412 | version: "0.0.4" 413 | node_interop: 414 | dependency: transitive 415 | description: 416 | name: node_interop 417 | url: "https://pub.dartlang.org" 418 | source: hosted 419 | version: "1.1.1" 420 | node_io: 421 | dependency: transitive 422 | description: 423 | name: node_io 424 | url: "https://pub.dartlang.org" 425 | source: hosted 426 | version: "1.1.1" 427 | package_config: 428 | dependency: transitive 429 | description: 430 | name: package_config 431 | url: "https://pub.dartlang.org" 432 | source: hosted 433 | version: "1.9.3" 434 | path: 435 | dependency: transitive 436 | description: 437 | name: path 438 | url: "https://pub.dartlang.org" 439 | source: hosted 440 | version: "1.7.0" 441 | path_provider: 442 | dependency: transitive 443 | description: 444 | name: path_provider 445 | url: "https://pub.dartlang.org" 446 | source: hosted 447 | version: "1.6.14" 448 | path_provider_linux: 449 | dependency: transitive 450 | description: 451 | name: path_provider_linux 452 | url: "https://pub.dartlang.org" 453 | source: hosted 454 | version: "0.0.1+2" 455 | path_provider_macos: 456 | dependency: transitive 457 | description: 458 | name: path_provider_macos 459 | url: "https://pub.dartlang.org" 460 | source: hosted 461 | version: "0.0.4+4" 462 | path_provider_platform_interface: 463 | dependency: transitive 464 | description: 465 | name: path_provider_platform_interface 466 | url: "https://pub.dartlang.org" 467 | source: hosted 468 | version: "1.0.3" 469 | pedantic: 470 | dependency: transitive 471 | description: 472 | name: pedantic 473 | url: "https://pub.dartlang.org" 474 | source: hosted 475 | version: "1.9.0" 476 | platform: 477 | dependency: transitive 478 | description: 479 | name: platform 480 | url: "https://pub.dartlang.org" 481 | source: hosted 482 | version: "2.2.1" 483 | plugin_platform_interface: 484 | dependency: transitive 485 | description: 486 | name: plugin_platform_interface 487 | url: "https://pub.dartlang.org" 488 | source: hosted 489 | version: "1.0.2" 490 | pool: 491 | dependency: transitive 492 | description: 493 | name: pool 494 | url: "https://pub.dartlang.org" 495 | source: hosted 496 | version: "1.4.0" 497 | process: 498 | dependency: transitive 499 | description: 500 | name: process 501 | url: "https://pub.dartlang.org" 502 | source: hosted 503 | version: "3.0.13" 504 | provider: 505 | dependency: transitive 506 | description: 507 | name: provider 508 | url: "https://pub.dartlang.org" 509 | source: hosted 510 | version: "4.3.2+2" 511 | pub_semver: 512 | dependency: transitive 513 | description: 514 | name: pub_semver 515 | url: "https://pub.dartlang.org" 516 | source: hosted 517 | version: "1.4.4" 518 | pubspec_parse: 519 | dependency: transitive 520 | description: 521 | name: pubspec_parse 522 | url: "https://pub.dartlang.org" 523 | source: hosted 524 | version: "0.1.5" 525 | quiver: 526 | dependency: transitive 527 | description: 528 | name: quiver 529 | url: "https://pub.dartlang.org" 530 | source: hosted 531 | version: "2.1.3" 532 | rxdart: 533 | dependency: transitive 534 | description: 535 | name: rxdart 536 | url: "https://pub.dartlang.org" 537 | source: hosted 538 | version: "0.24.1" 539 | shelf: 540 | dependency: transitive 541 | description: 542 | name: shelf 543 | url: "https://pub.dartlang.org" 544 | source: hosted 545 | version: "0.7.9" 546 | shelf_web_socket: 547 | dependency: transitive 548 | description: 549 | name: shelf_web_socket 550 | url: "https://pub.dartlang.org" 551 | source: hosted 552 | version: "0.2.3" 553 | sky_engine: 554 | dependency: transitive 555 | description: flutter 556 | source: sdk 557 | version: "0.0.99" 558 | source_gen: 559 | dependency: transitive 560 | description: 561 | name: source_gen 562 | url: "https://pub.dartlang.org" 563 | source: hosted 564 | version: "0.9.6" 565 | source_span: 566 | dependency: transitive 567 | description: 568 | name: source_span 569 | url: "https://pub.dartlang.org" 570 | source: hosted 571 | version: "1.7.0" 572 | sqflite: 573 | dependency: transitive 574 | description: 575 | name: sqflite 576 | url: "https://pub.dartlang.org" 577 | source: hosted 578 | version: "1.3.1+1" 579 | sqflite_common: 580 | dependency: transitive 581 | description: 582 | name: sqflite_common 583 | url: "https://pub.dartlang.org" 584 | source: hosted 585 | version: "1.0.2+1" 586 | stack_trace: 587 | dependency: transitive 588 | description: 589 | name: stack_trace 590 | url: "https://pub.dartlang.org" 591 | source: hosted 592 | version: "1.9.5" 593 | stream_channel: 594 | dependency: transitive 595 | description: 596 | name: stream_channel 597 | url: "https://pub.dartlang.org" 598 | source: hosted 599 | version: "2.0.0" 600 | stream_transform: 601 | dependency: transitive 602 | description: 603 | name: stream_transform 604 | url: "https://pub.dartlang.org" 605 | source: hosted 606 | version: "1.2.0" 607 | string_scanner: 608 | dependency: transitive 609 | description: 610 | name: string_scanner 611 | url: "https://pub.dartlang.org" 612 | source: hosted 613 | version: "1.0.5" 614 | synchronized: 615 | dependency: transitive 616 | description: 617 | name: synchronized 618 | url: "https://pub.dartlang.org" 619 | source: hosted 620 | version: "2.2.0+2" 621 | term_glyph: 622 | dependency: transitive 623 | description: 624 | name: term_glyph 625 | url: "https://pub.dartlang.org" 626 | source: hosted 627 | version: "1.1.0" 628 | test_api: 629 | dependency: transitive 630 | description: 631 | name: test_api 632 | url: "https://pub.dartlang.org" 633 | source: hosted 634 | version: "0.2.17" 635 | timing: 636 | dependency: transitive 637 | description: 638 | name: timing 639 | url: "https://pub.dartlang.org" 640 | source: hosted 641 | version: "0.1.1+2" 642 | typed_data: 643 | dependency: transitive 644 | description: 645 | name: typed_data 646 | url: "https://pub.dartlang.org" 647 | source: hosted 648 | version: "1.2.0" 649 | uuid: 650 | dependency: transitive 651 | description: 652 | name: uuid 653 | url: "https://pub.dartlang.org" 654 | source: hosted 655 | version: "2.2.2" 656 | vector_math: 657 | dependency: transitive 658 | description: 659 | name: vector_math 660 | url: "https://pub.dartlang.org" 661 | source: hosted 662 | version: "2.0.8" 663 | watcher: 664 | dependency: transitive 665 | description: 666 | name: watcher 667 | url: "https://pub.dartlang.org" 668 | source: hosted 669 | version: "0.9.7+15" 670 | web_socket_channel: 671 | dependency: transitive 672 | description: 673 | name: web_socket_channel 674 | url: "https://pub.dartlang.org" 675 | source: hosted 676 | version: "1.1.0" 677 | xdg_directories: 678 | dependency: transitive 679 | description: 680 | name: xdg_directories 681 | url: "https://pub.dartlang.org" 682 | source: hosted 683 | version: "0.1.0" 684 | yaml: 685 | dependency: transitive 686 | description: 687 | name: yaml 688 | url: "https://pub.dartlang.org" 689 | source: hosted 690 | version: "2.2.1" 691 | youtube_player_flutter: 692 | dependency: "direct main" 693 | description: 694 | name: youtube_player_flutter 695 | url: "https://pub.dartlang.org" 696 | source: hosted 697 | version: "7.0.0+7" 698 | sdks: 699 | dart: ">=2.9.0-14.0.dev <3.0.0" 700 | flutter: ">=1.17.0 <2.0.0" 701 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: recipes 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.6.0 <3.0.0" 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | 27 | 28 | # The following adds the Cupertino Icons font to your application. 29 | # Use with the CupertinoIcons class for iOS style icons. 30 | cupertino_icons: ^0.1.3 31 | dio: ^3.0.10 32 | freezed_annotation: 33 | google_fonts: ^1.1.0 34 | flutter_bloc: ^4.0.0 35 | cached_network_image: ^2.2.0+1 36 | dartz: ^0.9.1 37 | json_serializable: ^3.4.1 38 | youtube_player_flutter: ^7.0.0+7 39 | hive: ^1.4.4 40 | hive_flutter: ^0.3.1 41 | 42 | dev_dependencies: 43 | flutter_test: 44 | sdk: flutter 45 | build_runner: 46 | freezed: 47 | hive_generator: ^0.5.1 48 | 49 | # For information on the generic Dart part of this file, see the 50 | # following page: https://dart.dev/tools/pub/pubspec 51 | 52 | # The following section is specific to Flutter. 53 | flutter: 54 | 55 | # The following line ensures that the Material Icons font is 56 | # included with your application, so that you can use the icons in 57 | # the material Icons class. 58 | uses-material-design: true 59 | 60 | # To add assets to your application, add an assets section, like this: 61 | # assets: 62 | # - images/a_dot_burr.jpeg 63 | # - images/a_dot_ham.jpeg 64 | 65 | # An image asset can refer to one or more resolution-specific "variants", see 66 | # https://flutter.dev/assets-and-images/#resolution-aware. 67 | 68 | # For details regarding adding assets from package dependencies, see 69 | # https://flutter.dev/assets-and-images/#from-packages 70 | 71 | # To add custom fonts to your application, add a fonts section here, 72 | # in this "flutter" section. Each entry in this list should have a 73 | # "family" key with the font family name, and a "fonts" key with a 74 | # list giving the asset and other descriptors for the font. For 75 | # example: 76 | # fonts: 77 | # - family: Schyler 78 | # fonts: 79 | # - asset: fonts/Schyler-Regular.ttf 80 | # - asset: fonts/Schyler-Italic.ttf 81 | # style: italic 82 | # - family: Trajan Pro 83 | # fonts: 84 | # - asset: fonts/TrajanPro.ttf 85 | # - asset: fonts/TrajanPro_Bold.ttf 86 | # weight: 700 87 | # 88 | # For details regarding fonts from package dependencies, 89 | # see https://flutter.dev/custom-fonts/#from-packages 90 | -------------------------------------------------------------------------------- /screenshot/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/screenshot/0.jpg -------------------------------------------------------------------------------- /screenshot/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/screenshot/1.jpg -------------------------------------------------------------------------------- /screenshot/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/screenshot/2.jpg -------------------------------------------------------------------------------- /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 that Flutter provides. 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:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:recipes/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekAbdelouahed/Flutter-Recipes/2d07e23b58ce8a3191d0ab1e7a87fcfb7e74b4ff/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | recipes 18 | 19 | 20 | 21 | 24 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "recipes", 3 | "short_name": "recipes", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 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 | } 24 | --------------------------------------------------------------------------------