├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── recipe_app │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── 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-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── fonts │ ├── Inter-Black.ttf │ ├── Inter-Bold.ttf │ ├── Inter-ExtraBold.ttf │ ├── Inter-ExtraLight.ttf │ ├── Inter-Light.ttf │ ├── Inter-Medium.ttf │ ├── Inter-Regular.ttf │ ├── Inter-SemiBold.ttf │ └── Inter-Thin.ttf ├── icons │ ├── arrow-upward.svg │ ├── camera.svg │ ├── check-green.svg │ ├── check-grey.svg │ ├── drag-icon.svg │ ├── edit.svg │ ├── emoticon-party.svg │ ├── filter.svg │ ├── heart.svg │ ├── home.svg │ ├── image.svg │ ├── lock.svg │ ├── message.svg │ ├── notification.svg │ ├── profile.svg │ ├── scan.svg │ └── search.svg └── images │ ├── cake.jpg │ ├── emoticon-party.png │ ├── onboarding.png │ └── pancake.jpg ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@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 └── RunnerTests │ └── RunnerTests.swift ├── lib ├── main.dart └── src │ ├── core │ ├── controllers │ │ ├── dashboard_controller.dart │ │ ├── helper_controller.dart │ │ ├── login_controller.dart │ │ ├── other_controller.dart │ │ ├── password_controller.dart │ │ ├── register_controller.dart │ │ ├── search_controller.dart │ │ ├── upload_controller.dart │ │ └── verify_controller.dart │ ├── functions │ │ └── network_image.dart │ └── models │ │ ├── helper_model.dart │ │ └── recipe_model.dart │ ├── routes │ └── routes.dart │ └── ui │ ├── screens │ ├── auth │ │ ├── change_password_screen.dart │ │ ├── login_screen.dart │ │ ├── password_recovery_screen.dart │ │ ├── register_screen.dart │ │ └── verify_screen.dart │ ├── demo.dart │ ├── intro │ │ └── onboarding_screen.dart │ ├── main │ │ ├── dashboard_screen.dart │ │ └── home_screen.dart │ ├── notification │ │ └── notification_screen.dart │ ├── recipe │ │ ├── detail_recipe_screen.dart │ │ └── popular_recipe_sceen.dart │ ├── search │ │ └── search_form_screen.dart │ ├── upload │ │ ├── step1_screen.dart │ │ └── step2_screen.dart │ └── user │ │ ├── profile_screen.dart │ │ └── user_recipe_screen.dart │ ├── utils │ ├── assets_util.dart │ ├── colors_util.dart │ ├── helper_util.dart │ ├── layout_util.dart │ └── typography_util.dart │ └── widgets │ ├── button_widget.dart │ ├── component_widget.dart │ ├── form_widget.dart │ ├── helper_widget.dart │ ├── notification_widget.dart │ └── recipe_widget.dart ├── pubspec.lock ├── pubspec.yaml ├── screenshoot ├── dashboard.png ├── login.png ├── notification.png ├── profile.png └── recipe.png └── test └── widget_test.dart /.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 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /.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: "41456452f29d64e8deb623a3c927524bcf9f111b" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b 17 | base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b 18 | - platform: android 19 | create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b 20 | base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b 21 | - platform: ios 22 | create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b 23 | base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b 24 | 25 | # User provided section 26 | 27 | # List of Local paths (relative to this file) that should be 28 | # ignored by the migrate tool. 29 | # 30 | # Files that are not part of the templates will be ignored by default. 31 | unmanaged_files: 32 | - 'lib/main.dart' 33 | - 'ios/Runner.xcodeproj/project.pbxproj' 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Chefio Flutter UI 3 | 4 | This is implementation figma design into Flutter code. 5 | 6 | ## Screenshoot 7 |

8 | 9 | 10 | 11 | 12 | 13 |

