├── .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 |
4 |
--------------------------------------------------------------------------------
/assets/icons/camera.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/check-green.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/assets/icons/check-grey.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/assets/icons/drag-icon.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/assets/icons/edit.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/filter.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/assets/icons/heart.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/assets/icons/home.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/image.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/lock.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/assets/icons/message.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/assets/icons/notification.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/profile.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/scan.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/search.svg:
--------------------------------------------------------------------------------
1 |
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 |
--------------------------------------------------------------------------------