14 | 15 | ## Table of Contents 16 | - [Quick Start](#quick-start) 17 | - [Package Used](#package-used) 18 | - [Authors](#authors) 19 | - [Thanks](#thanks) 20 | - [Copyright and license](#copyright-and-license) 21 | ## Quick Start 22 | This is a normal flutter app. You should follow the instructions in the [official documentation](https://flutter.io/docs/get-started/install). 23 | ## Package Used 24 | * Getx State Management 25 | * Cache Image Newtork 26 | * Shimmer 27 | * Flutter SVG 28 | * Flutter Icon 29 | * Dotted Border 30 | * Pin Code Fields 31 | ## Author 32 | 33 | [Agis R Herdiana](https://www.github.com/agisrh) 34 | 35 | 36 | ## Thanks 37 | 38 | Thanks to [Choirul Syafril](https://www.behance.net/gallery/109160303/Freebies-Chefio-Recipe-App-UI-Kit) for awesome design 39 | ## Copyright and License 40 | 41 | Code and documentation copyright 2022 the authors. Code released under the [MIT License](https://github.com/agisrh/recipe_app/blob/master/LICENSE). 42 | 43 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /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 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | android { 26 | namespace "com.example.recipe_app" 27 | compileSdkVersion flutter.compileSdkVersion 28 | ndkVersion flutter.ndkVersion 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | 39 | sourceSets { 40 | main.java.srcDirs += 'src/main/kotlin' 41 | } 42 | 43 | defaultConfig { 44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 45 | applicationId "com.example.recipe_app" 46 | // You can update the following values to match your application needs. 47 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 48 | minSdkVersion flutter.minSdkVersion 49 | targetSdkVersion flutter.targetSdkVersion 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | } 53 | 54 | buildTypes { 55 | release { 56 | // TODO: Add your own signing config for the release build. 57 | // Signing with the debug keys for now, so `flutter run --release` works. 58 | signingConfig signingConfigs.debug 59 | } 60 | } 61 | } 62 | 63 | flutter { 64 | source '../..' 65 | } 66 | 67 | dependencies {} 68 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/recipe_app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.recipe_app 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 10 | } 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | google() 16 | mavenCentral() 17 | } 18 | } 19 | 20 | rootProject.buildDir = '../build' 21 | subprojects { 22 | project.buildDir = "${rootProject.buildDir}/${project.name}" 23 | } 24 | subprojects { 25 | project.evaluationDependsOn(':app') 26 | } 27 | 28 | tasks.register("clean", Delete) { 29 | delete rootProject.buildDir 30 | } 31 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | } 9 | settings.ext.flutterSdkPath = flutterSdkPath() 10 | 11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") 12 | 13 | repositories { 14 | google() 15 | mavenCentral() 16 | gradlePluginPortal() 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false 21 | } 22 | } 23 | 24 | plugins { 25 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 26 | id "com.android.application" version "7.3.0" apply false 27 | } 28 | 29 | include ":app" 30 | -------------------------------------------------------------------------------- /assets/fonts/Inter-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/assets/fonts/Inter-Black.ttf -------------------------------------------------------------------------------- /assets/fonts/Inter-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/assets/fonts/Inter-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/Inter-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/assets/fonts/Inter-ExtraBold.ttf -------------------------------------------------------------------------------- /assets/fonts/Inter-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/assets/fonts/Inter-ExtraLight.ttf -------------------------------------------------------------------------------- /assets/fonts/Inter-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/assets/fonts/Inter-Light.ttf -------------------------------------------------------------------------------- /assets/fonts/Inter-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/assets/fonts/Inter-Medium.ttf -------------------------------------------------------------------------------- /assets/fonts/Inter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/assets/fonts/Inter-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/Inter-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/assets/fonts/Inter-SemiBold.ttf -------------------------------------------------------------------------------- /assets/fonts/Inter-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/assets/fonts/Inter-Thin.ttf -------------------------------------------------------------------------------- /assets/icons/arrow-upward.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/camera.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/check-green.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/check-grey.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/drag-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/icons/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/icons/home.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/image.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/icons/message.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/notification.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/profile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/scan.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/images/cake.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/assets/images/cake.jpg -------------------------------------------------------------------------------- /assets/images/emoticon-party.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/assets/images/emoticon-party.png -------------------------------------------------------------------------------- /assets/images/onboarding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/assets/images/onboarding.png -------------------------------------------------------------------------------- /assets/images/pancake.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/assets/images/pancake.jpg -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '15.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - path_provider_foundation (0.0.1): 4 | - Flutter 5 | - FlutterMacOS 6 | - sqflite (0.0.3): 7 | - Flutter 8 | - FlutterMacOS 9 | 10 | DEPENDENCIES: 11 | - Flutter (from `Flutter`) 12 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) 13 | - sqflite (from `.symlinks/plugins/sqflite/darwin`) 14 | 15 | EXTERNAL SOURCES: 16 | Flutter: 17 | :path: Flutter 18 | path_provider_foundation: 19 | :path: ".symlinks/plugins/path_provider_foundation/darwin" 20 | sqflite: 21 | :path: ".symlinks/plugins/sqflite/darwin" 22 | 23 | SPEC CHECKSUMS: 24 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 25 | path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c 26 | sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec 27 | 28 | PODFILE CHECKSUM: 9c46fd01abff66081b39f5fa5767b3f1d0b11d76 29 | 30 | COCOAPODS: 1.13.0 31 | -------------------------------------------------------------------------------- /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 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import 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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/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 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Recipe App 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | recipe_app 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 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:get/get.dart'; 4 | import 'src/routes/routes.dart'; 5 | 6 | void main() { 7 | runApp(const MyApp()); 8 | } 9 | 10 | class MyApp extends StatelessWidget { 11 | const MyApp({super.key}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( 16 | statusBarColor: Colors.white, 17 | statusBarIconBrightness: Brightness.dark, 18 | )); 19 | return GetMaterialApp( 20 | debugShowCheckedModeBanner: false, 21 | theme: ThemeData( 22 | fontFamily: 'Inter', 23 | ), 24 | defaultTransition: Transition.native, 25 | initialRoute: "/intro/onboarding", 26 | getPages: Routes.pages, 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/core/controllers/dashboard_controller.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/src/core/controllers/helper_controller.dart: -------------------------------------------------------------------------------- 1 | export 'login_controller.dart'; 2 | export 'register_controller.dart'; 3 | export 'verify_controller.dart'; 4 | export 'password_controller.dart'; 5 | export 'dashboard_controller.dart'; 6 | export 'other_controller.dart'; 7 | export 'search_controller.dart'; 8 | export 'upload_controller.dart'; 9 | -------------------------------------------------------------------------------- /lib/src/core/controllers/login_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | class LoginController extends GetxController { 5 | final username = TextEditingController(); 6 | final password = TextEditingController(); 7 | 8 | // Show or hide password 9 | var showPassword = true.obs; 10 | void visiblePassword() => showPassword.value = !showPassword.value; 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/core/controllers/other_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | class OtherController extends GetxController { 4 | var choice = 0.obs; 5 | void setChoice(int index) => choice.value = index; 6 | 7 | var sliderValue = 30.0.obs; 8 | void setSlider(double value) => sliderValue.value = value; 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/core/controllers/password_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | class PasswordController extends GetxController { 5 | final username = TextEditingController(); 6 | final password = TextEditingController(); 7 | 8 | // Show or hide password 9 | var showPassword = true.obs; 10 | void visiblePassword() => showPassword.value = !showPassword.value; 11 | 12 | @override 13 | void onInit() { 14 | passwordListener(); 15 | super.onInit(); 16 | } 17 | 18 | // Password Contains 19 | var eightChars = false.obs; 20 | var hasNumber = false.obs; 21 | var hasSpecialCharacters = false.obs; 22 | var btnDisable = true.obs; 23 | void passwordListener() { 24 | password.addListener(() { 25 | // Must 8 digit 26 | if (password.text.length >= 8) { 27 | eightChars.value = true; 28 | } else { 29 | eightChars.value = false; 30 | } 31 | 32 | // Must container number 33 | if (password.text.contains(RegExp(r'[0-9]'))) { 34 | hasNumber.value = true; 35 | } else { 36 | hasNumber.value = false; 37 | } 38 | 39 | // Must container special character 40 | if (password.text.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]'))) { 41 | hasSpecialCharacters.value = true; 42 | } else { 43 | hasSpecialCharacters.value = false; 44 | } 45 | 46 | // Diable button 47 | if (eightChars.value && hasNumber.value && hasSpecialCharacters.value) { 48 | btnDisable.value = false; 49 | } else { 50 | btnDisable.value = true; 51 | } 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/src/core/controllers/register_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | class RegisterController extends GetxController { 5 | final username = TextEditingController(); 6 | final password = TextEditingController(); 7 | 8 | // Show or hide password 9 | var showPassword = true.obs; 10 | void visiblePassword() => showPassword.value = !showPassword.value; 11 | 12 | @override 13 | void onInit() { 14 | passwordListener(); 15 | super.onInit(); 16 | } 17 | 18 | // Password Contains 19 | var eightChars = false.obs; 20 | var hasNumber = false.obs; 21 | var hasSpecialCharacters = false.obs; 22 | var btnDisable = true.obs; 23 | void passwordListener() { 24 | password.addListener(() { 25 | // Must 8 digit 26 | if (password.text.length >= 8) { 27 | eightChars.value = true; 28 | } else { 29 | eightChars.value = false; 30 | } 31 | 32 | // Must container number 33 | if (password.text.contains(RegExp(r'[0-9]'))) { 34 | hasNumber.value = true; 35 | } else { 36 | hasNumber.value = false; 37 | } 38 | 39 | // Must container special character 40 | if (password.text.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]'))) { 41 | hasSpecialCharacters.value = true; 42 | } else { 43 | hasSpecialCharacters.value = false; 44 | } 45 | 46 | // Diable button 47 | if (eightChars.value && hasNumber.value && hasSpecialCharacters.value) { 48 | btnDisable.value = false; 49 | } else { 50 | btnDisable.value = true; 51 | } 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/src/core/controllers/search_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | class SearchFormController extends GetxController { 5 | final search = TextEditingController(); 6 | 7 | @override 8 | void onInit() { 9 | searchListener(); 10 | super.onInit(); 11 | } 12 | 13 | var hasTxtSearch = false.obs; 14 | void searchListener() { 15 | search.addListener(() { 16 | // check if search form is not empty 17 | if (search.text.isNotEmpty) { 18 | hasTxtSearch.value = true; 19 | } else { 20 | hasTxtSearch.value = false; 21 | } 22 | }); 23 | } 24 | 25 | void clearForm() => search.clear(); 26 | 27 | var isFilter = false.obs; 28 | void filter() => isFilter.value = true; 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/core/controllers/upload_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | class UploadController extends GetxController { 5 | final foodName = TextEditingController(); 6 | final description = TextEditingController(); 7 | final ingredient = TextEditingController(); 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/core/controllers/verify_controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:get/get.dart'; 3 | 4 | class VerifyController extends GetxController { 5 | var counter = "".obs; 6 | var timeStart = true.obs; 7 | late Timer _timer; 8 | int currentSeconds = 0; 9 | int timerMaxSeconds = 180; 10 | 11 | String get timerText => 12 | '${((timerMaxSeconds - currentSeconds) ~/ 60).toString().padLeft(2, '0')}:${((timerMaxSeconds - currentSeconds) % 60).toString().padLeft(2, '0')}'; 13 | 14 | void startTimer() { 15 | timeStart.value = true; 16 | _timer = Timer.periodic(const Duration(seconds: 1), (timer) { 17 | currentSeconds = timer.tick; 18 | if (timer.tick > timerMaxSeconds) { 19 | _timer.cancel(); 20 | timeStart.value = false; 21 | } else { 22 | counter.value = timerText; 23 | } 24 | }); 25 | } 26 | 27 | @override 28 | void onInit() { 29 | startTimer(); 30 | super.onInit(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/core/functions/network_image.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:shimmer/shimmer.dart'; 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | 5 | class PNetworkImage extends StatelessWidget { 6 | final String? image; 7 | final BoxFit? fit; 8 | final double? width, height; 9 | const PNetworkImage(this.image, {Key? key, this.fit, this.height, this.width}) 10 | : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | // return Image.network( 15 | // image!, 16 | // // placeholder: (context, url) => Center(child: CircularProgressIndicator()), 17 | // // errorWidget: (context, url, error) => Image.asset('assets/placeholder.jpg',fit: BoxFit.cover,), 18 | // fit: fit, 19 | // width: width, 20 | // height: height, 21 | // ); 22 | 23 | return CachedNetworkImage( 24 | imageUrl: image!, 25 | imageBuilder: (context, imageProvider) => Container( 26 | decoration: BoxDecoration( 27 | image: DecorationImage( 28 | image: imageProvider, 29 | fit: fit, 30 | ), 31 | ), 32 | ), 33 | placeholder: (context, url) => Shimmer.fromColors( 34 | baseColor: Colors.black12, 35 | highlightColor: Colors.white10, 36 | child: Container( 37 | decoration: const BoxDecoration( 38 | color: Colors.amberAccent, 39 | )), 40 | ), 41 | errorWidget: (context, url, error) => const Center( 42 | child: Icon(Icons.error), 43 | ), 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/core/models/helper_model.dart: -------------------------------------------------------------------------------- 1 | export 'recipe_model.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/core/models/recipe_model.dart: -------------------------------------------------------------------------------- 1 | class RecipeModel { 2 | late String author; 3 | late String title; 4 | late String category; 5 | late String duration; 6 | late String imgAuthor; 7 | late String imgCover; 8 | late bool favorite; 9 | 10 | RecipeModel({ 11 | required this.author, 12 | required this.title, 13 | required this.category, 14 | required this.duration, 15 | required this.imgAuthor, 16 | required this.imgCover, 17 | required this.favorite, 18 | }); 19 | 20 | RecipeModel.fromJson(Map json) { 21 | author = json['author'].toString(); 22 | title = json['title'].toString(); 23 | category = json['category'].toString(); 24 | duration = json['duration'].toString(); 25 | imgAuthor = json['img_author'].toString(); 26 | imgCover = json['img_cover'].toString(); 27 | favorite = json['favorite']; 28 | } 29 | 30 | Map toJson() { 31 | final Map data = {}; 32 | data['author'] = author; 33 | data['title'] = title; 34 | data['category'] = category; 35 | data['duration'] = duration; 36 | data['img_author'] = imgAuthor; 37 | data['img_cover'] = imgCover; 38 | data['favorite'] = favorite; 39 | return data; 40 | } 41 | 42 | // static List fromJsonList(List list) { 43 | // if (list == null) return null; 44 | // return list.map((item) => RecipeModel.fromJson(item)).toList(); 45 | // } 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/routes/routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:recipe_app/src/ui/screens/auth/change_password_screen.dart'; 3 | import 'package:recipe_app/src/ui/screens/auth/login_screen.dart'; 4 | import 'package:recipe_app/src/ui/screens/auth/password_recovery_screen.dart'; 5 | import 'package:recipe_app/src/ui/screens/auth/register_screen.dart'; 6 | import 'package:recipe_app/src/ui/screens/auth/verify_screen.dart'; 7 | import 'package:recipe_app/src/ui/screens/intro/onboarding_screen.dart'; 8 | import 'package:recipe_app/src/ui/screens/main/home_screen.dart'; 9 | import 'package:recipe_app/src/ui/screens/recipe/detail_recipe_screen.dart'; 10 | import 'package:recipe_app/src/ui/screens/search/search_form_screen.dart'; 11 | import 'package:recipe_app/src/ui/screens/upload/step1_screen.dart'; 12 | import 'package:recipe_app/src/ui/screens/upload/step2_screen.dart'; 13 | 14 | class Routes { 15 | static final pages = [ 16 | GetPage( 17 | name: '/intro/onboarding', 18 | page: () => const OnboardingScreen(), 19 | ), 20 | GetPage( 21 | name: '/auth/login', 22 | page: () => LoginScreen(), 23 | ), 24 | GetPage( 25 | name: '/auth/register', 26 | page: () => RegisterScreen(), 27 | ), 28 | GetPage( 29 | name: '/auth/verify', 30 | page: () => const VerifyScreen(), 31 | ), 32 | GetPage( 33 | name: '/auth/password/recovery', 34 | page: () => PasswordRecoveryScreen(), 35 | ), 36 | GetPage( 37 | name: '/auth/password/change', 38 | page: () => ChangePasswordScreen(), 39 | ), 40 | GetPage( 41 | name: '/home', 42 | page: () => const HomeScreen(), 43 | ), 44 | GetPage( 45 | name: '/search/form', 46 | page: () => const SearchFormScreen(), 47 | ), 48 | GetPage( 49 | name: '/recipe/detail', 50 | page: () => const DetailReciperScreen(), 51 | ), 52 | GetPage( 53 | name: '/upload/step/1', 54 | page: () => Step1Screen(), 55 | ), 56 | GetPage( 57 | name: '/upload/step/2', 58 | page: () => Step2Screen(), 59 | ), 60 | ]; 61 | } 62 | -------------------------------------------------------------------------------- /lib/src/ui/screens/auth/change_password_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:recipe_app/src/core/controllers/helper_controller.dart'; 5 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 6 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 7 | 8 | class ChangePasswordScreen extends StatelessWidget { 9 | final GlobalKey _formKey = GlobalKey(); 10 | 11 | ChangePasswordScreen({super.key}); 12 | @override 13 | Widget build(BuildContext context) { 14 | final passwordC = Get.put(PasswordController()); 15 | return Scaffold( 16 | backgroundColor: AppColors.bgColor, 17 | body: Obx( 18 | () => Center( 19 | child: SingleChildScrollView( 20 | child: Form( 21 | key: _formKey, 22 | child: Container( 23 | margin: const EdgeInsets.symmetric(horizontal: 24), 24 | child: Column( 25 | children: [ 26 | titleGreeting( 27 | title: 'Reset your password', 28 | subtitle: 'Please enter your new password', 29 | ), 30 | textfieldPassword( 31 | controller: passwordC.password, 32 | obsecure: passwordC.showPassword.value, 33 | hintText: 'Password', 34 | icon: SvgPicture.asset(AssetIcons.lock), 35 | onTap: () => passwordC.visiblePassword(), 36 | ), 37 | const Align( 38 | alignment: Alignment.topLeft, 39 | child: Text( 40 | 'Your Password must contain:', 41 | style: TextTypography.mP2, 42 | ), 43 | ), 44 | Container( 45 | margin: const EdgeInsets.only(top: 15), 46 | child: Column( 47 | children: [ 48 | itemContain( 49 | label: 'Atleast 8 characters', 50 | isOk: passwordC.eightChars.value), 51 | const SizedBox(height: 15), 52 | itemContain( 53 | label: 'Contains a number', 54 | isOk: passwordC.hasNumber.value), 55 | const SizedBox(height: 15), 56 | itemContain( 57 | label: 'Contains a special character', 58 | isOk: passwordC.hasSpecialCharacters.value), 59 | ], 60 | ), 61 | ), 62 | Container( 63 | margin: const EdgeInsets.only(top: 50, bottom: 24), 64 | child: Button( 65 | onPressed: () { 66 | if (_formKey.currentState!.validate()) { 67 | dialog( 68 | title: 'Recovery Success', 69 | subtitle: 'Your password has been changed', 70 | icon: Image.asset(AssetImages.emoticonParty), 71 | txtButton: 'Back to Login', 72 | onPressed: () { 73 | Get.offAllNamed('/auth/login'); 74 | }, 75 | ); 76 | } 77 | }, 78 | txtButton: 'Change', 79 | color: AppColors.primary, 80 | disable: passwordC.btnDisable.value, 81 | ), 82 | ), 83 | ], 84 | ), 85 | ), 86 | ), 87 | ), 88 | ), 89 | ), 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/src/ui/screens/auth/login_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | import 'package:fluttericon/font_awesome_icons.dart'; 4 | import 'package:get/get.dart'; 5 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 6 | import 'package:recipe_app/src/core/controllers/helper_controller.dart'; 7 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 8 | 9 | class LoginScreen extends StatelessWidget { 10 | final GlobalKey _formKey = GlobalKey(); 11 | 12 | LoginScreen({super.key}); 13 | @override 14 | Widget build(BuildContext context) { 15 | final loginC = Get.put(LoginController()); 16 | return Scaffold( 17 | backgroundColor: AppColors.bgColor, 18 | body: Center( 19 | child: SingleChildScrollView( 20 | child: Form( 21 | key: _formKey, 22 | child: Container( 23 | margin: const EdgeInsets.symmetric(horizontal: 24), 24 | child: Column( 25 | children: [ 26 | titleGreeting( 27 | title: 'Welcome Back!', 28 | subtitle: 'Please enter your account here', 29 | ), 30 | textfieldIcon( 31 | controller: loginC.username, 32 | hintText: 'Email or phone number', 33 | icon: SvgPicture.asset(AssetIcons.message), 34 | isRequired: 'Email or phone number is required !', 35 | ), 36 | Obx( 37 | () => textfieldPassword( 38 | controller: loginC.password, 39 | obsecure: loginC.showPassword.value, 40 | hintText: 'Password', 41 | isRequired: 'Password is required !', 42 | icon: SvgPicture.asset(AssetIcons.lock), 43 | onTap: () => loginC.visiblePassword(), 44 | ), 45 | ), 46 | InkWell( 47 | child: const Align( 48 | alignment: Alignment.centerRight, 49 | child: Text( 50 | 'Forgot password?', 51 | style: 52 | TextStyle(color: AppColors.mainText, fontSize: 15), 53 | ), 54 | ), 55 | onTap: () => Get.toNamed('/auth/password/recovery'), 56 | ), 57 | Container( 58 | margin: const EdgeInsets.only(top: 50, bottom: 24), 59 | child: Button( 60 | disable: false, 61 | onPressed: () { 62 | if (_formKey.currentState!.validate()) { 63 | Get.toNamed('/home'); 64 | } 65 | }, 66 | txtButton: 'Login', 67 | color: AppColors.primary, 68 | ), 69 | ), 70 | const Text('Or continue with', style: TextTypography.sP2), 71 | Container( 72 | margin: const EdgeInsets.only(top: 24, bottom: 24), 73 | child: ButtonIcon( 74 | onPressed: () {}, 75 | txtButton: 'Google', 76 | color: AppColors.secondary, 77 | icon: const Icon( 78 | FontAwesome.google, 79 | color: Colors.white, 80 | size: 20, 81 | ), 82 | ), 83 | ), 84 | richTextLink( 85 | title: 'Don’t have any account?', 86 | linkText: ' Sign up', 87 | onTap: () { 88 | Get.toNamed('/auth/register'); 89 | }, 90 | ) 91 | ], 92 | ), 93 | ), 94 | ), 95 | ), 96 | ), 97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /lib/src/ui/screens/auth/password_recovery_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:recipe_app/src/core/controllers/helper_controller.dart'; 5 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 6 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 7 | 8 | class PasswordRecoveryScreen extends StatelessWidget { 9 | final GlobalKey _formKey = GlobalKey(); 10 | 11 | PasswordRecoveryScreen({super.key}); 12 | @override 13 | Widget build(BuildContext context) { 14 | final passwordC = Get.put(PasswordController()); 15 | return Scaffold( 16 | backgroundColor: AppColors.bgColor, 17 | body: Center( 18 | child: SingleChildScrollView( 19 | child: Form( 20 | key: _formKey, 21 | child: Container( 22 | margin: const EdgeInsets.symmetric(horizontal: 24), 23 | child: Column( 24 | children: [ 25 | titleGreeting( 26 | title: 'Password recovery', 27 | subtitle: 'Enter your email to recover your password', 28 | ), 29 | textfieldIcon( 30 | controller: passwordC.username, 31 | hintText: 'Email address', 32 | icon: SvgPicture.asset(AssetIcons.message), 33 | isRequired: 'Email is required !', 34 | ), 35 | Container( 36 | margin: const EdgeInsets.only(top: 10), 37 | child: Button( 38 | disable: false, 39 | onPressed: () { 40 | if (_formKey.currentState!.validate()) { 41 | Get.toNamed('/auth/password/change'); 42 | } 43 | }, 44 | txtButton: 'Recovery', 45 | color: AppColors.primary, 46 | ), 47 | ), 48 | ], 49 | ), 50 | ), 51 | ), 52 | ), 53 | ), 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/ui/screens/auth/register_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:flutter_svg/flutter_svg.dart'; 4 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 5 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 6 | import 'package:recipe_app/src/core/controllers/helper_controller.dart'; 7 | 8 | class RegisterScreen extends StatelessWidget { 9 | final GlobalKey _formKey = GlobalKey(); 10 | 11 | RegisterScreen({super.key}); 12 | @override 13 | Widget build(BuildContext context) { 14 | final registerC = Get.put(RegisterController()); 15 | return Scaffold( 16 | backgroundColor: AppColors.bgColor, 17 | body: Obx( 18 | () => Center( 19 | child: SingleChildScrollView( 20 | child: Form( 21 | key: _formKey, 22 | child: Container( 23 | margin: const EdgeInsets.symmetric(horizontal: 24), 24 | child: Column( 25 | children: [ 26 | titleGreeting( 27 | title: 'Welcome!', 28 | subtitle: 'Please enter your account here', 29 | ), 30 | textfieldIcon( 31 | controller: registerC.username, 32 | hintText: 'Email or phone number', 33 | icon: SvgPicture.asset(AssetIcons.message), 34 | isRequired: 'Email or phone number is required !', 35 | ), 36 | textfieldPassword( 37 | controller: registerC.password, 38 | obsecure: registerC.showPassword.value, 39 | hintText: 'Password', 40 | icon: SvgPicture.asset(AssetIcons.lock), 41 | onTap: () => registerC.visiblePassword(), 42 | ), 43 | const Align( 44 | alignment: Alignment.topLeft, 45 | child: Text( 46 | 'Your Password must contain:', 47 | style: TextTypography.mP2, 48 | ), 49 | ), 50 | Container( 51 | margin: const EdgeInsets.only(top: 15), 52 | child: Column( 53 | children: [ 54 | itemContain( 55 | label: 'Atleast 8 characters', 56 | isOk: registerC.eightChars.value), 57 | const SizedBox(height: 15), 58 | itemContain( 59 | label: 'Contains a number', 60 | isOk: registerC.hasNumber.value), 61 | const SizedBox(height: 15), 62 | itemContain( 63 | label: 'Contains a special character', 64 | isOk: registerC.hasSpecialCharacters.value), 65 | ], 66 | ), 67 | ), 68 | Container( 69 | margin: const EdgeInsets.only(top: 50, bottom: 24), 70 | child: Button( 71 | onPressed: () { 72 | if (_formKey.currentState!.validate()) { 73 | Get.toNamed('/auth/verify'); 74 | } 75 | }, 76 | txtButton: 'Sign Up', 77 | color: AppColors.primary, 78 | disable: registerC.btnDisable.value, 79 | ), 80 | ), 81 | ], 82 | ), 83 | ), 84 | ), 85 | ), 86 | ), 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/src/ui/screens/auth/verify_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 4 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 5 | import 'package:recipe_app/src/core/controllers/helper_controller.dart'; 6 | 7 | class VerifyScreen extends StatelessWidget { 8 | const VerifyScreen({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | final verifyC = Get.put(VerifyController()); 13 | return Scaffold( 14 | backgroundColor: AppColors.bgColor, 15 | body: Obx( 16 | () => Center( 17 | child: SingleChildScrollView( 18 | child: Container( 19 | margin: const EdgeInsets.symmetric(horizontal: 24), 20 | child: Column( 21 | children: [ 22 | titleGreeting( 23 | title: 'Check your email', 24 | subtitle: 'We’ve sent the code to your email', 25 | ), 26 | textfieldOTP(context: context, length: 4), 27 | Row( 28 | mainAxisAlignment: MainAxisAlignment.center, 29 | children: [ 30 | const Text( 31 | 'code expires in: ', 32 | style: TextTypography.mP2, 33 | ), 34 | Text( 35 | verifyC.counter.value.toString(), 36 | style: const TextStyle(color: AppColors.secondary), 37 | ) 38 | ], 39 | ), 40 | Container( 41 | margin: const EdgeInsets.only(top: 50, bottom: 20), 42 | child: Button( 43 | onPressed: () { 44 | dialog( 45 | title: 'Register Success', 46 | subtitle: 47 | 'Your account has been registered, now you can login', 48 | icon: Image.asset(AssetImages.emoticonParty), 49 | txtButton: 'Back to Login', 50 | onPressed: () { 51 | Get.offAllNamed('/auth/login'); 52 | }, 53 | ); 54 | }, 55 | txtButton: 'Verify', 56 | color: AppColors.primary, 57 | ), 58 | ), 59 | ButtonOutline( 60 | onPressed: () { 61 | verifyC.startTimer(); 62 | }, 63 | disable: verifyC.timeStart.value, 64 | txtButton: 'Resend', 65 | color: AppColors.secondaryText, 66 | ), 67 | ], 68 | ), 69 | ), 70 | ), 71 | ), 72 | ), 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/ui/screens/demo.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:recipe_app/src/core/functions/network_image.dart'; 3 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 4 | 5 | class Sample2 extends StatelessWidget { 6 | const Sample2({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return SafeArea( 11 | child: Material( 12 | color: AppColors.bgColor, 13 | child: CustomScrollView( 14 | slivers: [ 15 | SliverPersistentHeader( 16 | delegate: MySliverAppBar( 17 | expandedHeight: 250, 18 | ), 19 | pinned: true, 20 | ), 21 | SliverList( 22 | delegate: SliverChildBuilderDelegate( 23 | (_, index) => ListTile( 24 | title: Text("Index: $index"), 25 | ), 26 | ), 27 | ) 28 | ], 29 | ), 30 | ), 31 | ); 32 | } 33 | } 34 | 35 | class MySliverAppBar extends SliverPersistentHeaderDelegate { 36 | final double expandedHeight; 37 | 38 | MySliverAppBar({required this.expandedHeight}); 39 | 40 | @override 41 | Widget build( 42 | BuildContext context, double shrinkOffset, bool overlapsContent) { 43 | return Stack( 44 | fit: StackFit.expand, 45 | children: [ 46 | Container( 47 | height: 500, 48 | color: AppColors.bgColor, 49 | margin: const EdgeInsets.only(top: 10, left: 16), 50 | child: Opacity( 51 | opacity: shrinkOffset / expandedHeight, 52 | child: Row( 53 | children: [ 54 | Container( 55 | margin: const EdgeInsets.only(right: 24), 56 | height: 46, 57 | width: 46, 58 | child: ClipRRect( 59 | borderRadius: BorderRadius.circular(100), 60 | child: const PNetworkImage( 61 | 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80', 62 | fit: BoxFit.cover, 63 | ), 64 | ), 65 | ), 66 | const Text("Food", style: TextTypography.mH1), 67 | ], 68 | ), 69 | ), 70 | ), 71 | Positioned( 72 | //top: expandedHeight / 2 - shrinkOffset, 73 | left: MediaQuery.of(context).size.width / 4, 74 | child: Opacity( 75 | opacity: (1 - shrinkOffset / expandedHeight), 76 | child: Container( 77 | height: 200, 78 | width: 200, 79 | margin: const EdgeInsets.only(top: 20), 80 | child: ClipRRect( 81 | borderRadius: BorderRadius.circular(100), 82 | child: const PNetworkImage( 83 | 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80', 84 | fit: BoxFit.cover, 85 | ), 86 | ), 87 | ), 88 | ), 89 | ), 90 | ], 91 | ); 92 | } 93 | 94 | @override 95 | double get maxExtent => expandedHeight; 96 | 97 | @override 98 | double get minExtent => kToolbarHeight; 99 | 100 | @override 101 | bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true; 102 | } 103 | -------------------------------------------------------------------------------- /lib/src/ui/screens/intro/onboarding_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 4 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 5 | 6 | class OnboardingScreen extends StatelessWidget { 7 | const OnboardingScreen({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Scaffold( 12 | backgroundColor: AppColors.bgColor, 13 | body: ListView( 14 | children: [ 15 | Column( 16 | children: [ 17 | Container( 18 | margin: const EdgeInsets.only(top: 15, bottom: 50), 19 | child: Image.asset( 20 | AssetImages.onboardingImg, 21 | width: SizeConfig().deviceWidth(context), 22 | fit: BoxFit.fitWidth, 23 | ), 24 | ), 25 | const Text( 26 | 'Start Cooking', 27 | style: TextTypography.mH1, 28 | ), 29 | Container( 30 | margin: const EdgeInsets.only(top: 16), 31 | padding: const EdgeInsets.symmetric(horizontal: 80), 32 | child: const Text( 33 | 'Let’s join our community to cook better food!', 34 | style: TextTypography.sP1, 35 | textAlign: TextAlign.center, 36 | ), 37 | ), 38 | Container( 39 | margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 50), 40 | child: Button( 41 | disable: false, 42 | onPressed: () { 43 | Get.toNamed('/auth/login'); 44 | }, 45 | txtButton: 'Get Started', 46 | color: AppColors.primary, 47 | ), 48 | ) 49 | ], 50 | ), 51 | ], 52 | ), 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/src/ui/screens/main/dashboard_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 3 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 4 | import '../recipe/popular_recipe_sceen.dart'; 5 | 6 | class DashboardScreen extends StatelessWidget { 7 | const DashboardScreen({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | List choice = ['All', 'Food', 'Drink']; 12 | 13 | return Scaffold( 14 | backgroundColor: AppColors.bgColor, 15 | body: Column( 16 | children: [ 17 | const SizedBox(height: 70), 18 | Container( 19 | margin: const EdgeInsets.symmetric(horizontal: 24), 20 | child: Column( 21 | crossAxisAlignment: CrossAxisAlignment.start, 22 | children: [ 23 | searchForm(context: context, redirect: true), 24 | Container( 25 | padding: const EdgeInsets.only(top: 24, bottom: 16), 26 | child: const Text('Category', style: TextTypography.mH2), 27 | ), 28 | buildFilter(choice), 29 | ], 30 | ), 31 | ), 32 | Container( 33 | margin: const EdgeInsets.symmetric(vertical: 24), 34 | child: divider(), 35 | ), 36 | DefaultTabController( 37 | length: 2, 38 | child: Expanded( 39 | child: Column( 40 | children: [ 41 | Container( 42 | decoration: const BoxDecoration( 43 | border: Border( 44 | bottom: 45 | BorderSide(width: 1.5, color: AppColors.outline), 46 | ), 47 | ), 48 | child: const TabBar( 49 | labelColor: AppColors.titleText, 50 | unselectedLabelColor: AppColors.secondaryText, 51 | indicatorColor: AppColors.primary, 52 | indicatorWeight: 3, 53 | labelStyle: TextStyle( 54 | fontSize: 15, 55 | fontWeight: FontWeight.w600, 56 | ), 57 | tabs: [Tab(text: "Popular"), Tab(text: "New")], 58 | ), 59 | ), 60 | Expanded( 61 | child: Container( 62 | margin: const EdgeInsets.symmetric(horizontal: 24), 63 | child: const TabBarView( 64 | children: [ 65 | PopularRecipeScreen(), 66 | PopularRecipeScreen(), 67 | ], 68 | ), 69 | ), 70 | ), 71 | ], 72 | ), 73 | ), 74 | ), 75 | ], 76 | ), 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/src/ui/screens/main/home_screen.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: deprecated_member_use 2 | 3 | import 'package:get/get.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_svg/flutter_svg.dart'; 6 | import 'package:recipe_app/src/ui/screens/user/profile_screen.dart'; 7 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 8 | import 'package:recipe_app/src/ui/screens/main/dashboard_screen.dart'; 9 | import 'package:recipe_app/src/ui/screens/upload/step1_screen.dart'; 10 | import 'package:recipe_app/src/ui/screens/notification/notification_screen.dart'; 11 | 12 | class HomeScreen extends StatelessWidget { 13 | const HomeScreen({super.key}); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | // List pages 18 | List pages = [ 19 | const DashboardScreen(), 20 | Step1Screen(), 21 | const DashboardScreen(), 22 | const NotificationScreen(), 23 | const ProfileScreen(), 24 | ]; 25 | final navC = Get.put(NavbarController()); 26 | return Obx( 27 | () => Scaffold( 28 | body: pages.elementAt(navC.index.value), 29 | floatingActionButtonLocation: const FixedCenterDockedFabLocation(), 30 | floatingActionButton: FloatingActionButton( 31 | backgroundColor: AppColors.primary, 32 | onPressed: () { 33 | navC.setIndex(2); 34 | }, 35 | elevation: 4.0, 36 | child: Container( 37 | margin: const EdgeInsets.all(15.0), 38 | child: SvgPicture.asset(AssetIcons.scan), 39 | ), 40 | ), 41 | bottomNavigationBar: BottomAppBar( 42 | clipBehavior: Clip.antiAliasWithSaveLayer, 43 | elevation: 0.5, 44 | shape: const CircularNotchedRectangle(), 45 | child: ClipRRect( 46 | borderRadius: BorderRadius.circular(10), 47 | child: Container( 48 | color: Colors.white, 49 | child: BottomNavigationBar( 50 | selectedItemColor: AppColors.primary, 51 | selectedFontSize: 12, 52 | currentIndex: navC.index.value, 53 | onTap: (index) { 54 | navC.setIndex(index); 55 | }, 56 | type: BottomNavigationBarType.fixed, 57 | items: [ 58 | BottomNavigationBarItem( 59 | label: "Home", 60 | icon: SvgPicture.asset( 61 | AssetIcons.home, 62 | color: navC.index.value == 0 63 | ? AppColors.primary 64 | : AppColors.secondaryText, 65 | ), 66 | ), 67 | BottomNavigationBarItem( 68 | label: "Upload", 69 | icon: SvgPicture.asset( 70 | AssetIcons.edit, 71 | color: navC.index.value == 1 72 | ? AppColors.primary 73 | : AppColors.secondaryText, 74 | ), 75 | ), 76 | const BottomNavigationBarItem( 77 | label: "Scan", 78 | icon: Icon( 79 | Icons.cancel, 80 | color: Colors.transparent, 81 | ), 82 | ), 83 | BottomNavigationBarItem( 84 | label: "Notification", 85 | icon: SvgPicture.asset( 86 | AssetIcons.notification, 87 | color: navC.index.value == 3 88 | ? AppColors.primary 89 | : AppColors.secondaryText, 90 | ), 91 | ), 92 | BottomNavigationBarItem( 93 | label: "Profile", 94 | icon: SvgPicture.asset( 95 | AssetIcons.profile, 96 | color: navC.index.value == 4 97 | ? AppColors.primary 98 | : AppColors.secondaryText, 99 | ), 100 | ), 101 | ], 102 | ), 103 | ), 104 | ), 105 | ), 106 | ), 107 | ); 108 | } 109 | } 110 | 111 | class FixedCenterDockedFabLocation extends StandardFabLocation 112 | with FabCenterOffsetX, FabDockedOffsetY { 113 | const FixedCenterDockedFabLocation(); 114 | 115 | @override 116 | String toString() => 'FloatingActionButtonLocation.fixedCenterDocked'; 117 | 118 | @override 119 | double getOffsetY( 120 | ScaffoldPrelayoutGeometry scaffoldGeometry, double adjustment) { 121 | final double contentBottom = scaffoldGeometry.contentBottom; 122 | final double bottomMinInset = scaffoldGeometry.minInsets.bottom; 123 | if (bottomMinInset > 0) { 124 | // Hide if there's a keyboard 125 | return contentBottom; 126 | } 127 | return super.getOffsetY(scaffoldGeometry, adjustment); 128 | } 129 | } 130 | 131 | class NavbarController extends GetxController { 132 | var index = 0.obs; 133 | void setIndex(int page) => index.value = page; 134 | } 135 | -------------------------------------------------------------------------------- /lib/src/ui/screens/notification/notification_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 3 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 4 | 5 | class NotificationScreen extends StatelessWidget { 6 | const NotificationScreen({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Scaffold( 11 | backgroundColor: AppColors.bgColor, 12 | body: Container( 13 | margin: const EdgeInsets.symmetric(horizontal: 24), 14 | child: ListView( 15 | children: [ 16 | Container( 17 | margin: const EdgeInsets.only(top: 24, bottom: 12), 18 | child: const Text('New', style: TextTypography.mH2), 19 | ), 20 | userActivity( 21 | name: 'Dean Winchester', 22 | activity: 'now following you', 23 | time: '1h', 24 | urlProfile: 25 | 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80', 26 | labelButton: 'Follow', 27 | ), 28 | Container( 29 | margin: const EdgeInsets.only(top: 24, bottom: 12), 30 | child: const Text('Today', style: TextTypography.mH2), 31 | ), 32 | postActivity( 33 | user1: 'John Steve', 34 | user2: 'Sam Winchester', 35 | activity: 'liked your recipe', 36 | time: '20 min', 37 | urlProfile1: 38 | 'https://images.unsplash.com/photo-1520341280432-4749d4d7bcf9', 39 | urlProfile2: 40 | 'https://images.unsplash.com/flagged/photo-1570612861542-284f4c12e75f', 41 | urlPostImg: 42 | 'https://images.unsplash.com/photo-1576618148400-f54bed99fcfd', 43 | ), 44 | userActivity( 45 | name: 'Dean Winchester', 46 | activity: 'now following you', 47 | time: '1h', 48 | urlProfile: 49 | 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80', 50 | labelButton: 'Followed', 51 | disable: true, 52 | ), 53 | postActivity( 54 | user1: 'John Steve', 55 | user2: 'Sam Winchester', 56 | activity: 'liked your recipe', 57 | time: '20 min', 58 | urlProfile1: 59 | 'https://images.unsplash.com/photo-1520341280432-4749d4d7bcf9', 60 | urlProfile2: 61 | 'https://images.unsplash.com/flagged/photo-1570612861542-284f4c12e75f', 62 | urlPostImg: 63 | 'https://images.unsplash.com/photo-1607478900766-efe13248b125', 64 | ), 65 | Container( 66 | margin: const EdgeInsets.only(top: 24, bottom: 12), 67 | child: const Text('Yesterday', style: TextTypography.mH2), 68 | ), 69 | userActivity( 70 | name: 'Dean Winchester', 71 | activity: 'now following you', 72 | time: '1h', 73 | urlProfile: 74 | 'https://images.unsplash.com/photo-1547425260-76bcadfb4f2c', 75 | labelButton: 'Follow', 76 | ), 77 | userActivity( 78 | name: 'Dean Winchester', 79 | activity: 'now following you', 80 | time: '1h', 81 | urlProfile: 82 | 'https://images.unsplash.com/photo-1520341280432-4749d4d7bcf9', 83 | labelButton: 'Followed', 84 | disable: true, 85 | ), 86 | ], 87 | ), 88 | ), 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/src/ui/screens/recipe/detail_recipe_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | import 'package:fluttericon/octicons_icons.dart'; 4 | import 'package:get/get.dart'; 5 | import 'package:recipe_app/src/core/functions/network_image.dart'; 6 | import 'package:recipe_app/src/core/models/helper_model.dart'; 7 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 8 | import 'package:recipe_app/src/ui/widgets/component_widget.dart'; 9 | 10 | class DetailReciperScreen extends StatelessWidget { 11 | const DetailReciperScreen({super.key}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | RecipeModel data = Get.arguments; 16 | List ingredients = ['4 Eggs', '1/2 Butter', '1 Liter Water']; 17 | 18 | return Scaffold( 19 | backgroundColor: Colors.white, 20 | body: CustomScrollView( 21 | slivers: [ 22 | SliverAppBar( 23 | expandedHeight: MediaQuery.of(context).size.height * 0.5, 24 | elevation: 0, 25 | snap: true, 26 | floating: true, 27 | stretch: true, 28 | backgroundColor: Colors.grey.shade50, 29 | flexibleSpace: FlexibleSpaceBar( 30 | stretchModes: const [ 31 | StretchMode.zoomBackground, 32 | ], 33 | background: PNetworkImage( 34 | data.imgCover, 35 | fit: BoxFit.cover, 36 | ), 37 | ), 38 | bottom: PreferredSize( 39 | preferredSize: const Size.fromHeight(45), 40 | child: Transform.translate( 41 | offset: const Offset(0, 1), 42 | child: Container( 43 | height: 45, 44 | decoration: const BoxDecoration( 45 | color: Colors.white, 46 | borderRadius: BorderRadius.only( 47 | topLeft: Radius.circular(30), 48 | topRight: Radius.circular(30), 49 | ), 50 | ), 51 | child: Center( 52 | child: Container( 53 | width: 50, 54 | height: 8, 55 | decoration: BoxDecoration( 56 | color: Colors.grey.shade300, 57 | borderRadius: BorderRadius.circular(10), 58 | ), 59 | ), 60 | ), 61 | ), 62 | ), 63 | ), 64 | ), 65 | SliverList( 66 | delegate: SliverChildListDelegate( 67 | [ 68 | Container( 69 | height: MediaQuery.of(context).size.height * 0.90, 70 | color: Colors.white, 71 | padding: 72 | const EdgeInsets.symmetric(horizontal: 20, vertical: 5), 73 | child: Column( 74 | crossAxisAlignment: CrossAxisAlignment.start, 75 | mainAxisSize: MainAxisSize.min, 76 | children: [ 77 | Text(data.title, style: TextTypography.mH2), 78 | const SizedBox( 79 | height: 5, 80 | ), 81 | Row( 82 | children: [ 83 | Text(data.category, style: TextTypography.category), 84 | Container( 85 | margin: const EdgeInsets.symmetric(horizontal: 5), 86 | child: const Icon( 87 | Octicons.primitive_dot, 88 | color: AppColors.secondaryText, 89 | size: 10, 90 | ), 91 | ), 92 | Text(data.duration, style: TextTypography.category), 93 | ], 94 | ), 95 | const SizedBox(height: 20), 96 | Row( 97 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 98 | children: [ 99 | Row( 100 | children: [ 101 | SizedBox( 102 | height: 32, 103 | width: 32, 104 | child: ClipRRect( 105 | borderRadius: BorderRadius.circular(100), 106 | child: PNetworkImage( 107 | data.imgAuthor, 108 | fit: BoxFit.cover, 109 | ), 110 | ), 111 | ), 112 | const SizedBox(width: 8), 113 | Text(data.author, style: TextTypography.mH3) 114 | ], 115 | ), 116 | Row( 117 | children: [ 118 | CircleAvatar( 119 | radius: 17, 120 | backgroundColor: AppColors.primary, 121 | child: SvgPicture.asset(AssetIcons.heart), 122 | ), 123 | const SizedBox(width: 8), 124 | const Text("273 Likes", style: TextTypography.mH3) 125 | ], 126 | ) 127 | ], 128 | ), 129 | Container( 130 | margin: const EdgeInsets.symmetric(vertical: 16), 131 | child: const Divider(thickness: 1), 132 | ), 133 | const Text('Description', style: TextTypography.mH3), 134 | Container( 135 | padding: const EdgeInsets.only(top: 8, bottom: 16), 136 | child: const Text( 137 | "Your recipe has been uploaded, you can see it on your profile. Your recipe has been uploaded, you can see it on your", 138 | style: TextTypography.sP2, 139 | ), 140 | ), 141 | const Divider(thickness: 1), 142 | Container( 143 | padding: const EdgeInsets.symmetric(vertical: 16), 144 | child: const Text('Ingredients', 145 | style: TextTypography.mH3), 146 | ), 147 | Expanded( 148 | child: ListView.builder( 149 | reverse: false, 150 | scrollDirection: Axis.vertical, 151 | itemCount: ingredients.length, 152 | padding: const EdgeInsets.only(bottom: 16), 153 | itemBuilder: (context, index) { 154 | return Container( 155 | margin: const EdgeInsets.only(bottom: 15), 156 | child: listItem(label: ingredients[index]), 157 | ); 158 | }, 159 | ), 160 | ), 161 | const Divider(thickness: 1), 162 | Container( 163 | padding: const EdgeInsets.symmetric(vertical: 16), 164 | child: const Text('Steps', style: TextTypography.mH3), 165 | ), 166 | Expanded( 167 | flex: 2, 168 | child: ListView.builder( 169 | reverse: false, 170 | scrollDirection: Axis.vertical, 171 | itemCount: ingredients.length, 172 | padding: const EdgeInsets.only(top: 16), 173 | itemBuilder: (context, index) { 174 | return Container( 175 | margin: const EdgeInsets.only(bottom: 25), 176 | child: Row( 177 | crossAxisAlignment: CrossAxisAlignment.start, 178 | children: [ 179 | stepNumber(number: index + 1), 180 | const SizedBox(width: 16), 181 | Flexible( 182 | child: Column( 183 | children: [ 184 | const Text( 185 | 'Your recipe has been uploaded, you can see it on your profile. Your recipe has been uploaded, you can see it on your', 186 | style: TextTypography.mP2, 187 | ), 188 | Container( 189 | height: 155, 190 | margin: 191 | const EdgeInsets.only(top: 16), 192 | child: ClipRRect( 193 | borderRadius: 194 | BorderRadius.circular(12), 195 | child: PNetworkImage( 196 | data.imgCover, 197 | fit: BoxFit.cover, 198 | ), 199 | ), 200 | ), 201 | ], 202 | ), 203 | ), 204 | ], 205 | ), 206 | ); 207 | }, 208 | ), 209 | ), 210 | ], 211 | ), 212 | ) 213 | ], 214 | ), 215 | ), 216 | ], 217 | ), 218 | ); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /lib/src/ui/screens/recipe/popular_recipe_sceen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:recipe_app/src/core/models/helper_model.dart'; 3 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 4 | 5 | class PopularRecipeScreen extends StatelessWidget { 6 | const PopularRecipeScreen({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | List recipes = [ 11 | RecipeModel( 12 | author: 'Calum Lewis', 13 | title: 'Cupcake', 14 | category: 'Food', 15 | duration: '>60 mins', 16 | favorite: false, 17 | imgAuthor: 18 | 'https://images.unsplash.com/flagged/photo-1570612861542-284f4c12e75f', 19 | imgCover: 20 | 'https://images.unsplash.com/photo-1535141192574-5d4897c12636', 21 | ), 22 | RecipeModel( 23 | author: 'Elena Shelby', 24 | title: 'Pancake', 25 | category: 'Food', 26 | duration: '>60 mins', 27 | favorite: true, 28 | imgAuthor: 29 | 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80', 30 | imgCover: 31 | 'https://images.unsplash.com/photo-1488477304112-4944851de03d', 32 | ), 33 | RecipeModel( 34 | author: 'Eilif Sonas', 35 | title: 'Gingerbread', 36 | category: 'Food', 37 | duration: '>60 mins', 38 | favorite: false, 39 | imgAuthor: 'https://images.unsplash.com/photo-1547425260-76bcadfb4f2c', 40 | imgCover: 41 | 'https://images.unsplash.com/photo-1576618148400-f54bed99fcfd', 42 | ), 43 | RecipeModel( 44 | author: 'John Priyadi', 45 | title: 'Quince Tart', 46 | category: 'Food', 47 | duration: '>60 mins', 48 | favorite: false, 49 | imgAuthor: 50 | 'https://images.unsplash.com/photo-1520341280432-4749d4d7bcf9', 51 | imgCover: 52 | 'https://images.unsplash.com/photo-1607478900766-efe13248b125', 53 | ), 54 | ]; 55 | return GridView.builder( 56 | shrinkWrap: true, 57 | gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( 58 | maxCrossAxisExtent: 200, 59 | mainAxisExtent: 260, 60 | childAspectRatio: 1, 61 | crossAxisSpacing: 20, 62 | mainAxisSpacing: 30, 63 | ), 64 | itemCount: recipes.length, 65 | itemBuilder: (BuildContext ctx, index) { 66 | return RecipeWidget(data: recipes[index]); 67 | }, 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/src/ui/screens/search/search_form_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:flutter_svg/flutter_svg.dart'; 4 | import 'package:recipe_app/src/core/controllers/helper_controller.dart'; 5 | import 'package:recipe_app/src/ui/screens/recipe/popular_recipe_sceen.dart'; 6 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 7 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 8 | 9 | class SearchFormScreen extends StatelessWidget { 10 | const SearchFormScreen({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | List choice = ['Sushi', 'Sandwich', 'Seafood', 'Fried rice']; 15 | //final filterC = Get.put(SearchController()); 16 | final SearchFormController filterC = Get.find(); 17 | return Scaffold( 18 | backgroundColor: AppColors.bgColor, 19 | body: Obx( 20 | () => ListView( 21 | children: [ 22 | const SizedBox(height: 20), 23 | Row( 24 | children: [ 25 | Container( 26 | margin: const EdgeInsets.symmetric(horizontal: 24), 27 | child: GestureDetector( 28 | child: const Icon(Icons.arrow_back_ios, size: 20), 29 | onTap: () { 30 | Get.back(); 31 | }, 32 | ), 33 | ), 34 | Flexible( 35 | child: searchForm(context: context), 36 | ), 37 | Container( 38 | margin: const EdgeInsets.symmetric(horizontal: 24), 39 | child: GestureDetector( 40 | child: SvgPicture.asset(AssetIcons.filter), 41 | onTap: () => bottomFilter(context), 42 | ), 43 | ) 44 | ], 45 | ), 46 | Container( 47 | margin: const EdgeInsets.symmetric(vertical: 24), 48 | child: divider(), 49 | ), 50 | filterC.isFilter.value 51 | ? Row( 52 | children: [ 53 | Expanded( 54 | child: Container( 55 | margin: const EdgeInsets.symmetric(horizontal: 24), 56 | child: const PopularRecipeScreen(), 57 | ), 58 | ), 59 | ], 60 | ) 61 | : Column( 62 | children: [ 63 | searchHistory(text: '${filterC.isFilter.value}'), 64 | searchHistory(text: 'Salad'), 65 | Container( 66 | margin: const EdgeInsets.symmetric(vertical: 24), 67 | child: divider(), 68 | ), 69 | const Padding( 70 | padding: EdgeInsets.only(left: 24), 71 | child: Align( 72 | alignment: Alignment.centerLeft, 73 | child: Text('Search suggestions', 74 | style: TextTypography.mH2), 75 | ), 76 | ), 77 | Container( 78 | margin: const EdgeInsets.symmetric(horizontal: 24), 79 | child: buildFilter(choice), 80 | ), 81 | ], 82 | ), 83 | ], 84 | ), 85 | ), 86 | ); 87 | } 88 | 89 | Future bottomFilter(BuildContext context) { 90 | List choice = ['All', 'Food', 'Drink']; 91 | final filterCtrl = Get.put(SearchFormController()); 92 | return Get.bottomSheet( 93 | Padding( 94 | padding: const EdgeInsets.symmetric(horizontal: 24), 95 | child: Column( 96 | crossAxisAlignment: CrossAxisAlignment.start, 97 | children: [ 98 | Align( 99 | alignment: Alignment.center, 100 | child: Container( 101 | padding: const EdgeInsets.symmetric(vertical: 25), 102 | child: const Text('Add a Filter', style: TextTypography.mH2), 103 | ), 104 | ), 105 | const Text('Category', style: TextTypography.mH2), 106 | buildFilter(choice), 107 | richLabel(title1: 'Cooking Duration', title2: ' (in minutes)'), 108 | const Row( 109 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 110 | children: [ 111 | Text('<10', style: TextTypography.p1Primary), 112 | Text('30', style: TextTypography.p1Primary), 113 | Text('>60', style: TextTypography.p1Primary) 114 | ], 115 | ), 116 | buildSlider(), 117 | const SizedBox(height: 35), 118 | Row( 119 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 120 | children: [ 121 | ButtonDefault( 122 | width: SizeConfig().deviceWidth(context) / 2.5, 123 | onPressed: () { 124 | Get.back(); 125 | }, 126 | txtButton: 'Cancel', 127 | ), 128 | Button( 129 | disable: false, 130 | width: SizeConfig().deviceWidth(context) / 2.5, 131 | onPressed: () { 132 | filterCtrl.filter(); 133 | Get.back(); 134 | }, 135 | txtButton: 'Done', 136 | color: AppColors.primary, 137 | ), 138 | ], 139 | ), 140 | ], 141 | ), 142 | ), 143 | elevation: 20.0, 144 | enableDrag: false, 145 | backgroundColor: Colors.white, 146 | shape: const RoundedRectangleBorder( 147 | borderRadius: BorderRadius.only( 148 | topLeft: Radius.circular(32.0), 149 | topRight: Radius.circular(32.0), 150 | ), 151 | ), 152 | ); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /lib/src/ui/screens/upload/step1_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:dotted_border/dotted_border.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:recipe_app/src/core/controllers/helper_controller.dart'; 5 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 6 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 7 | 8 | class Step1Screen extends StatelessWidget { 9 | final GlobalKey _formKey = GlobalKey(); 10 | 11 | Step1Screen({super.key}); 12 | @override 13 | Widget build(BuildContext context) { 14 | final step1C = Get.put(UploadController()); 15 | return Scaffold( 16 | backgroundColor: AppColors.bgColor, 17 | body: Form( 18 | key: _formKey, 19 | child: Container( 20 | margin: const EdgeInsets.symmetric(horizontal: 24), 21 | child: Column( 22 | children: [ 23 | const SizedBox(height: 70), 24 | pagination(currentPage: '1', nextPage: '2'), 25 | Expanded( 26 | child: ListView( 27 | children: [ 28 | Column( 29 | children: [ 30 | Container( 31 | margin: const EdgeInsets.only(top: 35), 32 | width: SizeConfig().deviceWidth(context) / 1.2, 33 | height: SizeConfig().deviceHeight(context) / 5, 34 | child: DottedBorder( 35 | borderType: BorderType.RRect, 36 | radius: const Radius.circular(12), 37 | strokeWidth: 2, 38 | padding: const EdgeInsets.all(6), 39 | dashPattern: const [5, 5, 5, 5], 40 | color: const Color(0xFFD0DBEA), 41 | child: ClipRRect( 42 | borderRadius: 43 | const BorderRadius.all(Radius.circular(12)), 44 | child: Center( 45 | child: GestureDetector( 46 | child: uploadIcon( 47 | title: 'Add Cover Photo', 48 | subtitle: '(Up to 2 Mb)', 49 | ), 50 | onTap: () {}, 51 | ), 52 | ), 53 | ), 54 | ), 55 | ), 56 | ], 57 | ), 58 | const SizedBox(height: 20), 59 | labelForm(label: 'Food Name'), 60 | textfield( 61 | controller: step1C.foodName, 62 | hintText: 'Enter food name', 63 | isRequired: 'Food name is required !'), 64 | labelForm(label: 'Description'), 65 | textarea( 66 | minLines: 3, 67 | controller: step1C.description, 68 | hintText: 'Tell a little about your food', 69 | isRequired: 'Description is required !', 70 | ), 71 | richLabel( 72 | title1: 'Cooking Duration', title2: ' (in minutes)'), 73 | const Row( 74 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 75 | children: [ 76 | Text('<10', style: TextTypography.p1Primary), 77 | Text('30', style: TextTypography.p1Primary), 78 | Text('>60', style: TextTypography.p1Primary) 79 | ], 80 | ), 81 | buildSlider(), 82 | Container( 83 | margin: const EdgeInsets.symmetric(vertical: 50), 84 | child: Button( 85 | disable: false, 86 | onPressed: () { 87 | if (_formKey.currentState!.validate()) { 88 | Get.toNamed('/upload/step/2'); 89 | } 90 | }, 91 | txtButton: 'Next', 92 | color: AppColors.primary, 93 | ), 94 | ), 95 | ], 96 | ), 97 | ), 98 | ], 99 | ), 100 | ), 101 | ), 102 | ); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /lib/src/ui/screens/upload/step2_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:recipe_app/src/core/controllers/helper_controller.dart'; 5 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 6 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 7 | 8 | class Step2Screen extends StatelessWidget { 9 | final GlobalKey _formKey = GlobalKey(); 10 | final step2C = Get.put(UploadController()); 11 | 12 | Step2Screen({super.key}); 13 | @override 14 | Widget build(BuildContext context) { 15 | return Scaffold( 16 | backgroundColor: AppColors.bgColor, 17 | body: Form( 18 | key: _formKey, 19 | child: Column( 20 | children: [ 21 | const SizedBox(height: 70), 22 | Container( 23 | margin: const EdgeInsets.symmetric(horizontal: 24), 24 | child: pagination(currentPage: '2', nextPage: '2'), 25 | ), 26 | Expanded( 27 | child: ListView( 28 | children: [ 29 | Container( 30 | margin: const EdgeInsets.only(left: 24, bottom: 10), 31 | child: labelForm(label: 'Ingredients'), 32 | ), 33 | ListTile( 34 | minLeadingWidth: 10, 35 | leading: SvgPicture.asset(AssetIcons.drag), 36 | title: textfield( 37 | controller: step2C.ingredient, 38 | hintText: 'Enter Ingredient', 39 | isRequired: 'Ingredient is required !', 40 | ), 41 | ), 42 | const SizedBox(height: 10), 43 | ListTile( 44 | minLeadingWidth: 10, 45 | leading: SvgPicture.asset(AssetIcons.drag), 46 | title: textfield( 47 | controller: step2C.ingredient, 48 | hintText: 'Enter Ingredient', 49 | isRequired: 'Ingredient is required !', 50 | ), 51 | ), 52 | Container( 53 | margin: const EdgeInsets.symmetric( 54 | horizontal: 15, vertical: 30), 55 | child: ButtonOutline( 56 | onPressed: () {}, 57 | color: AppColors.outline, 58 | colorLabel: AppColors.titleText, 59 | txtButton: '+ Ingredient', 60 | ), 61 | ), 62 | divider(), 63 | Container( 64 | margin: const EdgeInsets.only(left: 24, bottom: 10), 65 | child: labelForm(label: 'Steps'), 66 | ), 67 | ListTile( 68 | minLeadingWidth: 10, 69 | leading: stepNumber(number: 1), 70 | title: textarea( 71 | controller: step2C.ingredient, 72 | hintText: 'Tell a little about your food', 73 | minLines: 4, 74 | ), 75 | subtitle: Container( 76 | margin: const EdgeInsets.only(top: 8), 77 | padding: const EdgeInsets.symmetric(vertical: 10), 78 | decoration: const BoxDecoration( 79 | color: AppColors.form, 80 | borderRadius: BorderRadius.all( 81 | Radius.circular(8), 82 | ), 83 | ), 84 | child: SvgPicture.asset(AssetIcons.camera), 85 | ), 86 | ), 87 | Container( 88 | margin: const EdgeInsets.all(24), 89 | child: Row( 90 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 91 | children: [ 92 | ButtonDefault( 93 | width: SizeConfig().deviceWidth(context) / 2.5, 94 | onPressed: () { 95 | Get.back(); 96 | }, 97 | txtButton: 'Back', 98 | ), 99 | Button( 100 | disable: false, 101 | width: SizeConfig().deviceWidth(context) / 2.5, 102 | onPressed: () { 103 | dialog( 104 | title: 'Upload Success', 105 | subtitle: 106 | 'Your recipe has been uploaded, you can see it on your profile', 107 | icon: Image.asset(AssetImages.emoticonParty), 108 | txtButton: 'Back to Home', 109 | onPressed: () { 110 | Get.offAllNamed('/home'); 111 | }, 112 | ); 113 | }, 114 | txtButton: 'Done', 115 | color: AppColors.primary, 116 | ), 117 | ], 118 | ), 119 | ), 120 | ], 121 | ), 122 | ), 123 | ], 124 | ), 125 | ), 126 | ); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /lib/src/ui/screens/user/profile_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:recipe_app/src/core/functions/network_image.dart'; 3 | import 'package:recipe_app/src/ui/screens/user/user_recipe_screen.dart'; 4 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 5 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 6 | 7 | class ProfileScreen extends StatelessWidget { 8 | const ProfileScreen({Key? key}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Scaffold( 13 | body: Column( 14 | children: [ 15 | Align( 16 | alignment: Alignment.topRight, 17 | child: Container( 18 | margin: const EdgeInsets.only(right: 30, top: 70), 19 | child: const Icon( 20 | Icons.share, 21 | color: AppColors.mainText, 22 | ), 23 | ), 24 | ), 25 | Container( 26 | height: 100, 27 | width: 100, 28 | margin: const EdgeInsets.only(top: 20), 29 | child: ClipRRect( 30 | borderRadius: BorderRadius.circular(50), 31 | child: const PNetworkImage( 32 | 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80', 33 | fit: BoxFit.cover, 34 | ), 35 | ), 36 | ), 37 | Container( 38 | margin: const EdgeInsets.symmetric(vertical: 24), 39 | child: const Text('Elena Shelby', style: TextTypography.mH2), 40 | ), 41 | Row( 42 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 43 | children: [ 44 | countCategory(count: '32', label: 'Recipe'), 45 | countCategory(count: '782', label: 'Following'), 46 | countCategory(count: '1.287', label: 'Followers'), 47 | ], 48 | ), 49 | // Container( 50 | // margin: EdgeInsets.only(left: 52, right: 52, top: 32), 51 | // child: Button( 52 | // disable: false, 53 | // onPressed: () {}, 54 | // txtButton: 'Follow', 55 | // color: AppColors.primary, 56 | // ), 57 | // ), 58 | Container( 59 | margin: const EdgeInsets.symmetric(vertical: 24), 60 | child: divider(), 61 | ), 62 | DefaultTabController( 63 | length: 2, 64 | child: Expanded( 65 | child: Column( 66 | children: [ 67 | Container( 68 | decoration: const BoxDecoration( 69 | border: Border( 70 | bottom: 71 | BorderSide(width: 1.5, color: AppColors.outline), 72 | ), 73 | ), 74 | child: const TabBar( 75 | labelColor: AppColors.titleText, 76 | unselectedLabelColor: AppColors.secondaryText, 77 | indicatorColor: AppColors.primary, 78 | indicatorWeight: 3, 79 | labelStyle: TextStyle( 80 | fontSize: 15, 81 | fontWeight: FontWeight.w600, 82 | ), 83 | tabs: [Tab(text: "Recipes"), Tab(text: "Liked")], 84 | ), 85 | ), 86 | Expanded( 87 | child: Container( 88 | margin: const EdgeInsets.symmetric(horizontal: 24), 89 | child: const TabBarView( 90 | children: [ 91 | UserRecipeScreen(), 92 | UserRecipeScreen(), 93 | ], 94 | ), 95 | ), 96 | ), 97 | ], 98 | ), 99 | ), 100 | ), 101 | ], 102 | ), 103 | ); 104 | } 105 | } 106 | 107 | Widget countCategory({required String count, required String label}) { 108 | return Column( 109 | children: [ 110 | Text(count, style: TextTypography.mH2), 111 | Text(label, style: TextTypography.category), 112 | ], 113 | ); 114 | } 115 | -------------------------------------------------------------------------------- /lib/src/ui/screens/user/user_recipe_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:recipe_app/src/core/models/helper_model.dart'; 3 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 4 | 5 | class UserRecipeScreen extends StatelessWidget { 6 | const UserRecipeScreen({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | List recipes = [ 11 | RecipeModel( 12 | author: 'Calum Lewis', 13 | title: 'Cupcake', 14 | category: 'Food', 15 | duration: '>60 mins', 16 | favorite: false, 17 | imgAuthor: 18 | 'https://images.unsplash.com/flagged/photo-1570612861542-284f4c12e75f', 19 | imgCover: 20 | 'https://images.unsplash.com/photo-1535141192574-5d4897c12636', 21 | ), 22 | RecipeModel( 23 | author: 'Elena Shelby', 24 | title: 'Pancake', 25 | category: 'Food', 26 | duration: '>60 mins', 27 | favorite: true, 28 | imgAuthor: 29 | 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80', 30 | imgCover: 31 | 'https://images.unsplash.com/photo-1488477304112-4944851de03d', 32 | ), 33 | RecipeModel( 34 | author: 'Eilif Sonas', 35 | title: 'Gingerbread', 36 | category: 'Food', 37 | duration: '>60 mins', 38 | favorite: false, 39 | imgAuthor: 'https://images.unsplash.com/photo-1547425260-76bcadfb4f2c', 40 | imgCover: 41 | 'https://images.unsplash.com/photo-1576618148400-f54bed99fcfd', 42 | ), 43 | RecipeModel( 44 | author: 'John Priyadi', 45 | title: 'Quince Tart', 46 | category: 'Food', 47 | duration: '>60 mins', 48 | favorite: false, 49 | imgAuthor: 50 | 'https://images.unsplash.com/photo-1520341280432-4749d4d7bcf9', 51 | imgCover: 52 | 'https://images.unsplash.com/photo-1607478900766-efe13248b125', 53 | ), 54 | ]; 55 | return GridView.builder( 56 | shrinkWrap: true, 57 | gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( 58 | maxCrossAxisExtent: 200, 59 | mainAxisExtent: 220, 60 | childAspectRatio: 1, 61 | crossAxisSpacing: 20, 62 | mainAxisSpacing: 30, 63 | ), 64 | itemCount: recipes.length, 65 | itemBuilder: (BuildContext ctx, index) { 66 | return UserRecipe(data: recipes[index]); 67 | }, 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/src/ui/utils/assets_util.dart: -------------------------------------------------------------------------------- 1 | abstract class AssetImages { 2 | static const onboardingImg = 'assets/images/onboarding.png'; 3 | static const emoticonParty = 'assets/images/emoticon-party.png'; 4 | } 5 | 6 | abstract class AssetIcons { 7 | static const message = 'assets/icons/message.svg'; 8 | static const lock = 'assets/icons/lock.svg'; 9 | static const checkGreen = 'assets/icons/check-green.svg'; 10 | static const checkGrey = 'assets/icons/check-grey.svg'; 11 | static const home = 'assets/icons/home.svg'; 12 | static const profile = 'assets/icons/profile.svg'; 13 | static const notification = 'assets/icons/notification.svg'; 14 | static const edit = 'assets/icons/edit.svg'; 15 | static const scan = 'assets/icons/scan.svg'; 16 | static const search = 'assets/icons/search.svg'; 17 | static const filter = 'assets/icons/filter.svg'; 18 | static const arrowUpward = 'assets/icons/arrow-upward.svg'; 19 | static const heart = 'assets/icons/heart.svg'; 20 | static const image = 'assets/icons/image.svg'; 21 | static const drag = 'assets/icons/drag-icon.svg'; 22 | static const camera = 'assets/icons/camera.svg'; 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/ui/utils/colors_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppColors { 4 | static const primary = Color(0xFF1FCC79); 5 | static const secondary = Color(0xFFFF6464); 6 | static const mainText = Color(0xFF2E3E5C); 7 | static const secondaryText = Color(0xFF9FA5C0); 8 | static const bgColor = Color(0xFFFFFFFF); 9 | static const outline = Color(0xFFD0DBEA); 10 | static const form = Color(0xFFF4F5F7); 11 | static const titleText = Color(0xFF3E5481); 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/ui/utils/helper_util.dart: -------------------------------------------------------------------------------- 1 | export 'colors_util.dart'; 2 | export 'layout_util.dart'; 3 | export 'assets_util.dart'; 4 | export 'typography_util.dart'; 5 | -------------------------------------------------------------------------------- /lib/src/ui/utils/layout_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SizeConfig { 4 | double deviceWidth(BuildContext context) { 5 | return MediaQuery.of(context).size.width; 6 | } 7 | 8 | double deviceHeight(BuildContext context) { 9 | return MediaQuery.of(context).size.height; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/ui/utils/typography_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'helper_util.dart'; 3 | 4 | class TextTypography { 5 | static const TextStyle mH1 = TextStyle( 6 | color: AppColors.mainText, 7 | fontWeight: FontWeight.w700, 8 | fontSize: 22.0, 9 | ); 10 | 11 | static const TextStyle mH2 = TextStyle( 12 | color: AppColors.titleText, 13 | fontSize: 17, 14 | fontWeight: FontWeight.w700, 15 | ); 16 | 17 | static const TextStyle mH2_500 = TextStyle( 18 | color: AppColors.titleText, 19 | fontSize: 17, 20 | fontWeight: FontWeight.w500, 21 | ); 22 | 23 | static const TextStyle mH3 = TextStyle( 24 | color: AppColors.titleText, 25 | fontSize: 15, 26 | fontWeight: FontWeight.w700, 27 | ); 28 | 29 | static const TextStyle mP1 = TextStyle( 30 | color: AppColors.mainText, 31 | fontWeight: FontWeight.w500, 32 | fontSize: 17.0, 33 | ); 34 | 35 | static const TextStyle sP1 = TextStyle( 36 | color: AppColors.secondaryText, 37 | fontWeight: FontWeight.w500, 38 | fontSize: 17.0, 39 | ); 40 | 41 | static const TextStyle mP2 = TextStyle( 42 | color: AppColors.mainText, 43 | fontWeight: FontWeight.w500, 44 | fontSize: 15.0, 45 | ); 46 | 47 | static const TextStyle sP2 = TextStyle( 48 | color: AppColors.secondaryText, 49 | fontWeight: FontWeight.w500, 50 | fontSize: 15.0, 51 | ); 52 | 53 | static const TextStyle category = TextStyle( 54 | fontSize: 12, 55 | color: AppColors.secondaryText, 56 | ); 57 | 58 | static const TextStyle p1Primary = TextStyle( 59 | color: AppColors.primary, 60 | fontWeight: FontWeight.w700, 61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/ui/widgets/button_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:recipe_app/src/ui/utils/colors_util.dart'; 3 | 4 | class Button extends StatelessWidget { 5 | final String txtButton; 6 | final Color color; 7 | final GestureTapCallback onPressed; 8 | final bool disable; 9 | final double width; 10 | 11 | const Button( 12 | {super.key, 13 | required this.onPressed, 14 | required this.txtButton, 15 | required this.color, 16 | this.disable = false, 17 | this.width = double.infinity}); 18 | @override 19 | Widget build(BuildContext context) { 20 | final ButtonStyle flatButtonStyle = TextButton.styleFrom( 21 | backgroundColor: disable ? AppColors.form : color, 22 | shape: const RoundedRectangleBorder( 23 | borderRadius: BorderRadius.all( 24 | Radius.circular(32), 25 | ), 26 | ), 27 | ); 28 | return SizedBox( 29 | width: width, 30 | child: TextButton( 31 | style: flatButtonStyle, 32 | child: Padding( 33 | padding: const EdgeInsets.symmetric(vertical: 10.0), 34 | child: Row( 35 | mainAxisSize: MainAxisSize.min, 36 | children: [ 37 | Text( 38 | txtButton, 39 | maxLines: 1, 40 | style: TextStyle( 41 | color: disable ? AppColors.secondaryText : Colors.white, 42 | fontWeight: FontWeight.w700, 43 | fontSize: 15, 44 | ), 45 | ), 46 | ], 47 | ), 48 | ), 49 | onPressed: () { 50 | if (!disable) { 51 | onPressed(); 52 | } 53 | }, 54 | ), 55 | ); 56 | } 57 | } 58 | 59 | class ButtonDefault extends StatelessWidget { 60 | final String txtButton; 61 | final GestureTapCallback onPressed; 62 | final double width; 63 | 64 | const ButtonDefault( 65 | {super.key, 66 | required this.onPressed, 67 | required this.txtButton, 68 | this.width = double.infinity}); 69 | @override 70 | Widget build(BuildContext context) { 71 | final ButtonStyle flatButtonStyle = TextButton.styleFrom( 72 | backgroundColor: AppColors.form, 73 | shape: const RoundedRectangleBorder( 74 | borderRadius: BorderRadius.all( 75 | Radius.circular(32), 76 | ), 77 | ), 78 | ); 79 | return SizedBox( 80 | width: width, 81 | child: TextButton( 82 | style: flatButtonStyle, 83 | child: Padding( 84 | padding: const EdgeInsets.symmetric(vertical: 10.0), 85 | child: Row( 86 | mainAxisSize: MainAxisSize.min, 87 | children: [ 88 | Text( 89 | txtButton, 90 | maxLines: 1, 91 | style: const TextStyle( 92 | color: AppColors.mainText, 93 | fontWeight: FontWeight.w700, 94 | fontSize: 15, 95 | ), 96 | ), 97 | ], 98 | ), 99 | ), 100 | onPressed: () { 101 | onPressed(); 102 | }, 103 | ), 104 | ); 105 | } 106 | } 107 | 108 | class ButtonIcon extends StatelessWidget { 109 | final String txtButton; 110 | final Color color; 111 | final GestureTapCallback onPressed; 112 | final double width; 113 | final Widget icon; 114 | 115 | const ButtonIcon( 116 | {super.key, 117 | required this.onPressed, 118 | required this.txtButton, 119 | required this.color, 120 | this.width = double.infinity, 121 | required this.icon}); 122 | @override 123 | Widget build(BuildContext context) { 124 | final ButtonStyle flatButtonStyle = TextButton.styleFrom( 125 | backgroundColor: color, 126 | shape: const RoundedRectangleBorder( 127 | borderRadius: BorderRadius.all( 128 | Radius.circular(32), 129 | ), 130 | ), 131 | ); 132 | return SizedBox( 133 | width: width, 134 | child: TextButton( 135 | style: flatButtonStyle, 136 | child: Padding( 137 | padding: const EdgeInsets.symmetric(vertical: 10.0), 138 | child: Row( 139 | mainAxisSize: MainAxisSize.min, 140 | children: [ 141 | Container(child: icon), 142 | const SizedBox(width: 8), 143 | Text( 144 | txtButton, 145 | maxLines: 1, 146 | style: const TextStyle( 147 | color: Colors.white, 148 | fontWeight: FontWeight.w700, 149 | fontSize: 15, 150 | ), 151 | ), 152 | ], 153 | ), 154 | ), 155 | onPressed: () { 156 | onPressed(); 157 | }, 158 | ), 159 | ); 160 | } 161 | } 162 | 163 | class ButtonOutline extends StatelessWidget { 164 | final String txtButton; 165 | final Color color; 166 | final Color colorLabel; 167 | final GestureTapCallback onPressed; 168 | final bool disable; 169 | final double width; 170 | 171 | const ButtonOutline( 172 | {super.key, 173 | required this.onPressed, 174 | required this.txtButton, 175 | required this.color, 176 | this.disable = false, 177 | this.colorLabel = AppColors.outline, 178 | this.width = double.infinity}); 179 | @override 180 | Widget build(BuildContext context) { 181 | final ButtonStyle flatButtonStyle = TextButton.styleFrom( 182 | //backgroundColor: disable ? AppColors.form : color, 183 | shape: RoundedRectangleBorder( 184 | borderRadius: const BorderRadius.all( 185 | Radius.circular(32), 186 | ), 187 | side: 188 | BorderSide(color: disable ? AppColors.outline : color, width: 1.5), 189 | ), 190 | ); 191 | return SizedBox( 192 | width: width, 193 | child: TextButton( 194 | style: flatButtonStyle, 195 | child: Padding( 196 | padding: const EdgeInsets.symmetric(vertical: 10.0), 197 | child: Row( 198 | mainAxisSize: MainAxisSize.min, 199 | children: [ 200 | Text( 201 | txtButton, 202 | maxLines: 1, 203 | style: TextStyle( 204 | color: disable ? AppColors.outline : colorLabel, 205 | fontWeight: FontWeight.w700, 206 | fontSize: 15, 207 | ), 208 | ), 209 | ], 210 | ), 211 | ), 212 | onPressed: () { 213 | if (!disable) { 214 | onPressed(); 215 | } 216 | }, 217 | ), 218 | ); 219 | } 220 | } 221 | 222 | class ButtonSM extends StatelessWidget { 223 | final String txtButton; 224 | final Color color; 225 | final GestureTapCallback onPressed; 226 | final bool disable; 227 | 228 | const ButtonSM({ 229 | super.key, 230 | required this.onPressed, 231 | required this.txtButton, 232 | required this.color, 233 | this.disable = false, 234 | }); 235 | @override 236 | Widget build(BuildContext context) { 237 | final ButtonStyle flatButtonStyle = TextButton.styleFrom( 238 | backgroundColor: disable ? AppColors.form : color, 239 | shape: const RoundedRectangleBorder( 240 | borderRadius: BorderRadius.all( 241 | Radius.circular(32), 242 | ), 243 | ), 244 | ); 245 | return TextButton( 246 | style: flatButtonStyle, 247 | child: Padding( 248 | padding: const EdgeInsets.symmetric(horizontal: 10), 249 | child: Row( 250 | mainAxisSize: MainAxisSize.min, 251 | children: [ 252 | Text( 253 | txtButton, 254 | maxLines: 1, 255 | style: TextStyle( 256 | color: disable ? AppColors.mainText : Colors.white, 257 | fontWeight: FontWeight.w500, 258 | fontSize: 12, 259 | ), 260 | ), 261 | ], 262 | ), 263 | ), 264 | onPressed: () { 265 | if (!disable) { 266 | onPressed(); 267 | } 268 | }, 269 | ); 270 | } 271 | } 272 | 273 | Widget btnNavbar({required Widget icon, required String title}) { 274 | return Column( 275 | children: [ 276 | IconButton( 277 | onPressed: () {}, 278 | iconSize: 27.0, 279 | icon: icon, 280 | ), 281 | Text(title), 282 | ], 283 | ); 284 | } 285 | -------------------------------------------------------------------------------- /lib/src/ui/widgets/component_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/gestures.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_svg/svg.dart'; 4 | import 'package:fluttericon/entypo_icons.dart'; 5 | import 'package:get/get.dart'; 6 | import 'package:recipe_app/src/core/controllers/helper_controller.dart'; 7 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 8 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 9 | 10 | Widget richTextLink( 11 | {required String title, 12 | required String linkText, 13 | required GestureTapCallback onTap}) { 14 | return RichText( 15 | text: TextSpan( 16 | text: title, 17 | style: const TextStyle( 18 | fontWeight: FontWeight.w500, 19 | fontSize: 15.0, 20 | color: AppColors.mainText, 21 | ), 22 | children: [ 23 | TextSpan( 24 | text: ' $linkText', 25 | style: const TextStyle( 26 | fontWeight: FontWeight.w700, 27 | fontSize: 15.0, 28 | color: AppColors.primary, 29 | ), 30 | recognizer: TapGestureRecognizer() 31 | ..onTap = () { 32 | onTap(); 33 | }, 34 | ) 35 | ], 36 | ), 37 | ); 38 | } 39 | 40 | Widget itemContain({required String label, required bool isOk}) { 41 | return Row( 42 | children: [ 43 | isOk 44 | ? SvgPicture.asset(AssetIcons.checkGreen) 45 | : SvgPicture.asset(AssetIcons.checkGrey), 46 | const SizedBox(width: 8), 47 | Text( 48 | label, 49 | style: TextStyle( 50 | color: isOk ? AppColors.mainText : AppColors.secondaryText), 51 | ), 52 | ], 53 | ); 54 | } 55 | 56 | Widget listItem({required String label}) { 57 | return Row( 58 | mainAxisSize: MainAxisSize.min, 59 | children: [ 60 | SvgPicture.asset(AssetIcons.checkGreen), 61 | const SizedBox(width: 8), 62 | Text( 63 | label, 64 | style: TextTypography.mP2, 65 | ), 66 | ], 67 | ); 68 | } 69 | 70 | Widget titleGreeting({required String title, required String subtitle}) { 71 | return Column( 72 | children: [ 73 | const SizedBox(height: 50), 74 | Text( 75 | title, 76 | style: TextTypography.mH1, 77 | ), 78 | Container( 79 | margin: const EdgeInsets.only(top: 8), 80 | child: Text( 81 | subtitle, 82 | style: TextTypography.sP2, 83 | textAlign: TextAlign.center, 84 | ), 85 | ), 86 | const SizedBox(height: 32), 87 | ], 88 | ); 89 | } 90 | 91 | Future dialog( 92 | {required String title, 93 | required String subtitle, 94 | required Widget icon, 95 | required String txtButton, 96 | required GestureTapCallback onPressed}) { 97 | return Get.defaultDialog( 98 | title: '', 99 | contentPadding: const EdgeInsets.symmetric(horizontal: 20), 100 | content: Column( 101 | children: [ 102 | icon, 103 | Container( 104 | padding: const EdgeInsets.only(top: 32, bottom: 8), 105 | child: Text( 106 | title, 107 | style: TextTypography.mH1, 108 | ), 109 | ), 110 | Text( 111 | subtitle, 112 | style: TextTypography.mP2, 113 | textAlign: TextAlign.center, 114 | ), 115 | Container( 116 | margin: const EdgeInsets.symmetric(vertical: 24), 117 | child: Button( 118 | onPressed: () { 119 | onPressed(); 120 | }, 121 | txtButton: txtButton, 122 | color: AppColors.primary, 123 | ), 124 | ), 125 | ], 126 | ), 127 | ); 128 | } 129 | 130 | Widget buildFilter(List choice) { 131 | final controller = Get.put(OtherController()); 132 | return Obx( 133 | () => Wrap( 134 | children: List.generate( 135 | choice.length, 136 | (int index) { 137 | return Container( 138 | margin: const EdgeInsets.only(right: 12, top: 16), 139 | child: ChoiceChip( 140 | pressElevation: 0, 141 | showCheckmark: false, 142 | side: BorderSide.none, 143 | backgroundColor: AppColors.form, 144 | selectedColor: AppColors.primary, 145 | labelStyle: controller.choice.value == index 146 | ? const TextStyle( 147 | color: Colors.white, fontWeight: FontWeight.w700) 148 | : const TextStyle( 149 | color: AppColors.secondaryText, 150 | fontWeight: FontWeight.w500, 151 | ), 152 | labelPadding: 153 | const EdgeInsets.symmetric(horizontal: 14, vertical: 5), 154 | label: Text(choice[index]), 155 | selected: controller.choice.value == index, 156 | onSelected: (bool selected) { 157 | controller.setChoice(index); 158 | }, 159 | ), 160 | ); 161 | }, 162 | ).toList(), 163 | ), 164 | ); 165 | } 166 | 167 | Widget buildSlider() { 168 | final filterC = Get.put(OtherController()); 169 | return SliderTheme( 170 | data: SliderThemeData( 171 | trackShape: CustomTrackShape(), 172 | ), 173 | child: Obx( 174 | () => Slider( 175 | value: filterC.sliderValue.value, 176 | min: 0, 177 | max: 60, 178 | activeColor: AppColors.primary, 179 | inactiveColor: AppColors.form, 180 | label: filterC.sliderValue.value.round().toString(), 181 | onChanged: (double value) { 182 | filterC.setSlider(value); 183 | }, 184 | ), 185 | ), 186 | ); 187 | } 188 | 189 | class CustomTrackShape extends RoundedRectSliderTrackShape { 190 | @override 191 | Rect getPreferredRect({ 192 | required RenderBox parentBox, 193 | Offset offset = Offset.zero, 194 | required SliderThemeData sliderTheme, 195 | bool isEnabled = false, 196 | bool isDiscrete = false, 197 | }) { 198 | const double trackHeight = 3; 199 | final double trackLeft = offset.dx; 200 | final double trackTop = 201 | offset.dy + (parentBox.size.height - trackHeight) / 2; 202 | final double trackWidth = parentBox.size.width; 203 | return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight); 204 | } 205 | } 206 | 207 | Widget divider() { 208 | return Container(color: AppColors.form, height: 8); 209 | } 210 | 211 | Widget searchHistory({required String text}) { 212 | return ListTile( 213 | dense: true, 214 | leading: const Icon(Entypo.clock, color: AppColors.secondaryText), 215 | title: Text(text, style: TextTypography.mH2_500), 216 | trailing: SvgPicture.asset(AssetIcons.arrowUpward), 217 | ); 218 | } 219 | 220 | Widget uploadIcon({required String title, required String subtitle}) { 221 | return Column( 222 | mainAxisAlignment: MainAxisAlignment.center, 223 | children: [ 224 | SvgPicture.asset(AssetIcons.image), 225 | Container( 226 | margin: const EdgeInsets.symmetric(vertical: 5), 227 | child: Text( 228 | title, 229 | style: const TextStyle( 230 | fontSize: 15, 231 | fontWeight: FontWeight.w700, 232 | color: AppColors.titleText, 233 | ), 234 | ), 235 | ), 236 | Text( 237 | subtitle, 238 | style: const TextStyle( 239 | fontSize: 12, 240 | color: AppColors.secondaryText, 241 | ), 242 | ) 243 | ], 244 | ); 245 | } 246 | 247 | Widget pagination({required String currentPage, required String nextPage}) { 248 | return Row( 249 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 250 | children: [ 251 | InkWell( 252 | onTap: () => Get.back(), 253 | child: const Text( 254 | 'Cancel', 255 | style: TextStyle( 256 | fontSize: 17, 257 | color: AppColors.secondary, 258 | fontWeight: FontWeight.w700, 259 | ), 260 | ), 261 | ), 262 | RichText( 263 | text: TextSpan( 264 | text: '$currentPage/', 265 | style: const TextStyle( 266 | fontSize: 17, 267 | fontWeight: FontWeight.w700, 268 | color: AppColors.titleText, 269 | ), 270 | children: [ 271 | TextSpan( 272 | text: nextPage, 273 | style: const TextStyle( 274 | fontSize: 17, 275 | fontWeight: FontWeight.w700, 276 | color: AppColors.secondaryText, 277 | ), 278 | ) 279 | ], 280 | ), 281 | ), 282 | ], 283 | ); 284 | } 285 | 286 | Widget stepNumber({required int number}) { 287 | return CircleAvatar( 288 | radius: 15, 289 | backgroundColor: AppColors.mainText, 290 | child: Text( 291 | number.toString(), 292 | style: const TextStyle( 293 | color: Colors.white, 294 | fontSize: 12, 295 | ), 296 | ), 297 | ); 298 | } 299 | -------------------------------------------------------------------------------- /lib/src/ui/widgets/form_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | import 'package:fluttericon/mfg_labs_icons.dart'; 4 | import 'package:get/get.dart'; 5 | import 'package:pin_code_fields/pin_code_fields.dart'; 6 | import 'package:recipe_app/src/core/controllers/helper_controller.dart'; 7 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 8 | 9 | Widget textfieldIcon({ 10 | String hintText = '', 11 | String isRequired = '', 12 | required Widget icon, 13 | required TextEditingController controller, 14 | }) { 15 | return Padding( 16 | padding: const EdgeInsets.only(bottom: 25), 17 | child: Column( 18 | crossAxisAlignment: CrossAxisAlignment.start, 19 | children: [ 20 | TextFormField( 21 | controller: controller, 22 | decoration: InputDecoration( 23 | isDense: true, 24 | hintText: hintText, 25 | hintStyle: 26 | const TextStyle(fontSize: 15, color: AppColors.secondaryText), 27 | prefixIcon: Padding( 28 | padding: const EdgeInsets.symmetric( 29 | vertical: 5, 30 | horizontal: 15, 31 | ), 32 | child: icon, 33 | ), 34 | border: const OutlineInputBorder( 35 | borderRadius: BorderRadius.all(Radius.circular(32)), 36 | borderSide: BorderSide(color: AppColors.outline, width: 1), 37 | ), 38 | enabledBorder: const OutlineInputBorder( 39 | borderRadius: BorderRadius.all(Radius.circular(32)), 40 | borderSide: BorderSide(color: AppColors.outline, width: 1), 41 | ), 42 | focusedBorder: const OutlineInputBorder( 43 | borderRadius: BorderRadius.all(Radius.circular(32.0)), 44 | borderSide: BorderSide(color: AppColors.primary, width: 1.5), 45 | ), 46 | errorBorder: const OutlineInputBorder( 47 | borderRadius: BorderRadius.all(Radius.circular(32.0)), 48 | borderSide: BorderSide(color: AppColors.secondary, width: 1), 49 | ), 50 | ), 51 | validator: (value) { 52 | if (isRequired != '') { 53 | if (value!.isEmpty) { 54 | return isRequired; 55 | } 56 | } 57 | return null; 58 | }, 59 | ), 60 | ], 61 | ), 62 | ); 63 | } 64 | 65 | Widget textfieldPassword({ 66 | String hintText = '', 67 | String isRequired = '', 68 | required Widget icon, 69 | required bool obsecure, 70 | required TextEditingController controller, 71 | required GestureTapCallback onTap, 72 | }) { 73 | return Padding( 74 | padding: const EdgeInsets.only(bottom: 25), 75 | child: Column( 76 | crossAxisAlignment: CrossAxisAlignment.start, 77 | children: [ 78 | TextFormField( 79 | controller: controller, 80 | obscureText: obsecure, 81 | decoration: InputDecoration( 82 | isDense: true, 83 | hintText: hintText, 84 | hintStyle: 85 | const TextStyle(fontSize: 15, color: AppColors.secondaryText), 86 | prefixIcon: Padding( 87 | padding: const EdgeInsets.symmetric( 88 | vertical: 5, 89 | horizontal: 15, 90 | ), 91 | child: icon, 92 | ), 93 | suffixIcon: InkWell( 94 | child: Padding( 95 | padding: const EdgeInsets.symmetric( 96 | vertical: 5, 97 | horizontal: 15, 98 | ), 99 | child: Icon( 100 | obsecure 101 | ? Icons.visibility_outlined 102 | : Icons.visibility_off_outlined, 103 | size: 24, 104 | color: AppColors.secondaryText, 105 | ), 106 | ), 107 | onTap: () { 108 | onTap(); 109 | }, 110 | ), 111 | border: const OutlineInputBorder( 112 | borderRadius: BorderRadius.all(Radius.circular(32)), 113 | borderSide: BorderSide(color: AppColors.outline, width: 1), 114 | ), 115 | enabledBorder: const OutlineInputBorder( 116 | borderRadius: BorderRadius.all(Radius.circular(32)), 117 | borderSide: BorderSide(color: AppColors.outline, width: 1), 118 | ), 119 | focusedBorder: const OutlineInputBorder( 120 | borderRadius: BorderRadius.all(Radius.circular(32.0)), 121 | borderSide: BorderSide(color: AppColors.primary, width: 1.5), 122 | ), 123 | errorBorder: const OutlineInputBorder( 124 | borderRadius: BorderRadius.all(Radius.circular(32.0)), 125 | borderSide: BorderSide(color: AppColors.secondary, width: 1), 126 | ), 127 | ), 128 | validator: (value) { 129 | if (isRequired != '') { 130 | if (value!.isEmpty) { 131 | return isRequired; 132 | } 133 | } 134 | return null; 135 | }, 136 | ), 137 | ], 138 | ), 139 | ); 140 | } 141 | 142 | Widget passwordRegister({ 143 | String hintText = '', 144 | String isRequired = '', 145 | required Widget icon, 146 | required bool obsecure, 147 | required TextEditingController controller, 148 | required GestureTapCallback onTap, 149 | }) { 150 | return Padding( 151 | padding: const EdgeInsets.only(bottom: 25), 152 | child: Column( 153 | crossAxisAlignment: CrossAxisAlignment.start, 154 | children: [ 155 | TextFormField( 156 | controller: controller, 157 | obscureText: obsecure, 158 | decoration: InputDecoration( 159 | isDense: true, 160 | hintText: hintText, 161 | hintStyle: 162 | const TextStyle(fontSize: 15, color: AppColors.secondaryText), 163 | prefixIcon: Padding( 164 | padding: const EdgeInsets.symmetric( 165 | vertical: 5, 166 | horizontal: 15, 167 | ), 168 | child: icon, 169 | ), 170 | suffixIcon: InkWell( 171 | child: Padding( 172 | padding: const EdgeInsets.symmetric( 173 | vertical: 5, 174 | horizontal: 15, 175 | ), 176 | child: Icon( 177 | obsecure 178 | ? Icons.visibility_outlined 179 | : Icons.visibility_off_outlined, 180 | size: 24, 181 | color: AppColors.secondaryText, 182 | ), 183 | ), 184 | onTap: () { 185 | onTap(); 186 | }, 187 | ), 188 | border: const OutlineInputBorder( 189 | borderRadius: BorderRadius.all(Radius.circular(32)), 190 | borderSide: BorderSide(color: AppColors.outline, width: 1), 191 | ), 192 | enabledBorder: const OutlineInputBorder( 193 | borderRadius: BorderRadius.all(Radius.circular(32)), 194 | borderSide: BorderSide(color: AppColors.outline, width: 1), 195 | ), 196 | focusedBorder: const OutlineInputBorder( 197 | borderRadius: BorderRadius.all(Radius.circular(32.0)), 198 | borderSide: BorderSide(color: AppColors.primary, width: 1.5), 199 | ), 200 | errorBorder: const OutlineInputBorder( 201 | borderRadius: BorderRadius.all(Radius.circular(32.0)), 202 | borderSide: BorderSide(color: AppColors.secondary, width: 1), 203 | ), 204 | ), 205 | validator: (value) { 206 | if (isRequired != '') { 207 | if (value!.isEmpty) { 208 | return isRequired; 209 | } 210 | } 211 | return null; 212 | }, 213 | ), 214 | ], 215 | ), 216 | ); 217 | } 218 | 219 | Widget textfieldOTP({ 220 | required BuildContext context, 221 | required int length, 222 | }) { 223 | return PinCodeTextField( 224 | appContext: context, 225 | backgroundColor: Colors.white, 226 | length: length, 227 | animationType: AnimationType.fade, 228 | pinTheme: PinTheme( 229 | shape: PinCodeFieldShape.box, 230 | borderRadius: BorderRadius.circular(20), 231 | fieldHeight: 72, 232 | fieldWidth: 72, 233 | selectedColor: AppColors.primary, 234 | activeColor: AppColors.outline, 235 | inactiveColor: AppColors.outline, 236 | ), 237 | cursorColor: Colors.black, 238 | animationDuration: const Duration(milliseconds: 300), 239 | keyboardType: TextInputType.number, 240 | onCompleted: (v) {}, 241 | onChanged: (value) {}, 242 | ); 243 | } 244 | 245 | Widget textfield({ 246 | String hintText = '', 247 | String isRequired = '', 248 | required TextEditingController controller, 249 | }) { 250 | return TextFormField( 251 | controller: controller, 252 | decoration: InputDecoration( 253 | isDense: true, 254 | hintText: hintText, 255 | contentPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 19), 256 | hintStyle: const TextStyle(fontSize: 15, color: AppColors.secondaryText), 257 | border: const OutlineInputBorder( 258 | borderRadius: BorderRadius.all(Radius.circular(32)), 259 | borderSide: BorderSide(color: AppColors.outline, width: 1), 260 | ), 261 | enabledBorder: const OutlineInputBorder( 262 | borderRadius: BorderRadius.all(Radius.circular(32)), 263 | borderSide: BorderSide(color: AppColors.outline, width: 1), 264 | ), 265 | focusedBorder: const OutlineInputBorder( 266 | borderRadius: BorderRadius.all(Radius.circular(32.0)), 267 | borderSide: BorderSide(color: AppColors.primary, width: 1.5), 268 | ), 269 | errorBorder: const OutlineInputBorder( 270 | borderRadius: BorderRadius.all(Radius.circular(32.0)), 271 | borderSide: BorderSide(color: AppColors.secondary, width: 1), 272 | ), 273 | ), 274 | validator: (value) { 275 | if (isRequired != '') { 276 | if (value!.isEmpty) { 277 | return isRequired; 278 | } 279 | } 280 | return null; 281 | }, 282 | ); 283 | } 284 | 285 | Widget searchForm({required BuildContext context, bool redirect = false}) { 286 | final searchC = Get.put(SearchFormController()); 287 | return Obx( 288 | () => TextFormField( 289 | onTap: () { 290 | if (redirect) { 291 | Get.toNamed('/search/form'); 292 | FocusScope.of(context).requestFocus(FocusNode()); 293 | } 294 | }, 295 | controller: searchC.search, 296 | cursorColor: AppColors.mainText, 297 | style: const TextStyle(fontSize: 15, color: AppColors.mainText), 298 | decoration: InputDecoration( 299 | filled: true, 300 | fillColor: AppColors.form, 301 | hintText: "Search", 302 | hintStyle: const TextStyle( 303 | fontSize: 15, 304 | color: AppColors.secondaryText, 305 | ), 306 | prefixIcon: Padding( 307 | padding: const EdgeInsets.symmetric( 308 | vertical: 5, 309 | horizontal: 15, 310 | ), 311 | child: SvgPicture.asset(AssetIcons.search), 312 | ), 313 | suffixIcon: Padding( 314 | padding: const EdgeInsets.symmetric( 315 | vertical: 5, 316 | horizontal: 15, 317 | ), 318 | child: GestureDetector( 319 | child: Icon( 320 | MfgLabs.cancel_circled, 321 | color: searchC.hasTxtSearch.value 322 | ? AppColors.mainText 323 | : Colors.transparent, 324 | size: 20, 325 | ), 326 | onTap: () => searchC.clearForm(), 327 | ), 328 | ), 329 | border: const OutlineInputBorder( 330 | borderRadius: BorderRadius.all(Radius.circular(32)), 331 | borderSide: BorderSide(color: AppColors.form, width: 1), 332 | ), 333 | enabledBorder: const OutlineInputBorder( 334 | borderRadius: BorderRadius.all(Radius.circular(32)), 335 | borderSide: BorderSide(color: AppColors.form, width: 1), 336 | ), 337 | focusedBorder: const OutlineInputBorder( 338 | borderRadius: BorderRadius.all(Radius.circular(32.0)), 339 | borderSide: BorderSide(color: AppColors.form, width: 1), 340 | ), 341 | errorBorder: const OutlineInputBorder( 342 | borderRadius: BorderRadius.all(Radius.circular(32.0)), 343 | borderSide: BorderSide(color: AppColors.form, width: 1), 344 | ), 345 | ), 346 | ), 347 | ); 348 | } 349 | 350 | Widget textarea({ 351 | String hintText = '', 352 | String isRequired = '', 353 | required TextEditingController controller, 354 | required int minLines, 355 | }) { 356 | return TextFormField( 357 | minLines: minLines, 358 | keyboardType: TextInputType.multiline, 359 | maxLines: null, 360 | controller: controller, 361 | decoration: InputDecoration( 362 | hintText: hintText, 363 | hintStyle: const TextStyle(fontSize: 15, color: AppColors.secondaryText), 364 | border: const OutlineInputBorder( 365 | borderRadius: BorderRadius.all(Radius.circular(8)), 366 | borderSide: BorderSide(color: AppColors.outline, width: 1), 367 | ), 368 | enabledBorder: const OutlineInputBorder( 369 | borderRadius: BorderRadius.all(Radius.circular(8)), 370 | borderSide: BorderSide(color: AppColors.outline, width: 1), 371 | ), 372 | focusedBorder: const OutlineInputBorder( 373 | borderRadius: BorderRadius.all(Radius.circular(8)), 374 | borderSide: BorderSide(color: AppColors.primary, width: 1.5), 375 | ), 376 | errorBorder: const OutlineInputBorder( 377 | borderRadius: BorderRadius.all(Radius.circular(8)), 378 | borderSide: BorderSide(color: AppColors.secondary, width: 1), 379 | ), 380 | ), 381 | validator: (value) { 382 | if (isRequired != '') { 383 | if (value!.isEmpty) { 384 | return isRequired; 385 | } 386 | } 387 | return null; 388 | }, 389 | ); 390 | } 391 | 392 | Widget labelForm({required String label}) { 393 | return Container( 394 | margin: const EdgeInsets.only(top: 24, bottom: 10), 395 | child: Align( 396 | alignment: Alignment.centerLeft, 397 | child: Text(label, style: TextTypography.mH2), 398 | ), 399 | ); 400 | } 401 | 402 | Widget richLabel({required String title1, required String title2}) { 403 | return Container( 404 | padding: const EdgeInsets.only(top: 30, bottom: 15), 405 | child: RichText( 406 | text: TextSpan( 407 | text: title1, 408 | style: TextTypography.mH2, 409 | children: [ 410 | TextSpan( 411 | text: title2, 412 | style: const TextStyle( 413 | fontSize: 15.0, 414 | color: AppColors.secondaryText, 415 | ), 416 | ) 417 | ], 418 | ), 419 | ), 420 | ); 421 | } 422 | -------------------------------------------------------------------------------- /lib/src/ui/widgets/helper_widget.dart: -------------------------------------------------------------------------------- 1 | export 'button_widget.dart'; 2 | export 'form_widget.dart'; 3 | export 'component_widget.dart'; 4 | export 'recipe_widget.dart'; 5 | export 'notification_widget.dart'; 6 | -------------------------------------------------------------------------------- /lib/src/ui/widgets/notification_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:assorted_layout_widgets/assorted_layout_widgets.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:recipe_app/src/core/functions/network_image.dart'; 4 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 5 | import 'package:recipe_app/src/ui/widgets/helper_widget.dart'; 6 | 7 | Widget userActivity({ 8 | required String name, 9 | required String activity, 10 | required String time, 11 | required String urlProfile, 12 | required String labelButton, 13 | bool disable = false, 14 | }) { 15 | return ListTile( 16 | contentPadding: const EdgeInsets.all(0), 17 | leading: Container( 18 | margin: const EdgeInsets.only(right: 5), 19 | height: 48, 20 | width: 48, 21 | child: ClipRRect( 22 | borderRadius: BorderRadius.circular(50), 23 | child: PNetworkImage( 24 | urlProfile, 25 | fit: BoxFit.cover, 26 | ), 27 | ), 28 | ), 29 | title: Text(name, style: TextTypography.mH3), 30 | subtitle: Text( 31 | '$activity ・ $time', 32 | style: TextTypography.category, 33 | ), 34 | trailing: ButtonSM( 35 | color: AppColors.primary, 36 | txtButton: labelButton, 37 | disable: disable, 38 | onPressed: () {}, 39 | ), 40 | ); 41 | } 42 | 43 | Widget postActivity({ 44 | required String user1, 45 | required String user2, 46 | required String activity, 47 | required String time, 48 | required String urlProfile1, 49 | required String urlProfile2, 50 | required String urlPostImg, 51 | }) { 52 | return ListTile( 53 | contentPadding: const EdgeInsets.all(0), 54 | leading: ColumnSuper( 55 | innerDistance: -30.0, 56 | alignment: Alignment.centerLeft, 57 | children: [ 58 | Container( 59 | height: 45, 60 | width: 45, 61 | margin: const EdgeInsets.only(left: 10), 62 | child: ClipRRect( 63 | borderRadius: BorderRadius.circular(50), 64 | child: PNetworkImage( 65 | urlProfile1, 66 | fit: BoxFit.cover, 67 | ), 68 | ), 69 | ), 70 | Container( 71 | height: 50, 72 | width: 50, 73 | decoration: BoxDecoration( 74 | borderRadius: const BorderRadius.all( 75 | Radius.circular(50), 76 | ), 77 | border: Border.all( 78 | color: AppColors.bgColor, 79 | width: 2, 80 | ), 81 | ), 82 | child: ClipRRect( 83 | borderRadius: BorderRadius.circular(50), 84 | child: PNetworkImage( 85 | urlProfile2, 86 | fit: BoxFit.cover, 87 | ), 88 | ), 89 | ) 90 | ], 91 | ), 92 | // leading: Container( 93 | // height: 48, 94 | // width: 48, 95 | // child: ClipRRect( 96 | // borderRadius: BorderRadius.circular(50), 97 | // child: PNetworkImage( 98 | // urlProfile1, 99 | // fit: BoxFit.cover, 100 | // ), 101 | // ), 102 | // ), 103 | title: Row( 104 | children: [ 105 | Flexible( 106 | child: RichText( 107 | text: TextSpan( 108 | children: [ 109 | TextSpan( 110 | text: user1, 111 | style: TextTypography.mH3, 112 | ), 113 | const TextSpan( 114 | text: ' and ', 115 | style: TextStyle( 116 | fontSize: 15, 117 | color: AppColors.secondaryText, 118 | ), 119 | ), 120 | TextSpan( 121 | text: user2, 122 | style: TextTypography.mH3, 123 | ) 124 | ], 125 | ), 126 | ), 127 | ), 128 | ], 129 | ), 130 | subtitle: Container( 131 | margin: const EdgeInsets.only(top: 5), 132 | child: Text( 133 | '$activity ・ $time', 134 | style: TextTypography.category, 135 | ), 136 | ), 137 | trailing: SizedBox( 138 | height: 64, 139 | width: 60, 140 | child: ClipRRect( 141 | borderRadius: BorderRadius.circular(12), 142 | child: PNetworkImage( 143 | urlPostImg, 144 | fit: BoxFit.cover, 145 | ), 146 | ), 147 | ), 148 | ); 149 | } 150 | -------------------------------------------------------------------------------- /lib/src/ui/widgets/recipe_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'package:get/get.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:fluttericon/octicons_icons.dart'; 5 | import 'package:recipe_app/src/core/functions/network_image.dart'; 6 | import 'package:recipe_app/src/core/models/recipe_model.dart'; 7 | import 'package:recipe_app/src/ui/utils/helper_util.dart'; 8 | 9 | class RecipeWidget extends StatelessWidget { 10 | final RecipeModel data; 11 | 12 | const RecipeWidget({super.key, required this.data}); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Column( 17 | children: [ 18 | Row( 19 | children: [ 20 | SizedBox( 21 | height: 31, 22 | width: 31, 23 | child: ClipRRect( 24 | borderRadius: BorderRadius.circular(11), 25 | child: PNetworkImage( 26 | data.imgAuthor, 27 | fit: BoxFit.cover, 28 | ), 29 | ), 30 | ), 31 | Padding( 32 | padding: const EdgeInsets.only(left: 8), 33 | child: Text( 34 | data.author, 35 | style: const TextStyle( 36 | fontSize: 12, 37 | color: AppColors.mainText, 38 | fontWeight: FontWeight.w500, 39 | ), 40 | ), 41 | ), 42 | ], 43 | ), 44 | Container( 45 | margin: const EdgeInsets.only(top: 8), 46 | height: 151, 47 | child: Stack( 48 | children: [ 49 | GestureDetector( 50 | onTap: () => Get.toNamed('/recipe/detail', arguments: data), 51 | child: ClipRRect( 52 | borderRadius: BorderRadius.circular(16), 53 | child: PNetworkImage( 54 | data.imgCover, 55 | fit: BoxFit.cover, 56 | ), 57 | ), 58 | ), 59 | Positioned( 60 | right: 10.0, 61 | top: 10.0, 62 | child: ClipRRect( 63 | borderRadius: BorderRadius.circular(8.0), 64 | child: BackdropFilter( 65 | filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), 66 | child: SizedBox( 67 | height: 35, 68 | width: 35, 69 | child: IconButton( 70 | onPressed: () {}, 71 | icon: Icon( 72 | data.favorite 73 | ? Icons.favorite 74 | : Icons.favorite_border, 75 | color: 76 | data.favorite ? Colors.redAccent : Colors.white, 77 | size: 20, 78 | ), 79 | ), 80 | ), 81 | ), 82 | ), 83 | ), 84 | ], 85 | ), 86 | ), 87 | InkWell( 88 | child: Container( 89 | padding: const EdgeInsets.only(top: 16, bottom: 8), 90 | child: Align( 91 | alignment: Alignment.centerLeft, 92 | child: Text(data.title, style: TextTypography.mH2), 93 | ), 94 | ), 95 | onTap: () => Get.toNamed('/recipe/detail', arguments: data), 96 | ), 97 | Row( 98 | children: [ 99 | Text(data.category, style: TextTypography.category), 100 | Container( 101 | margin: const EdgeInsets.symmetric(horizontal: 5), 102 | child: const Icon( 103 | Octicons.primitive_dot, 104 | color: AppColors.secondaryText, 105 | size: 10, 106 | ), 107 | ), 108 | Text(data.duration, style: TextTypography.category) 109 | ], 110 | ) 111 | ], 112 | ); 113 | } 114 | } 115 | 116 | class UserRecipe extends StatelessWidget { 117 | final RecipeModel data; 118 | 119 | const UserRecipe({super.key, required this.data}); 120 | 121 | @override 122 | Widget build(BuildContext context) { 123 | return Column( 124 | children: [ 125 | SizedBox( 126 | height: 151, 127 | child: Stack( 128 | children: [ 129 | GestureDetector( 130 | onTap: () => Get.toNamed('/recipe/detail', arguments: data), 131 | child: ClipRRect( 132 | borderRadius: BorderRadius.circular(16), 133 | child: PNetworkImage( 134 | data.imgCover, 135 | fit: BoxFit.cover, 136 | ), 137 | ), 138 | ), 139 | Positioned( 140 | right: 10.0, 141 | top: 10.0, 142 | child: ClipRRect( 143 | borderRadius: BorderRadius.circular(8.0), 144 | child: BackdropFilter( 145 | filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), 146 | child: SizedBox( 147 | height: 35, 148 | width: 35, 149 | child: IconButton( 150 | onPressed: () {}, 151 | icon: Icon( 152 | data.favorite 153 | ? Icons.favorite 154 | : Icons.favorite_border, 155 | color: 156 | data.favorite ? Colors.redAccent : Colors.white, 157 | size: 20, 158 | ), 159 | ), 160 | ), 161 | ), 162 | ), 163 | ), 164 | ], 165 | ), 166 | ), 167 | InkWell( 168 | child: Container( 169 | padding: const EdgeInsets.only(top: 16, bottom: 8), 170 | child: Align( 171 | alignment: Alignment.centerLeft, 172 | child: Text(data.title, style: TextTypography.mH2), 173 | ), 174 | ), 175 | onTap: () => Get.toNamed('/recipe/detail', arguments: data), 176 | ), 177 | Row( 178 | children: [ 179 | Text(data.category, style: TextTypography.category), 180 | Container( 181 | margin: const EdgeInsets.symmetric(horizontal: 5), 182 | child: const Icon( 183 | Octicons.primitive_dot, 184 | color: AppColors.secondaryText, 185 | size: 10, 186 | ), 187 | ), 188 | Text(data.duration, style: TextTypography.category) 189 | ], 190 | ) 191 | ], 192 | ); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: recipe_app 2 | description: "A new Flutter project." 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 6 | 7 | # The following defines the version and build number for your application. 8 | # A version number is three numbers separated by dots, like 1.2.43 9 | # followed by an optional build number separated by a +. 10 | # Both the version and the builder number may be overridden in flutter 11 | # build by specifying --build-name and --build-number, respectively. 12 | # In Android, build-name is used as versionName while build-number used as versionCode. 13 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 14 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. 15 | # Read more about iOS versioning at 16 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 17 | # In Windows, build-name is used as the major, minor, and patch parts 18 | # of the product and file versions while build-number is used as the build suffix. 19 | version: 1.0.0+1 20 | 21 | environment: 22 | sdk: ">=3.2.6 <4.0.0" 23 | 24 | # Dependencies specify other packages that your package needs in order to work. 25 | # To automatically upgrade your package dependencies to the latest versions 26 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 27 | # dependencies can be manually updated by changing the version numbers below to 28 | # the latest version available on pub.dev. To see which dependencies have newer 29 | # versions available, run `flutter pub outdated`. 30 | dependencies: 31 | assorted_layout_widgets: ^9.0.1 32 | cached_network_image: ^3.3.1 33 | cupertino_icons: ^1.0.2 34 | dotted_border: ^2.1.0 35 | flutter: 36 | sdk: flutter 37 | flutter_svg: ^2.0.10+1 38 | fluttericon: ^2.0.0 39 | get: ^4.6.6 40 | pin_code_fields: ^8.0.1 41 | shimmer: ^3.0.0 42 | 43 | dev_dependencies: 44 | flutter_lints: ^2.0.0 45 | flutter_test: 46 | sdk: flutter 47 | 48 | # For information on the generic Dart part of this file, see the 49 | # following page: https://dart.dev/tools/pub/pubspec 50 | # The following section is specific to Flutter packages. 51 | flutter: 52 | # The following line ensures that the Material Icons font is 53 | # included with your application, so that you can use the icons in 54 | # the material Icons class. 55 | uses-material-design: true 56 | # To add assets to your application, add an assets section, like this: 57 | assets: 58 | - assets/images/ 59 | - assets/icons/ 60 | fonts: 61 | - family: Inter 62 | fonts: 63 | - asset: assets/fonts/Inter-Regular.ttf 64 | - asset: assets/fonts/Inter-Bold.ttf 65 | weight: 700 -------------------------------------------------------------------------------- /screenshoot/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/screenshoot/dashboard.png -------------------------------------------------------------------------------- /screenshoot/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/screenshoot/login.png -------------------------------------------------------------------------------- /screenshoot/notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/screenshoot/notification.png -------------------------------------------------------------------------------- /screenshoot/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/screenshoot/profile.png -------------------------------------------------------------------------------- /screenshoot/recipe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agisrh/Flutter-Recipe-App-UI/590140580b2e9b787430fcff28ec8da9a670030d/screenshoot/recipe.png -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:recipe_app/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(const 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 | --------------------------------------------------------------------------------